src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py

branch
eric7
changeset 10360
9ffdb1490bd2
parent 10359
de0420dac60e
child 10361
e6ff9a4f6ee5
--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py	Wed Nov 29 14:49:44 2023 +0100
+++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py	Wed Nov 29 18:07:53 2023 +0100
@@ -57,6 +57,7 @@
         "M131",
         "M132",
         ## Comprehensions
+        "M180",
         "M181",
         "M182",
         "M183",
@@ -66,13 +67,12 @@
         "M187",
         "M188",
         "M189",
+        "M190",
         "M191",
         "M192",
         "M193",
+        "M194",
         "M195",
-        "M196",
-        "M197",
-        "M198",
         ## Dictionaries with sorted keys
         "M201",
         ## Property
@@ -248,6 +248,7 @@
             (
                 self.__checkComprehensions,
                 (
+                    "M180",
                     "M181",
                     "M182",
                     "M183",
@@ -257,13 +258,12 @@
                     "M187",
                     "M188",
                     "M189",
+                    "M190",
                     "M191",
                     "M192",
                     "M193",
+                    "M194",
                     "M195",
-                    "M196",
-                    "M197",
-                    "M198",
                 ),
             ),
             (self.__checkDictWithSortedKeys, ("M201",)),
@@ -847,56 +847,113 @@
     def __checkComprehensions(self):
         """
         Private method to check some comprehension related things.
-        """
+
+        This method is adapted from: flake8-comprehensions v3.14.0
+        Original: Copyright (c) 2017 Adam Johnson
+        """
+        visitedMapCalls = set()
+
         for node in ast.walk(self.__tree):
             if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
-                nArgs = len(node.args)
-                nKwArgs = len(node.keywords)
+                numPositionalArgs = len(node.args)
+                numKeywordArgs = len(node.keywords)
 
                 if (
-                    nArgs == 1
+                    numPositionalArgs == 1
                     and isinstance(node.args[0], ast.GeneratorExp)
                     and node.func.id in ("list", "set")
                 ):
                     errorCode = {
-                        "list": "M181",
-                        "set": "M182",
+                        "list": "M180",
+                        "set": "M181",
                     }[node.func.id]
                     self.__error(node.lineno - 1, node.col_offset, errorCode)
 
                 elif (
-                    nArgs == 1
+                    numPositionalArgs == 1
+                    and node.func.id == "dict"
+                    and len(node.keywords) == 0
                     and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp))
                     and isinstance(node.args[0].elt, ast.Tuple)
                     and len(node.args[0].elt.elts) == 2
-                    and node.func.id == "dict"
                 ):
                     if isinstance(node.args[0], ast.GeneratorExp):
-                        errorCode = "M183"
+                        errorCode = "M182"
                     else:
-                        errorCode = "M185"
+                        errorCode = "M184"
                     self.__error(node.lineno - 1, node.col_offset, errorCode)
 
                 elif (
-                    nArgs == 1
+                    numPositionalArgs == 1
                     and isinstance(node.args[0], ast.ListComp)
-                    and node.func.id in ("list", "set")
+                    and node.func.id in ("list", "set", "any", "all")
                 ):
                     errorCode = {
-                        "list": "M195",
-                        "set": "M184",
+                        "list": "M191",
+                        "set": "M183",
+                        "any": "M199",
+                        "all": "M199",
                     }[node.func.id]
-                    self.__error(node.lineno - 1, node.col_offset, errorCode)
-
-                elif nArgs == 1 and (
+                    self.__error(
+                        node.lineno - 1, node.col_offset, errorCode, node.func.id
+                    )
+
+                elif numPositionalArgs == 1 and (
                     isinstance(node.args[0], ast.Tuple)
                     and node.func.id == "tuple"
                     or isinstance(node.args[0], ast.List)
                     and node.func.id == "list"
                 ):
                     errorCode = {
-                        "tuple": "M197",
-                        "list": "M198",
+                        "tuple": "M189a",
+                        "list": "M190a",
+                    }[node.func.id]
+                    ##suffix = "remove the outer call to {func}()."
+                    self.__error(
+                        node.lineno - 1,
+                        node.col_offset,
+                        errorCode,
+                        type(node.args[0]).__name__.lower(),
+                        node.func.id,
+                    )
+
+                elif (
+                    numPositionalArgs == 1
+                    and numKeywordArgs == 0
+                    and isinstance(node.args[0], (ast.Dict, ast.DictComp))
+                    and node.func.id == "dict"
+                ):
+                    if isinstance(node.args[0], ast.Dict):
+                        type_ = "dict"
+                    else:
+                        type_ = "dict comprehension"
+                    self.__error(
+                        node.lineno - 1,
+                        node.col_offset,
+                        "M198",
+                        type_,
+                    )
+
+                elif (
+                    numPositionalArgs == 1
+                    and isinstance(node.args[0], (ast.Tuple, ast.List))
+                    and (
+                        node.func.id in ("tuple", "list", "set")
+                        or (
+                            node.func.id == "dict"
+                            and all(
+                                isinstance(elt, ast.Tuple) and len(elt.elts) == 2
+                                for elt in node.args[0].elts
+                            )
+                        )
+                    )
+                ):
+                    ##suffix = "rewrite as a {func} literal."
+                    errorCode = {
+                        "tuple": "M189b",
+                        "list": "M190b",
+                        "set": "M185",
+                        "dict": "M186",
                     }[node.func.id]
                     self.__error(
                         node.lineno - 1,
@@ -907,37 +964,20 @@
                     )
 
                 elif (
-                    nArgs == 1
-                    and isinstance(node.args[0], (ast.Tuple, ast.List))
-                    and node.func.id in ("tuple", "list", "set", "dict")
-                ):
-                    errorCode = {
-                        "tuple": "M192",
-                        "list": "M193",
-                        "set": "M191",
-                        "dict": "M191",
-                    }[node.func.id]
-                    self.__error(
-                        node.lineno - 1,
-                        node.col_offset,
-                        errorCode,
-                        type(node.args[0]).__name__.lower(),
-                        node.func.id,
-                    )
-
-                elif (
-                    nArgs == 0
+                    numPositionalArgs == 0
                     and not any(isinstance(a, ast.Starred) for a in node.args)
                     and not any(k.arg is None for k in node.keywords)
                     and node.func.id == "dict"
                 ) or (
-                    nArgs == 0 and nKwArgs == 0 and node.func.id in ("tuple", "list")
+                    numKeywordArgs == 0 and numKeywordArgs == 0 and node.func.id in (
+                        "tuple", "list"
+                    )
                 ):
-                    self.__error(node.lineno - 1, node.col_offset, "M186", node.func.id)
+                    self.__error(node.lineno - 1, node.col_offset, "M188", node.func.id)
 
                 elif (
                     node.func.id in {"list", "reversed"}
-                    and nArgs > 0
+                    and numPositionalArgs > 0
                     and isinstance(node.args[0], ast.Call)
                     and isinstance(node.args[0].func, ast.Name)
                     and node.args[0].func.id == "sorted"
@@ -957,7 +997,7 @@
                             self.__error(
                                 node.lineno - 1,
                                 node.col_offset,
-                                "M187a",
+                                "M193a",
                                 node.func.id,
                                 node.args[0].func.id,
                             )
@@ -965,22 +1005,29 @@
                             self.__error(
                                 node.lineno - 1,
                                 node.col_offset,
-                                "M187b",
+                                "M193b",
                                 node.func.id,
                                 node.args[0].func.id,
                                 not reverseFlagValue,
                             )
+                        ##if reverse_flag_value is None:
+                            ##remediation = " - toggle reverse argument to sorted()"
+                        ##else:
+                            ##remediation = " - use sorted(..., reverse={!r})".format(
+                                ##not reverse_flag_value
+                            ##)
+
                     else:
                         self.__error(
                             node.lineno - 1,
                             node.col_offset,
-                            "M187c",
+                            "M193c",
                             node.func.id,
                             node.args[0].func.id,
                         )
 
                 elif (
-                    nArgs > 0
+                    numPositionalArgs > 0
                     and isinstance(node.args[0], ast.Call)
                     and isinstance(node.args[0].func, ast.Name)
                     and (
@@ -999,14 +1046,14 @@
                     self.__error(
                         node.lineno - 1,
                         node.col_offset,
-                        "M188",
+                        "M194",
                         node.args[0].func.id,
                         node.func.id,
                     )
 
                 elif (
                     node.func.id in {"reversed", "set", "sorted"}
-                    and nArgs > 0
+                    and numPositionalArgs > 0
                     and isinstance(node.args[0], ast.Subscript)
                     and isinstance(node.args[0].slice, ast.Slice)
                     and node.args[0].slice.lower is None
@@ -1016,16 +1063,66 @@
                     and isinstance(node.args[0].slice.step.operand, ast.Constant)
                     and node.args[0].slice.step.operand.n == 1
                 ):
-                    self.__error(node.lineno - 1, node.col_offset, "M189", node.func.id)
-
-                elif isinstance(node, (ast.ListComp, ast.SetComp)) and (
+                    self.__error(node.lineno - 1, node.col_offset, "M195", node.func.id)
+
+                elif (
+                    node.func.id == "map"
+                    and node not in visitedMapCalls
+                    and len(node.args) == 2
+                    and isinstance(node.args[0], ast.Lambda)
+                ):
+                    self.__error(node.lineno - 1, node.col_offset, "M197", "generator expression")
+
+                elif (
+                    node.func.id in ("list", "set", "dict")
+                    and len(node.args) == 1
+                    and isinstance(node.args[0], ast.Call)
+                    and isinstance(node.args[0].func, ast.Name)
+                    and node.args[0].func.id == "map"
+                    and len(node.args[0].args) == 2
+                    and isinstance(node.args[0].args[0], ast.Lambda)
+                ):
+                    # To avoid raising M197 on the map() call inside the list/set/dict.
+                    mapCall = node.args[0]
+                    visitedMapCalls.add(mapCall)
+
+                    rewriteable = True
+                    if node.func.id == "dict":
+                        # For the generator expression to be rewriteable as a
+                        # dict comprehension, its lambda must return a 2-tuple.
+                        lambdaNode = node.args[0].args[0]
+                        if (
+                            not isinstance(lambdaNode.body, (ast.List, ast.Tuple))
+                            or len(lambdaNode.body.elts) != 2
+                        ):
+                            rewriteable = False
+
+                    if rewriteable:
+                        comprehensionType = f"{node.func.id} comprehension"
+                        self.__error(node.lineno - 1, node.col_offset, "M197", comprehensionType)
+
+                elif isinstance(node, (ast.DictComp, ast.ListComp, ast.SetComp)) and (
                     len(node.generators) == 1
                     and not node.generators[0].ifs
                     and not node.generators[0].is_async
                     and (
-                        isinstance(node.elt, ast.Name)
-                        and isinstance(node.generators[0].target, ast.Name)
-                        and node.elt.id == node.generators[0].target.id
+                        (
+                            isinstance(node, (ast.ListComp, ast.SetComp))
+                            and isinstance(node.elt, ast.Name)
+                            and isinstance(node.generators[0].target, ast.Name)
+                            and node.elt.id == node.generators[0].target.id
+                        )
+                        or (
+                            isinstance(node, ast.DictComp)
+                            and isinstance(node.key, ast.Name)
+                            and isinstance(node.value, ast.Name)
+                            and isinstance(node.generators[0].target, ast.Tuple)
+                            and len(node.generators[0].target.elts) == 2
+                            and isinstance(node.generators[0].target.elts[0], ast.Name)
+                            and node.generators[0].target.elts[0].id == node.key.id
+                            and isinstance(node.generators[0].target.elts[1], ast.Name)
+                            and node.generators[0].target.elts[1].id == node.value.id
+                        )
                     )
                 ):
                     compType = {

eric ide

mercurial