eric6/Plugins/CheckerPlugins/CodeStyleChecker/MiscellaneousChecker.py

changeset 7245
b47179fbb9d8
parent 7057
0e8d3b0c4889
child 7249
0bf517e60f54
equal deleted inserted replaced
7244:3e6502e77117 7245:b47179fbb9d8
46 46
47 ## Shadowed Builtins 47 ## Shadowed Builtins
48 "M131", "M132", 48 "M131", "M132",
49 49
50 ## Comprehensions 50 ## Comprehensions
51 "M191", "M192", "M193", "M194", 51 "M181", "M182", "M183", "M184",
52 "M185", "M186", "M187",
53 "M191", "M192", "M193",
52 "M195", "M196", "M197", "M198", 54 "M195", "M196", "M197", "M198",
53 55
54 ## Dictionaries with sorted keys 56 ## Dictionaries with sorted keys
55 "M201", 57 "M201",
56 58
57 ## Naive datetime usage 59 ## Naive datetime usage
58 "M301", "M302", "M303", "M304", "M305", "M306", "M307", "M308", 60 "M301", "M302", "M303", "M304", "M305", "M306", "M307", "M308",
59 "M311", "M312", "M313", "M314", "M315", 61 "M311", "M312", "M313", "M314", "M315",
60 "M321", 62 "M321",
63
64 ## sys.version and sys.version_info usage
65 "M401", "M402", "M403",
66 "M411", "M412", "M413", "M414",
67 "M421", "M422", "M423",
61 68
62 ## Bugbear 69 ## Bugbear
63 "M501", "M502", "M503", "M504", "M505", "M506", "M507", "M508", 70 "M501", "M502", "M503", "M504", "M505", "M506", "M507", "M508",
64 "M509", 71 "M509",
65 "M511", "M512", "M513", 72 "M511", "M512", "M513",
159 166
160 checkersWithCodes = [ 167 checkersWithCodes = [
161 (self.__checkCoding, ("M101", "M102")), 168 (self.__checkCoding, ("M101", "M102")),
162 (self.__checkCopyright, ("M111", "M112")), 169 (self.__checkCopyright, ("M111", "M112")),
163 (self.__checkBuiltins, ("M131", "M132")), 170 (self.__checkBuiltins, ("M131", "M132")),
164 (self.__checkComprehensions, ("M191", "M192", "M193", "M194", 171 (self.__checkComprehensions, ("M181", "M182", "M183", "M184",
172 "M185", "M186", "M187",
173 "M191", "M192", "M193",
165 "M195", "M196", "M197", "M198")), 174 "M195", "M196", "M197", "M198")),
166 (self.__checkDictWithSortedKeys, ("M201",)), 175 (self.__checkDictWithSortedKeys, ("M201",)),
176 (self.__checkDateTime, ("M301", "M302", "M303", "M304", "M305",
177 "M306", "M307", "M308", "M311", "M312",
178 "M313", "M314", "M315", "M321")),
179 (self.__checkSysVersion, ("M401", "M402", "M403",
180 "M411", "M412", "M413", "M414",
181 "M421", "M422", "M423")),
182 (self.__checkBugBear, ("M501", "M502", "M503", "M504", "M505",
183 "M506", "M507", "M508", "M509",
184 "M511", "M512", "M513",
185 "M521", "M522", "M523", "M524")),
167 (self.__checkPep3101, ("M601",)), 186 (self.__checkPep3101, ("M601",)),
168 (self.__checkFormatString, ("M611", "M612", "M613", 187 (self.__checkFormatString, ("M611", "M612", "M613",
169 "M621", "M622", "M623", "M624", "M625", 188 "M621", "M622", "M623", "M624", "M625",
170 "M631", "M632")), 189 "M631", "M632")),
171 (self.__checkBugBear, ("M501", "M502", "M503", "M504", "M505",
172 "M506", "M507", "M508", "M509",
173 "M511", "M512", "M513",
174 "M521", "M522", "M523", "M524")),
175 (self.__checkLogging, ("M651", "M652", "M653", "M654", "M655")), 190 (self.__checkLogging, ("M651", "M652", "M653", "M654", "M655")),
176 (self.__checkFuture, ("M701", "M702")), 191 (self.__checkFuture, ("M701", "M702")),
177 (self.__checkGettext, ("M711",)), 192 (self.__checkGettext, ("M711",)),
178 (self.__checkPrintStatements, ("M801",)), 193 (self.__checkPrintStatements, ("M801",)),
179 (self.__checkTuple, ("M811",)), 194 (self.__checkTuple, ("M811",)),
180 (self.__checkMutableDefault, ("M821", "M822")), 195 (self.__checkMutableDefault, ("M821", "M822")),
181 (self.__checkReturn, ("M831", "M832", "M833", "M834")), 196 (self.__checkReturn, ("M831", "M832", "M833", "M834")),
182 (self.__checkLineContinuation, ("M841",)), 197 (self.__checkLineContinuation, ("M841",)),
183 (self.__checkCommentedCode, ("M891",)), 198 (self.__checkCommentedCode, ("M891",)),
184 (self.__checkDateTime, ("M301", "M302", "M303", "M304", "M305",
185 "M306", "M307", "M308", "M311", "M312",
186 "M313", "M314", "M315", "M321")),
187 ] 199 ]
188 200
189 self.__defaultArgs = { 201 self.__defaultArgs = {
190 "BuiltinsChecker": { 202 "BuiltinsChecker": {
191 "chr": ["unichr", ], 203 "chr": ["unichr", ],
688 def __checkComprehensions(self): 700 def __checkComprehensions(self):
689 """ 701 """
690 Private method to check some comprehension related things. 702 Private method to check some comprehension related things.
691 """ 703 """
692 for node in ast.walk(self.__tree): 704 for node in ast.walk(self.__tree):
693 if (isinstance(node, ast.Call) and 705 if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
694 len(node.args) == 1 and 706 nArgs = len(node.args)
695 isinstance(node.func, ast.Name)): 707
696 if (isinstance(node.args[0], ast.GeneratorExp) and 708 if (
697 node.func.id in ('list', 'set', 'dict')): 709 nArgs == 1 and
710 isinstance(node.args[0], ast.GeneratorExp) and
711 node.func.id in ('list', 'set')
712 ):
698 errorCode = { 713 errorCode = {
699 "dict": "M193", 714 "list": "M181",
700 "list": "M191", 715 "set": "M182",
701 "set": "M192",
702 }[node.func.id] 716 }[node.func.id]
703 self.__error(node.lineno - 1, node.col_offset, errorCode) 717 self.__error(node.lineno - 1, node.col_offset, errorCode)
704 718
705 elif (isinstance(node.args[0], ast.ListComp) and 719 elif (
706 node.func.id in ('set', 'dict')): 720 nArgs == 1 and
721 isinstance(node.args[0], ast.GeneratorExp) and
722 isinstance(node.args[0].elt, ast.Tuple) and
723 len(node.args[0].elt.elts) == 2 and
724 node.func.id == "dict"
725 ):
726 self.__error(node.lineno - 1, node.col_offset, "M183")
727
728 elif (
729 nArgs == 1 and
730 isinstance(node.args[0], ast.ListComp) and
731 node.func.id in ('list', 'set', 'dict')
732 ):
707 errorCode = { 733 errorCode = {
708 'dict': 'M195', 734 'list': 'M195',
709 'set': 'M194', 735 'dict': 'M185',
736 'set': 'M184',
710 }[node.func.id] 737 }[node.func.id]
711 self.__error(node.lineno - 1, node.col_offset, errorCode) 738 self.__error(node.lineno - 1, node.col_offset, errorCode)
712 739
713 elif (isinstance(node.args[0], ast.List) and 740 elif nArgs == 1 and (
714 node.func.id in ('set', 'dict')): 741 isinstance(node.args[0], ast.Tuple) and
742 node.func.id == "tuple" or
743 isinstance(node.args[0], ast.List) and
744 node.func.id == "list"
745 ):
715 errorCode = { 746 errorCode = {
716 'dict': 'M197', 747 'tuple': 'M197',
717 'set': 'M196', 748 'list': 'M198',
718 }[node.func.id] 749 }[node.func.id]
719 self.__error(node.lineno - 1, node.col_offset, errorCode) 750 self.__error(node.lineno - 1, node.col_offset, errorCode,
720 751 type(node.args[0]).__name__.lower(),
721 elif (isinstance(node.args[0], ast.ListComp) and
722 node.func.id in ('all', 'any', 'frozenset', 'max', 'min',
723 'sorted', 'sum', 'tuple',)):
724 self.__error(node.lineno - 1, node.col_offset, "M198",
725 node.func.id) 752 node.func.id)
753
754 elif (
755 nArgs == 1 and
756 isinstance(node.args[0], (ast.Tuple, ast.List)) and
757 node.func.id in ("tuple", "list", "set", "dict")
758 ):
759 errorCode = {
760 "tuple": "M192",
761 "list": "M193",
762 "set": "M191",
763 "dict": "M191",
764 }[node.func.id]
765 self.__error(node.lineno - 1, node.col_offset, errorCode,
766 type(node.args[0]).__name__.lower(),
767 node.func.id)
768
769 elif (
770 nArgs == 1 and
771 isinstance(node.args[0], ast.ListComp) and
772 node.func.id in ('all', 'any', 'enumerate', 'frozenset',
773 'max', 'min', 'sorted', 'sum', 'tuple',)
774 ):
775 self.__error(node.lineno - 1, node.col_offset, "M187",
776 node.func.id)
777
778 elif (
779 nArgs == 0 and
780 not any(isinstance(a, ast.Starred) for a in node.args) and
781 not any(k.arg is None for k in node.keywords) and
782 node.func.id in ("tuple", "list", "dict")
783 ):
784 self.__error(node.lineno - 1, node.col_offset, "M186",
785 node.func.id)
786
787 elif isinstance(node, ast.Compare) and (
788 len(node.ops) == 1 and
789 isinstance(node.ops[0], ast.In) and
790 len(node.comparators) == 1 and
791 isinstance(node.comparators[0], ast.ListComp)
792 ):
793 self.__error(node.lineno - 1, node.col_offset, "M196")
726 794
727 def __checkMutableDefault(self): 795 def __checkMutableDefault(self):
728 """ 796 """
729 Private method to check for use of mutable types as default arguments. 797 Private method to check for use of mutable types as default arguments.
730 """ 798 """
874 visitor.visit(tree) 942 visitor.visit(tree)
875 for violation in visitor.violations: 943 for violation in visitor.violations:
876 node = violation[0] 944 node = violation[0]
877 reason = violation[1] 945 reason = violation[1]
878 self.__error(node.lineno - 1, node.col_offset, reason) 946 self.__error(node.lineno - 1, node.col_offset, reason)
947
948 def __checkSysVersion(self):
949 """
950 Private method to check the use of sys.version and sys.version_info.
951 """
952 visitor = SysVersionVisitor()
953 visitor.visit(self.__tree)
954 for violation in visitor.violations:
955 node = violation[0]
956 reason = violation[1]
957 self.__error(node.lineno - 1, node.col_offset, reason)
879 958
880 959
881 class TextVisitor(ast.NodeVisitor): 960 class TextVisitor(ast.NodeVisitor):
882 """ 961 """
883 Class implementing a node visitor for bytes and str instances. 962 Class implementing a node visitor for bytes and str instances.
1965 elif node.func.attr == 'fromisoformat': 2044 elif node.func.attr == 'fromisoformat':
1966 self.violations.append((node, "M315")) 2045 self.violations.append((node, "M315"))
1967 2046
1968 self.generic_visit(node) 2047 self.generic_visit(node)
1969 2048
2049
2050 class SysVersionVisitor(ast.NodeVisitor):
2051 """
2052 Class implementing a node visitor to check the use of sys.version and
2053 sys.version_info.
2054
2055 Note: This class is modelled after flake8-2020 checker.
2056 """
2057 def __init__(self):
2058 """
2059 Constructor
2060 """
2061 super(SysVersionVisitor, self).__init__()
2062
2063 self.violations = []
2064 self.__fromImports = {}
2065
2066 def visit_ImportFrom(self, node):
2067 """
2068 Public method to handle a from ... import ... statement.
2069
2070 @param node reference to the node to be processed
2071 @type ast.ImportFrom
2072 """
2073 for alias in node.names:
2074 if node.module is not None and not alias.asname:
2075 self.__fromImports[alias.name] = node.module
2076
2077 self.generic_visit(node)
2078
2079 def __isSys(self, attr, node):
2080 """
2081 Private method to check for a reference to sys attribute.
2082
2083 @param attr attribute name
2084 @type str
2085 @param node reference to the node to be checked
2086 @type ast.Node
2087 @return flag indicating a match
2088 @rtype bool
2089 """
2090 match = False
2091 if (
2092 isinstance(node, ast.Attribute) and
2093 isinstance(node.value, ast.Name) and
2094 node.value.id == "sys" and
2095 node.attr == attr
2096 ):
2097 match = True
2098 elif (
2099 isinstance(node, ast.Name) and
2100 node.id == attr and
2101 self.__fromImports.get(node.id) == "sys"
2102 ):
2103 match = True
2104
2105 return match
2106
2107 def __isSysVersionUpperSlice(self, node, n):
2108 """
2109 Private method to check the upper slice of sys.version.
2110
2111 @param node reference to the node to be checked
2112 @type ast.Node
2113 @param n slice value to check against
2114 @type int
2115 @return flag indicating a match
2116 @rtype bool
2117 """
2118 return (
2119 self.__isSys("version", node.value) and
2120 isinstance(node.slice, ast.Slice) and
2121 node.slice.lower is None and
2122 isinstance(node.slice.upper, ast.Num) and
2123 node.slice.upper.n == n and
2124 node.slice.step is None
2125 )
2126
2127 def visit_Subscript(self, node):
2128 """
2129 Public method to handle a subscript.
2130
2131 @param node reference to the node to be processed
2132 @type ast.Subscript
2133 """
2134 if self.__isSysVersionUpperSlice(node, 1):
2135 self.violations.append((node.value, "M423"))
2136 elif self.__isSysVersionUpperSlice(node, 3):
2137 self.violations.append((node.value, "M401"))
2138 elif (
2139 self.__isSys('version', node.value) and
2140 isinstance(node.slice, ast.Index) and
2141 isinstance(node.slice.value, ast.Num) and
2142 node.slice.value.n == 2
2143 ):
2144 self.violations.append((node.value, "M402"))
2145 elif (
2146 self.__isSys('version', node.value) and
2147 isinstance(node.slice, ast.Index) and
2148 isinstance(node.slice.value, ast.Num) and
2149 node.slice.value.n == 0
2150 ):
2151 self.violations.append((node.value, "M421"))
2152
2153 self.generic_visit(node)
2154
2155 def visit_Compare(self, node):
2156 """
2157 Public method to handle a comparison.
2158
2159 @param node reference to the node to be processed
2160 @type ast.Compare
2161 """
2162 if (
2163 isinstance(node.left, ast.Subscript) and
2164 self.__isSys('version_info', node.left.value) and
2165 isinstance(node.left.slice, ast.Index) and
2166 isinstance(node.left.slice.value, ast.Num) and
2167 node.left.slice.value.n == 0 and
2168 len(node.ops) == 1 and
2169 isinstance(node.ops[0], ast.Eq) and
2170 isinstance(node.comparators[0], ast.Num) and
2171 node.comparators[0].n == 3
2172 ):
2173 self.violations.append((node.left, "M411"))
2174 elif (
2175 self.__isSys('version', node.left) and
2176 len(node.ops) == 1 and
2177 isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) and
2178 isinstance(node.comparators[0], ast.Str)
2179 ):
2180 if len(node.comparators[0].s) == 1:
2181 errorCode = "M422"
2182 else:
2183 errorCode = "M403"
2184 self.violations.append((node.left, errorCode))
2185 elif (
2186 isinstance(node.left, ast.Subscript) and
2187 self.__isSys('version_info', node.left.value) and
2188 isinstance(node.left.slice, ast.Index) and
2189 isinstance(node.left.slice.value, ast.Num) and
2190 node.left.slice.value.n == 1 and
2191 len(node.ops) == 1 and
2192 isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) and
2193 isinstance(node.comparators[0], ast.Num)
2194 ):
2195 self.violations.append((node, "M413"))
2196 elif (
2197 isinstance(node.left, ast.Attribute) and
2198 self.__isSys('version_info', node.left.value) and
2199 node.left.attr == 'minor' and
2200 len(node.ops) == 1 and
2201 isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) and
2202 isinstance(node.comparators[0], ast.Num)
2203 ):
2204 self.violations.append((node, "M414"))
2205
2206 self.generic_visit(node)
2207
2208 def visit_Attribute(self, node):
2209 """
2210 Public method to handle an attribute.
2211
2212 @param node reference to the node to be processed
2213 @type ast.Attribute
2214 """
2215 if (
2216 isinstance(node.value, ast.Name) and
2217 node.value.id == 'six' and
2218 node.attr == 'PY3'
2219 ):
2220 self.violations.append((node, "M412"))
2221
2222 self.generic_visit(node)
2223
2224 def visit_Name(self, node):
2225 """
2226 Public method to handle an name.
2227
2228 @param node reference to the node to be processed
2229 @type ast.Name
2230 """
2231 if node.id == 'PY3' and self.__fromImports.get(node.id) == 'six':
2232 self.violations.append((node, "M412"))
2233
2234 self.generic_visit(node)
2235
1970 # 2236 #
1971 # eflag: noqa = M702 2237 # eflag: noqa = M702, M891

eric ide

mercurial