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

branch
eric7
changeset 11150
73d80859079c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/SysVersionVisitor.py	Thu Feb 27 14:42:39 2025 +0100
@@ -0,0 +1,198 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2025 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a node visitor to check the use of sys.version and sys.version_info.
+"""
+
+import ast
+
+import AstUtilities
+
+
+class SysVersionVisitor(ast.NodeVisitor):
+    """
+    Class implementing a node visitor to check the use of sys.version and
+    sys.version_info.
+
+    Note: This class is modeled after flake8-2020 v1.8.1.
+    """
+
+    def __init__(self):
+        """
+        Constructor
+        """
+        super().__init__()
+
+        self.violations = []
+        self.__fromImports = {}
+
+    def visit_ImportFrom(self, node):
+        """
+        Public method to handle a from ... import ... statement.
+
+        @param node reference to the node to be processed
+        @type ast.ImportFrom
+        """
+        for alias in node.names:
+            if node.module is not None and not alias.asname:
+                self.__fromImports[alias.name] = node.module
+
+        self.generic_visit(node)
+
+    def __isSys(self, attr, node):
+        """
+        Private method to check for a reference to sys attribute.
+
+        @param attr attribute name
+        @type str
+        @param node reference to the node to be checked
+        @type ast.Node
+        @return flag indicating a match
+        @rtype bool
+        """
+        match = False
+        if (
+            isinstance(node, ast.Attribute)
+            and isinstance(node.value, ast.Name)
+            and node.value.id == "sys"
+            and node.attr == attr
+        ) or (
+            isinstance(node, ast.Name)
+            and node.id == attr
+            and self.__fromImports.get(node.id) == "sys"
+        ):
+            match = True
+
+        return match
+
+    def __isSysVersionUpperSlice(self, node, n):
+        """
+        Private method to check the upper slice of sys.version.
+
+        @param node reference to the node to be checked
+        @type ast.Node
+        @param n slice value to check against
+        @type int
+        @return flag indicating a match
+        @rtype bool
+        """
+        return (
+            self.__isSys("version", node.value)
+            and isinstance(node.slice, ast.Slice)
+            and node.slice.lower is None
+            and AstUtilities.isNumber(node.slice.upper)
+            and AstUtilities.getValue(node.slice.upper) == n
+            and node.slice.step is None
+        )
+
+    def visit_Subscript(self, node):
+        """
+        Public method to handle a subscript.
+
+        @param node reference to the node to be processed
+        @type ast.Subscript
+        """
+        if self.__isSysVersionUpperSlice(node, 1):
+            self.violations.append((node.value, "M-423"))
+        elif self.__isSysVersionUpperSlice(node, 3):
+            self.violations.append((node.value, "M-401"))
+        elif (
+            self.__isSys("version", node.value)
+            and isinstance(node.slice, ast.Index)
+            and AstUtilities.isNumber(node.slice.value)
+            and AstUtilities.getValue(node.slice.value) == 2
+        ):
+            self.violations.append((node.value, "M-402"))
+        elif (
+            self.__isSys("version", node.value)
+            and isinstance(node.slice, ast.Index)
+            and AstUtilities.isNumber(node.slice.value)
+            and AstUtilities.getValue(node.slice.value) == 0
+        ):
+            self.violations.append((node.value, "M-421"))
+
+        self.generic_visit(node)
+
+    def visit_Compare(self, node):
+        """
+        Public method to handle a comparison.
+
+        @param node reference to the node to be processed
+        @type ast.Compare
+        """
+        if (
+            isinstance(node.left, ast.Subscript)
+            and self.__isSys("version_info", node.left.value)
+            and isinstance(node.left.slice, ast.Index)
+            and AstUtilities.isNumber(node.left.slice.value)
+            and AstUtilities.getValue(node.left.slice.value) == 0
+            and len(node.ops) == 1
+            and isinstance(node.ops[0], ast.Eq)
+            and AstUtilities.isNumber(node.comparators[0])
+            and AstUtilities.getValue(node.comparators[0]) == 3
+        ):
+            self.violations.append((node.left, "M-411"))
+        elif (
+            self.__isSys("version", node.left)
+            and len(node.ops) == 1
+            and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE))
+            and AstUtilities.isString(node.comparators[0])
+        ):
+            if len(AstUtilities.getValue(node.comparators[0])) == 1:
+                errorCode = "M-422"
+            else:
+                errorCode = "M-403"
+            self.violations.append((node.left, errorCode))
+        elif (
+            isinstance(node.left, ast.Subscript)
+            and self.__isSys("version_info", node.left.value)
+            and isinstance(node.left.slice, ast.Index)
+            and AstUtilities.isNumber(node.left.slice.value)
+            and AstUtilities.getValue(node.left.slice.value) == 1
+            and len(node.ops) == 1
+            and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE))
+            and AstUtilities.isNumber(node.comparators[0])
+        ):
+            self.violations.append((node, "M-413"))
+        elif (
+            isinstance(node.left, ast.Attribute)
+            and self.__isSys("version_info", node.left.value)
+            and node.left.attr == "minor"
+            and len(node.ops) == 1
+            and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE))
+            and AstUtilities.isNumber(node.comparators[0])
+        ):
+            self.violations.append((node, "M-414"))
+
+        self.generic_visit(node)
+
+    def visit_Attribute(self, node):
+        """
+        Public method to handle an attribute.
+
+        @param node reference to the node to be processed
+        @type ast.Attribute
+        """
+        if (
+            isinstance(node.value, ast.Name)
+            and node.value.id == "six"
+            and node.attr == "PY3"
+        ):
+            self.violations.append((node, "M-412"))
+
+        self.generic_visit(node)
+
+    def visit_Name(self, node):
+        """
+        Public method to handle an name.
+
+        @param node reference to the node to be processed
+        @type ast.Name
+        """
+        if node.id == "PY3" and self.__fromImports.get(node.id) == "six":
+            self.violations.append((node, "M-412"))
+
+        self.generic_visit(node)

eric ide

mercurial