From b435ed1f4c63ff53ddb86251fd801d4096f71494 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Wed, 7 Aug 2013 19:53:22 +0400 Subject: [PATCH] added possibility to specify missing methods for (class, version) pair. Added updater for a new versions. fixed PY-10497 Teach code compatibility inspection about differences in unittest implementations --- .../helpers/tools/class_method_versions.xml | 155 ++++++++++++++++++ .../PyCompatibilityInspection.java | 40 +++-- .../validation/UnsupportedFeaturesUtil.java | 69 ++++++++ 3 files changed, 249 insertions(+), 15 deletions(-) create mode 100644 python/helpers/tools/class_method_versions.xml diff --git a/python/helpers/tools/class_method_versions.xml b/python/helpers/tools/class_method_versions.xml new file mode 100644 index 000000000000..9a403b984dd1 --- /dev/null +++ b/python/helpers/tools/class_method_versions.xml @@ -0,0 +1,155 @@ + + assertCountEqual + assertNotRegex + assertRaisesRegex + maxDiff + addTypeEqualityFunc + assertGreater + assertDictContainsSubset + assertLess + assertMultiLineEqual + assertIsNotNone + assertGreaterEqual + assertNotIn + assertRaisesRegexp + addCleanup + assertRegexpMatches + assertLessEqual + tearDownClass + doCleanups + assertRegex + assertSameElements + assertDictEqual + assertItemsEqual + assertIn + assertNotRegexpMatches + assertNotIsInstance + assertTupleEqual + assertIsNone + assertIs + assertIsInstance + assertWarnsRegex + setUpClass + assertListEqual + assertIsNot + assertSequenceEqual + longMessage + skipTest + assertWarns + assertSetEqual + assertCountEqual + assertNotRegex + assertRaisesRegex + maxDiff + addTypeEqualityFunc + assertGreater + assertDictContainsSubset + assertLess + assertMultiLineEqual + assertIsNotNone + assertGreaterEqual + assertNotIn + assertRaisesRegexp + addCleanup + assertRegexpMatches + assertLessEqual + tearDownClass + doCleanups + assertRegex + assertSameElements + assertDictEqual + assertItemsEqual + assertIn + assertNotRegexpMatches + assertNotIsInstance + assertTupleEqual + assertIsNone + assertIs + assertIsInstance + assertWarnsRegex + setUpClass + assertListEqual + assertIsNot + assertSequenceEqual + longMessage + skipTest + assertWarns + assertSetEqual + assertCountEqual + assertNotRegex + assertRaisesRegex + maxDiff + addTypeEqualityFunc + assertGreater + assertDictContainsSubset + assertLess + assertMultiLineEqual + assertIsNotNone + assertGreaterEqual + assertNotIn + assertRaisesRegexp + addCleanup + assertRegexpMatches + assertLessEqual + tearDownClass + doCleanups + assertRegex + assertSameElements + assertDictEqual + assertItemsEqual + assertIn + assertNotRegexpMatches + assertNotIsInstance + assertTupleEqual + assertIsNone + assertIs + assertIsInstance + assertWarnsRegex + setUpClass + assertListEqual + assertIsNot + assertSequenceEqual + longMessage + skipTest + assertWarns + assertSetEqual + assertCountEqual + assertNotRegex + assertWarnsRegex + assertRaisesRegex + assertRegex + assertSameElements + assertWarns + assertCountEqual + assertNotRegex + assertWarnsRegex + setUpClass + assertRaisesRegex + assertNotRegexpMatches + assertNotIsInstance + assertRegex + tearDownClass + assertItemsEqual + maxDiff + assertWarns + assertIsInstance + assertCountEqual + assertNotRegex + assertWarnsRegex + setUpClass + assertRaisesRegex + assertNotRegexpMatches + assertNotIsInstance + assertRegex + tearDownClass + assertItemsEqual + maxDiff + assertWarns + assertIsInstance + assertItemsEqual + assertNotRegexpMatches + assertSameElements + assertItemsEqual + assertNotRegexpMatches + + diff --git a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java index 52d7d273078e..9dfa2c0d8e0c 100644 --- a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java @@ -161,14 +161,30 @@ public class PyCompatibilityInspection extends PyInspection { StringBuilder message = new StringBuilder("Python version "); final PyExpression callee = node.getCallee(); assert callee != null; - for (int i = 0; i != myVersionsToProcess.size(); ++i) { - LanguageLevel languageLevel = myVersionsToProcess.get(i); - PsiReference reference = callee.getReference(); - if (reference != null) { - PsiElement resolved = reference.resolve(); - ProjectFileIndex ind = ProjectRootManager.getInstance(callee.getProject()).getFileIndex(); - final String name = callee.getText(); - if (resolved != null) { + PsiReference reference = callee.getReference(); + if (reference != null) { + PsiElement resolved = reference.resolve(); + ProjectFileIndex ind = ProjectRootManager.getInstance(callee.getProject()).getFileIndex(); + if (resolved instanceof PyFunction) { + String name = ((PyFunction)resolved).getName(); + final PyClass containingClass = ((PyFunction)resolved).getContainingClass(); + if (containingClass != null) { + if (PyNames.INIT.equals(name)) + name = callee.getText(); + else + message = new StringBuilder("Class " + containingClass.getName() + " in python version "); + for (int i = 0; i != myVersionsToProcess.size(); ++i) { + LanguageLevel languageLevel = myVersionsToProcess.get(i); + if (UnsupportedFeaturesUtil.CLASS_METHODS.containsKey(containingClass.getName())) { + final Map> map = UnsupportedFeaturesUtil.CLASS_METHODS.get(containingClass.getName()); + final Set unsupportedMethods = map.get(languageLevel); + if (unsupportedMethods != null && unsupportedMethods.contains(name)) + len = appendLanguageLevel(message, len, languageLevel); + } + } + } + for (int i = 0; i != myVersionsToProcess.size(); ++i) { + LanguageLevel languageLevel = myVersionsToProcess.get(i); PsiFile file = resolved.getContainingFile(); VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile != null && ind.isInLibraryClasses(virtualFile)) { @@ -177,15 +193,9 @@ public class PyCompatibilityInspection extends PyInspection { } } } - //else { - // if (!name.equals("print") && UnsupportedFeaturesUtil.BUILTINS.get(languageLevel).contains(name)) { - // len = appendLanguageLevel(message, len, languageLevel); - // } - //} + commonRegisterProblem(message, " not have method " + name, len, node, null, false); } } - commonRegisterProblem(message, " not have method " + callee.getText(), - len, node, null, false); } @Override diff --git a/python/src/com/jetbrains/python/validation/UnsupportedFeaturesUtil.java b/python/src/com/jetbrains/python/validation/UnsupportedFeaturesUtil.java index 5b1e2b1182a5..c438583840bc 100644 --- a/python/src/com/jetbrains/python/validation/UnsupportedFeaturesUtil.java +++ b/python/src/com/jetbrains/python/validation/UnsupportedFeaturesUtil.java @@ -25,16 +25,39 @@ import java.util.*; public class UnsupportedFeaturesUtil { public static Map> BUILTINS = new HashMap>(); public static Map> MODULES = new HashMap>(); + public static Map>> CLASS_METHODS = new HashMap>>(); public static Vector ALL_LANGUAGE_LEVELS; static { try { fillMaps(); + fillTestCaseMethods(); } catch (IOException e) { Logger log = Logger.getInstance(UnsupportedFeaturesUtil.class.getName()); log.error("Cannot find \"versions.xml\". " + e.getMessage()); } + fillAllLanguageLeves(); + } + + private static void fillTestCaseMethods() throws IOException { + final Logger log = Logger.getInstance(UnsupportedFeaturesUtil.class.getName()); + final FileReader reader = new FileReader(PythonHelpersLocator.getHelperPath("/tools/class_method_versions.xml")); + try { + final XMLReader xr = XMLReaderFactory.createXMLReader(); + final ClassMethodsParser parser = new ClassMethodsParser(); + xr.setContentHandler(parser); + xr.parse(new InputSource(reader)); + } + catch (SAXException e) { + log.error("Improperly formed \"class_method_versions.xml\". " + e.getMessage()); + } + finally { + reader.close(); + } + } + + private static void fillAllLanguageLeves() { ALL_LANGUAGE_LEVELS = new Vector(); ALL_LANGUAGE_LEVELS.add(LanguageLevel.PYTHON24.toString()); ALL_LANGUAGE_LEVELS.add(LanguageLevel.PYTHON25.toString()); @@ -160,5 +183,51 @@ public class UnsupportedFeaturesUtil { myContent.write(ch, start, length); } } + + static class ClassMethodsParser extends DefaultHandler { + private CharArrayWriter myContent = new CharArrayWriter(); + private String myClassName = ""; + private LanguageLevel myCurrentLevel; + + public void startElement(String namespaceURI, + String localName, + String qName, + Attributes attr) throws SAXException { + myContent.reset(); + if (localName.equals("class_name")) { + myClassName = attr.getValue("name"); + if (!CLASS_METHODS.containsKey(myClassName)) { + CLASS_METHODS.put(myClassName, new HashMap>()); + + } + } + if (localName.equals("python")) { + myCurrentLevel = LanguageLevel.fromPythonVersion(attr.getValue("version")); + if (myClassName != null) { + final Map> map = CLASS_METHODS.get(myClassName); + if (map != null) + map.put(myCurrentLevel, new HashSet()); + } + } + } + + public void endElement(String namespaceURI, + String localName, + String qName) throws SAXException { + if (localName.equals("func")) { + Map> levelSetMap = CLASS_METHODS.get(myClassName); + if (levelSetMap != null) { + final Set set = levelSetMap.get(myCurrentLevel); + if (set != null) + set.add(myContent.toString()); + } + } + } + + public void characters(char[] ch, int start, int length) + throws SAXException { + myContent.write(ch, start, length); + } + } }