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 """ |
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 |