Code Style Checker

Fri, 02 Apr 2021 18:13:12 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Fri, 02 Apr 2021 18:13:12 +0200
changeset 8192
e1157bd8b4c2
parent 8191
9125da0c227e
child 8193
2aa8953a2145

Code Style Checker
- continued to implement checkers for potential code simplifications

eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyChecker.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/translations.py file | annotate | diff | comparison | revisions
--- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyChecker.py	Fri Apr 02 15:35:44 2021 +0200
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyChecker.py	Fri Apr 02 18:13:12 2021 +0200
@@ -21,6 +21,7 @@
         # Python-specifics
         "Y101", "Y102", "Y103", "Y104", "Y105", "Y106", "Y107", "Y108",
         "Y109", "Y110", "Y111", "Y112", "Y113", "Y114", "Y115", "Y116",
+        "Y117", "Y118", "Y119"
         
         # Comparations
     ]
--- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py	Fri Apr 02 15:35:44 2021 +0200
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py	Fri Apr 02 18:13:12 2021 +0200
@@ -115,6 +115,7 @@
         self.__check104(node)
         self.__check110_111(node)
         self.__check113(node)
+        self.__check118b(node)
         
         self.generic_visit(node)
     
@@ -141,6 +142,39 @@
         
         self.generic_visit(node)
     
+    def visit_With(self, node):
+        """
+        Public method to process a With node.
+        
+        @param node reference to the With node
+        @type ast.With
+        """
+        self.__check117(node)
+        
+        self.generic_visit(node)
+    
+    def visit_Compare(self, node):
+        """
+        Public method to process a Compare node.
+        
+        @param node reference to the Compare node
+        @type ast.Compare
+        """
+        self.__check118a(node)
+        
+        self.generic_visit(node)
+    
+    def visit_ClassDef(self, node):
+        """
+        Public method to process a ClassDef node.
+        
+        @param node reference to the ClassDef node
+        @type ast.ClassDef
+        """
+        self.__check119(node)
+        
+        self.generic_visit(node)
+    
     #############################################################
     ## Helper methods for the various checkers below
     #############################################################
@@ -468,8 +502,13 @@
             body = unparse(node.body[0].value)
             cond = unparse(node.test)
             orelse = unparse(node.orelse[0].value)
