src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Async/AsyncVisitor.py

branch
eric7
changeset 10116
4a619fb7bd09
child 10175
57ed3cb66e9a
diff -r 8ca3f08c5ac8 -r 4a619fb7bd09 src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Async/AsyncVisitor.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Async/AsyncVisitor.py	Mon Jul 17 17:37:38 2023 +0200
@@ -0,0 +1,217 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a node visitor to check async functions for use of synchronous
+functions.
+"""
+
+import ast
+import itertools
+import re
+
+try:
+    from ast import unparse
+except ImportError:
+    # Python < 3.9
+    from ast_unparse import unparse
+
+#
+# The visitor is adapted from flake8-async v22.11.14
+#
+
+
+class AsyncVisitor(ast.NodeVisitor):
+    """
+    Class implementing a node visitor for checking async functions for use of
+    synchronous functions.
+    """
+
+    HttpPackages = (
+        "requests",
+        "httpx",
+    )
+
+    HttpMethods = (
+        "close",
+        "delete",
+        "get",
+        "head",
+        "options",
+        "patch",
+        "post",
+        "put",
+        "request",
+        "send",
+        "stream",
+    )
+
+    Urllib3DangerousClasses = (
+        "HTTPConnectionPool",
+        "HTTPSConnectionPool",
+        "PoolManager",
+        "ProxyManager",
+        "connectionpool.ConnectionPool",
+        "connectionpool.HTTPConnectionPool",
+        "connectionpool.HTTPSConnectionPool",
+        "poolmanager.PoolManager",
+        "poolmanager.ProxyManager",
+        "request.RequestMethods",
+    )
+
+    SubprocessMethods = (
+        "run",
+        "Popen",
+        # deprecated methods
+        "call",
+        "check_call",
+        "check_output",
+        "getoutput",
+        "getstatusoutput",
+    )
+
+    OsProcessMethods = (
+        "popen",
+        "posix_spawn",
+        "posix_spawnp",
+        "spawnl",
+        "spawnle",
+        "spawnlp",
+        "spawnlpe",
+        "spawnv",
+        "spawnve",
+        "spawnvp",
+        "spawnvpe",
+        "system",
+    )
+
+    OsWaitMethods = (
+        "wait",
+        "wait3",
+        "wait4",
+        "waitid",
+        "waitpid",
+    )
+
+    OsPathFuncs = (
+        "_path_normpath",
+        "normpath",
+        "_joinrealpath",
+        "islink",
+        "lexists",
+        "ismount",  # safe on windows, unsafe on posix
+        "realpath",
+        "exists",
+        "isdir",
+        "isfile",
+        "getatime",
+        "getctime",
+        "getmtime",
+        "getsize",
+        "samefile",
+        "sameopenfile",
+        "relpath",
+    )
+
+    def __init__(self, args, checker):
+        """
+        Constructor
+
+        @param args dictionary containing the checker arguments
+        @type dict
+        @param checker reference to the checker
+        @type ImportsChecker
+        """
+        self.__appImportNames = args.get("ApplicationPackageNames", [])
+        self.__checker = checker
+
+        self.violations = []
+
+    def visit_AsyncFunctionDef(self, node):
+        """
+        Public method to handle an async function definition.
+
+        @param node reference to the node to be processed
+        @type ast.AsyncFunctionDef
+        """
+        for inner in itertools.chain.from_iterable(
+            map(ast.iter_child_nodes, node.body)
+        ):
+            errorCode = None
+            if (
+                isinstance(inner, ast.Call)
+                and isinstance(inner.func, ast.Name)
+                and inner.func.id == "open"
+            ):
+                errorCode = "ASY101"
+
+            elif (
+                isinstance(inner, ast.withitem)
+                and isinstance(inner.context_expr, ast.Call)
+                and isinstance(inner.context_expr.func, ast.Name)
+                and inner.context_expr.func.id == "open"
+            ):
+                errorCode = "ASY103"
+                inner = inner.context_expr
+
+            elif isinstance(inner, ast.Call):
+                funcName = unparse(inner.func)
+
+                if funcName in (
+                    "urllib3.request",
+                    "urllib.request.urlopen",
+                    "request.urlopen",
+                    "urlopen",
+                ):
+                    errorCode = "ASY100"
+                elif funcName == "time.sleep":
+                    errorCode = "ASY101"
+                else:
+                    match = re.fullmatch(
+                        r"(?P<package>{0}|os\.path|os|subprocess|urllib3)\."
+                        r"(?P<method>.*)".format("|".join(self.HttpPackages)),
+                        funcName,
+                    )
+                    if match:
+                        if (
+                            match.group("package") in self.HttpPackages
+                            and match.group("method") in self.HttpMethods
+                        ):
+                            errorCode = "ASY100"
+
+                        elif (
+                            match.group("package") == "subprocess"
+                            and match.group("method") in self.SubprocessMethods
+                        ) or (
+                            match.group("package") == "os"
+                            and match.group("method") in self.OsWaitMethods
+                        ):
+                            errorCode = "ASY101"
+
+                        elif (
+                            match.group("package") == "os"
+                            and match.group("method") in self.OsProcessMethods
+                        ):
+                            errorCode = "ASY102"
+
+                        elif (
+                            match.group("package") == "os.path"
+                            and match.group("method") in self.OsPathFuncs
+                        ):
+                            errorCode = "ASY104"
+
+                        elif (
+                            match.group("package") == "httpx"
+                            and match.group("method") == "Client"
+                        ) or (
+                            match.group("package") == "urllib3"
+                            and match.group("method") in self.Urllib3DangerousClasses
+                        ):
+                            errorCode = "ASY105"
+
+            if errorCode:
+                self.violations.append((inner, errorCode))
+
+        self.generic_visit(node)

eric ide

mercurial