eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py

branch
eric7
changeset 8808
033fa34447d0
parent 8802
129a973fc33e
child 8811
9ff6b1b5f601
diff -r f4486646a233 -r 033fa34447d0 eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py
--- a/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py	Sat Dec 04 17:41:18 2021 +0100
+++ b/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py	Sat Dec 04 18:06:17 2021 +0100
@@ -22,6 +22,9 @@
         
         ## Imports order
         "I201", "I202", "I203", "I204",
+        
+        ## Various other import related
+        "I901", "I902", "I903", "I904",
     ]
 
     def __init__(self, source, filename, tree, select, ignore, expected,
@@ -63,7 +66,8 @@
         
         checkersWithCodes = [
             (self.__checkLocalImports, ("I101", "I102", "I103")),
-            (self.__checkImportOrder, ("I201", "I202", "I203", "I204"))
+            (self.__checkImportOrder, ("I201", "I202", "I203", "I204")),
+            (self.__tidyImports, ("I901", "I902", "I903", "I904")),
         ]
         
         self.__checkers = []
@@ -321,3 +325,130 @@
                 return (node, "I204", ", ".join(expectedList))
         
         return None
+    
+    #######################################################################
+    ## Tidy imports
+    ##
+    ## adapted from: flake8-tidy-imports v4.5.0
+    #######################################################################
+    
+    def __tidyImports(self):
+        """
+        Private method to check various other import related topics.
+        """
+        self.__bannedModules = self.__args.get("BannedModules", [])
+        self.__banRelativeImports = self.__args.get("BanRelativeImports", "")
+        
+        ruleMethods = []
+        if not self.__ignoreCode("I901"):
+            ruleMethods.append(self.__checkUnnecessaryAlias)
+        if (
+            not self.__ignoreCode("I902")
+            and bool(self.__bannedModules)
+        ):
+            ruleMethods.append(self.__checkBannedImport)
+        if (
+            (not self.__ignoreCode("I903") and
+             self.__banRelativeImports == "parents") or
+            (not self.__ignoreCode("I904") and
+             self.__banRelativeImports == "true")
+        ):
+            ruleMethods.append(self.__checkBannedRelativeImports)
+        
+        for node in ast.walk(self.__tree):
+            for method in ruleMethods:
+                method(node)
+    
+    def __checkUnnecessaryAlias(self, node):
+        """
+        Private method to check unnecessary import aliases.
+        
+        @param node reference to the node to be checked
+        @type ast.AST
+        """
+        if isinstance(node, ast.Import):
+            for alias in node.names:
+                if "." not in alias.name:
+                    fromName = None
+                    importedName = alias.name
+                else:
+                    fromName, importedName = alias.name.rsplit(".", 1)
+                
+                if importedName == alias.asname:
+                    if fromName:
+                        rewritten = "from {0} import {1}".format(
+                            fromName, importedName)
+                    else:
+                        rewritten = "import {0}".format(importedName)
+                    
+                    self.__error(node.lineno - 1, node.col_offset, "I901",
+                                 rewritten)
+        
+        elif isinstance(node, ast.ImportFrom):
+            for alias in node.names:
+                if alias.name == alias.asname:
+                    rewritten = "from {0} import {1}".format(
+                        node.module, alias.name)
+                    
+                    self.__error(node.lineno - 1, node.col_offset, "I901",
+                                 rewritten)
+    
+    def __checkBannedImport(self, node):
+        """
+        Private method to check import of banned modules.
+        
+        @param node reference to the node to be checked
+        @type ast.AST
+        """
+        if not bool(self.__bannedModules):
+            return
+        
+        if isinstance(node, ast.Import):
+            moduleNames = [alias.name for alias in node.names]
+        elif isinstance(node, ast.ImportFrom):
+            nodeModule = node.module or ""
+            moduleNames = [nodeModule]
+            for alias in node.names:
+                moduleNames.append("{0}.{1}".format(nodeModule, alias.name))
+        else:
+            return
+        
+        # Sort from most to least specific paths.
+        moduleNames.sort(key=len, reverse=True)
+        
+        warned = set()
+        
+        for moduleName in moduleNames:
+            if moduleName in self.__bannedModules:
+                if any(mod.startswith(moduleName) for mod in warned):
+                    # Do not show an error for this line if we already showed
+                    # a more specific error.
+                    continue
+                else:
+                    warned.add(moduleName)
+                self.__error(node.lineno - 1, node.col_offset, "I902",
+                             moduleName)
+    
+    def __checkBannedRelativeImports(self, node):
+        """
+        Private method to check if relative imports are banned.
+        
+        @param node reference to the node to be checked
+        @type ast.AST
+        """
+        if not self.__banRelativeImports:
+            return
+        
+        elif self.__banRelativeImports == "parents":
+            minNodeLevel = 1
+            msgCode = "I903"
+        else:
+            minNodeLevel = 0
+            msgCode = "I904"
+        
+        if (
+            self.__banRelativeImports and
+            isinstance(node, ast.ImportFrom) and
+            node.level > minNodeLevel
+        ):
+            self.__error(node.lineno - 1, node.col_offset, msgCode)

eric ide

mercurial