-            self.__error(node.lineno - 1, node.col_offset, "Y108",
-                         assign, body, cond, orelse)
+            if len(
+                "{0} = {1} if {2} else {3}".format(assign, body, cond, orelse)
+            ) > 79:
+                self.__error(node.lineno - 1, node.col_offset, "Y108a")
+            else:
+                self.__error(node.lineno - 1, node.col_offset, "Y108b",
+                             assign, body, cond, orelse)
     
     def __check109(self, node):
         """
@@ -730,20 +769,21 @@
             variable = node.test.left
             child = node.orelse[0]
             elseValue = None
+            if node.body[0].value is not None:
+                bodyValueStr = unparse(node.body[0].value).strip("'")
+            else:
+                bodyValueStr = "None"
             if isinstance(node.test.comparators[0], ast.Str):
                 keyValuePairs = {
-                    node.test.comparators[0].s:
-                        unparse(node.body[0].value).strip("'")
+                    node.test.comparators[0].s: bodyValueStr
                 }
             elif isinstance(node.test.comparators[0], ast.Num):
                 keyValuePairs = {
-                    node.test.comparators[0].n:
-                        unparse(node.body[0].value).strip("'")
+                    node.test.comparators[0].n: bodyValueStr,
                 }
             else:
                 keyValuePairs = {
-                    node.test.comparators[0].value:
-                        unparse(node.body[0].value).strip("'")
+                    node.test.comparators[0].value: bodyValueStr
                 }
             while child:
                 if not (
@@ -787,6 +827,113 @@
                 ret = f"{keyValuePairs}.get({variable.id})"
             
             self.__error(node.lineno - 1, node.col_offset, "Y116", ret)
+    
+    def __check117(self, node):
+        """
+        Private method to check for multiple with-statements with same scope.
+        
+        @param node reference to the AST node to be checked
+        @type ast.With
+        """
+        # with A() as a:
+        #     with B() as b:
+        #         print("hello")
+        if (
+            len(node.body) == 1 and
+            isinstance(node.body[0], ast.With)
+        ):
+            withItems = []
+            for withitem in node.items + node.body[0].items:
+                withItems.append(f"{unparse(withitem)}")
+            mergedWith = f"with {', '.join(withItems)}:"
+            self.__error(node.lineno - 1, node.col_offset, "Y117", mergedWith)
+    
+    def __check118a(self, node):
+        """
+        Private method to check for usages of "key in dict.keys()".
+        
+        @param node reference to the AST node to be checked
+        @type ast.Compare
+        """
+        # key in dict.keys()
+        if (
+            len(node.ops) == 1 and
+            isinstance(node.ops[0], ast.In) and
+            len(node.comparators) == 1
+        ):
+            callNode = node.comparators[0]
+            if not isinstance(callNode, ast.Call):
+                return
+            
+            attrNode = callNode.func
+            if (
+                isinstance(callNode.func, ast.Attribute) and
+                callNode.func.attr == "keys" and
+                isinstance(callNode.func.ctx, ast.Load)
+            ):
+                keyStr = unparse(node.left)
+                dictStr = unparse(attrNode.value)
+                self.__error(node.lineno - 1, node.col_offset, "Y118",
+                             keyStr, dictStr)
+    
+    def __check118b(self, node):
+        """
+        Private method to check for usages of "key in dict.keys()".
+        
+        @param node reference to the AST node to be checked
+        @type ast.For
+        """
+        # for key in dict.keys():
+        #     # do something
+        callNode = node.iter
+        if not isinstance(callNode, ast.Call):
+            return
+        
+        attrNode = callNode.func
+        if (
+            isinstance(callNode.func, ast.Attribute) and
+            callNode.func.attr == "keys" and
+            isinstance(callNode.func.ctx, ast.Load)
+        ):
+            keyStr = unparse(node.target)
+            dictStr = unparse(attrNode.value)
+            self.__error(node.lineno - 1, node.col_offset, "Y118",
+                         keyStr, dictStr)
+    
+    def __check119(self, node):
+        """
+        Public method to check for classes that should be "dataclasses".
+        
+        @param node reference to the AST node to be checked
+        @type ast.ClassDef
+        """
+        if (
+            len(node.decorator_list) == 0 and
+            len(node.bases) == 0
+        ):
+            dataclassFunctions = [
+                "__init__",
+                "__eq__",
+                "__hash__",
+                "__repr__",
+                "__str__",
+            ]
+            hasOnlyConstructorMethod = True
+            for bodyElement in node.body:
+                if (
+                    isinstance(bodyElement, ast.FunctionDef) and
+                    bodyElement.name not in dataclassFunctions
+                ):
+                    hasOnlyConstructorMethod = False
+                    break
+
+            if (
+                hasOnlyConstructorMethod and
+                sum(1 for el in node.body
+                    if isinstance(el, ast.FunctionDef)) > 0
+            ):
+                self.__error(node.lineno - 1, node.col_offset, "Y119",
+                             node.name)
 
 #
 # eflag: noqa = M891
--- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/translations.py	Fri Apr 02 15:35:44 2021 +0200
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/translations.py	Fri Apr 02 18:13:12 2021 +0200
@@ -34,9 +34,12 @@
     "Y107": QCoreApplication.translate(
         "SimplifyChecker",
         '''Don't use return in try/except and finally'''),
-    "Y108": QCoreApplication.translate(
+    "Y108a": QCoreApplication.translate(
         "SimplifyChecker",
-        '''Use ternary operator "{0} = {1} if {2} else {3}"'''
+        '''Use ternary operator instead of if-else-block'''),
+    "Y108b": QCoreApplication.translate(
+        "SimplifyChecker",
+        '''Use ternary operator "{0} = {1} if {2} else {3}" '''
         '''instead of if-else-block'''),
     "Y109": QCoreApplication.translate(
         "SimplifyChecker",
@@ -63,6 +66,15 @@
         "SimplifyChecker",
         '''Use a dictionary lookup instead of 3+ if/elif-statements: '''
         '''return {0}'''),
+    "Y117": QCoreApplication.translate(
+        "SimplifyChecker",
+        '''Use "{0}" instead of multiple with statements'''),
+    "Y118": QCoreApplication.translate(
+        "SimplifyChecker",
+        '''Use "{0} in {1}" instead of "{0} in {1}.keys()"'''),
+    "Y119": QCoreApplication.translate(
+        "SimplifyChecker",
+        '''Use a dataclass for "class {0}"'''),
 }
 
 _simplifyMessagesSampleArgs = {
@@ -70,12 +82,15 @@
     "Y103": ["foo != bar"],
     "Y104": ["iterable"],
     "Y105": ["Exception"],
-    "Y108": ["foo", "bar", "condition", "baz"],
+    "Y108b": ["foo", "bar", "condition", "baz"],
     "Y109": ["foo", "[1, 42]", "foo == 1 or foo == 42"],
     "Y110": ["check", "foo", "iterable"],
     "Y111": ["check", "foo", "iterable"],
     "Y112": ["FOO", "foo"],
     "Y113": ["foo"],
     "Y114": ["foo > 42", "bar < 42"],
-    "Y116": ["mapping.get(foo, 42)"]
+    "Y116": ["mapping.get(foo, 42)"],
+    "Y117": ["with Foo() as foo, Bar() as bar:"],
+    "Y118": ["foo", "bar_dict"],
+    "Y119": ["Foo"]
 }

eric ide

mercurial