Plugins/CheckerPlugins/CodeStyleChecker/MiscellaneousChecker.py

changeset 4508
a3b38825acf0
parent 4507
1a5bc1ac7c2e
child 4509
7797ee4a45f9
--- a/Plugins/CheckerPlugins/CodeStyleChecker/MiscellaneousChecker.py	Mon Oct 26 19:14:34 2015 +0100
+++ b/Plugins/CheckerPlugins/CodeStyleChecker/MiscellaneousChecker.py	Tue Oct 27 19:46:12 2015 +0100
@@ -23,6 +23,8 @@
         "M111", "M112",
         "M121",
         "M131",
+        "M701", "M702", "M703", "M704", "M705", "M706",
+        "M721", "M722", "M723", "M724", "M725", "M726",
         "M801",
         "M811",
         
@@ -34,14 +36,20 @@
         """
         Constructor
         
-        @param source source code to be checked (list of string)
-        @param filename name of the source file (string)
-        @param select list of selected codes (list of string)
-        @param ignore list of codes to be ignored (list of string)
-        @param expected list of expected codes (list of string)
+        @param source source code to be checked
+        @type list of str
+        @param filename name of the source file
+        @type str
+        @param select list of selected codes
+        @type list of str
+        @param ignore list of codes to be ignored
+        @type list of str
+        @param expected list of expected codes
+        @type list of str
         @param repeat flag indicating to report each occurrence of a code
-            (boolean)
-        @param args dictionary of arguments for the miscellaneous checks (dict)
+        @type bool
+        @param args dictionary of arguments for the miscellaneous checks
+        @type dict
         """
         self.__select = tuple(select)
         self.__ignore = ('',) if select else tuple(ignore)
@@ -56,6 +64,16 @@
         self.__pep3101FormatRegex = re.compile(
             r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%')
 
+        self.__availableFutureImports = {
+            # future import: (code missing, code present)
+            'division': ("M701", "M721"),
+            'absolute_import': ("M702", "M722"),
+            'with_statement': ("M703", "M723"),
+            'print_function': ("M704", "M724"),
+            'unicode_literals': ("M705", "M725"),
+            'generator_stop': ("M706", "M726"),
+        }
+        
         # statistics counters
         self.counters = {}
         
@@ -70,6 +88,9 @@
             (self.__checkPep3101, ("M131",)),
             (self.__checkPrintStatements, ("M801",)),
             (self.__checkTuple, ("M811", )),
+            (self.__checkFuture, ("M701", "M702", "M703", "M704", "M705",
+                                  "M706", "M721", "M722", "M723", "M724",
+                                  "M725", "M726")),
         ]
         
         self.__defaultArgs = {
@@ -90,8 +111,10 @@
         """
         Private method to check if the message code should be ignored.
 
-        @param code message code to check for (string)
-        @return flag indicating to ignore the given code (boolean)
+        @param code message code to check for
+        @type str
+        @return flag indicating to ignore the given code
+        @rtype bool
         """
         return (code.startswith(self.__ignore) and
                 not code.startswith(self.__select))
@@ -100,10 +123,14 @@
         """
         Private method to record an issue.
         
-        @param lineNumber line number of the issue (integer)
-        @param offset position within line of the issue (integer)
-        @param code message code (string)
-        @param args arguments for the message (list)
+        @param lineNumber line number of the issue
+        @type int
+        @param offset position within line of the issue
+        @type int
+        @param code message code
+        @type str
+        @param args arguments for the message
+        @type list
         """
         if self.__ignoreCode(code):
             return
@@ -264,3 +291,77 @@
             if isinstance(node, ast.Tuple) and \
                     len(node.elts) == 1:
                 self.__error(node.lineno - 1, node.col_offset, "M811")
+    
+    def __checkFuture(self):
+        """
+        Private method to check the __future__ imports.
+        """
+        visitor = FutureImportVisitor()
+        visitor.visit(self.__tree)
+        if not visitor.hasCode:
+            return
+        
+        present = set()
+        for importNode in visitor.futureImports:
+            for alias in importNode.names:
+                if alias.name not in self.__availableFutureImports:
+                    # unknown code
+                    continue
+                self.__error(importNode.lineno - 1, 0,
+                             self.__availableFutureImports[alias.name][1])
+                present.add(alias.name)
+        for name in self.__availableFutureImports:
+            if name not in present:
+                self.__error(0, 0,
+                             self.__availableFutureImports[name][0])
+
+
+class FutureImportVisitor(ast.NodeVisitor):
+    """
+    Class implementing a node visitor to look for __future__ imports.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(FutureImportVisitor, self).__init__()
+        self.futureImports = []
+        self.__hasCode = False
+
+    def visit_ImportFrom(self, node):
+        """
+        Public method to analyze an 'from ... import ...' node.
+        
+        @param node reference to the ImportFrom node
+        @type ast.AST
+        """
+        if node.module == '__future__':
+            self.futureImports += [node]
+
+    def visit_Expr(self, node):
+        """
+        Public method to analyze an expression node.
+        
+        @param node reference to the expression node
+        @type ast.AST
+        """
+        if not isinstance(node.value, ast.Str) or node.value.col_offset != 0:
+            self.__hasCode = True
+
+    def generic_visit(self, node):
+        """
+        Public method to analyze any other node.
+        
+        @param node reference to the node
+        @type ast.AST
+        """
+        if not isinstance(node, ast.Module):
+            self.__hasCode = True
+        super(FutureImportVisitor, self).generic_visit(node)
+
+    @property
+    def hasCode(self):
+        """
+        Public method to check for the presence of some code.
+        """
+        return self.__hasCode or self.futureImports

eric ide

mercurial