15 |
15 |
16 class SimplifyChecker: |
16 class SimplifyChecker: |
17 """ |
17 """ |
18 Class implementing a checker for to help simplifying Python code. |
18 Class implementing a checker for to help simplifying Python code. |
19 """ |
19 """ |
|
20 |
20 Codes = [ |
21 Codes = [ |
21 # Python-specifics |
22 # Python-specifics |
22 "Y101", "Y102", "Y103", "Y104", "Y105", "Y106", "Y107", "Y108", |
23 "Y101", |
23 "Y109", "Y110", "Y111", "Y112", "Y113", "Y114", "Y115", "Y116", |
24 "Y102", |
24 "Y117", "Y118", "Y119", "Y120", "Y121", "Y122", |
25 "Y103", |
25 |
26 "Y104", |
|
27 "Y105", |
|
28 "Y106", |
|
29 "Y107", |
|
30 "Y108", |
|
31 "Y109", |
|
32 "Y110", |
|
33 "Y111", |
|
34 "Y112", |
|
35 "Y113", |
|
36 "Y114", |
|
37 "Y115", |
|
38 "Y116", |
|
39 "Y117", |
|
40 "Y118", |
|
41 "Y119", |
|
42 "Y120", |
|
43 "Y121", |
|
44 "Y122", |
26 # Python-specifics not part of flake8-simplify |
45 # Python-specifics not part of flake8-simplify |
27 "Y181", "Y182", |
46 "Y181", |
28 |
47 "Y182", |
29 # Comparations |
48 # Comparations |
30 "Y201", "Y202", "Y203", "Y204", "Y205", "Y206", "Y207", "Y208", |
49 "Y201", |
31 "Y211", "Y212", "Y213", |
50 "Y202", |
32 "Y221", "Y222", "Y223", "Y224", |
51 "Y203", |
33 |
52 "Y204", |
|
53 "Y205", |
|
54 "Y206", |
|
55 "Y207", |
|
56 "Y208", |
|
57 "Y211", |
|
58 "Y212", |
|
59 "Y213", |
|
60 "Y221", |
|
61 "Y222", |
|
62 "Y223", |
|
63 "Y224", |
34 # Opinionated |
64 # Opinionated |
35 "Y301", |
65 "Y301", |
36 |
|
37 # General Code Style |
66 # General Code Style |
38 "Y401", "Y402", |
67 "Y401", |
|
68 "Y402", |
39 ] |
69 ] |
40 |
70 |
41 def __init__(self, source, filename, tree, selected, ignored, expected, |
71 def __init__(self, source, filename, tree, selected, ignored, expected, repeat): |
42 repeat): |
|
43 """ |
72 """ |
44 Constructor |
73 Constructor |
45 |
74 |
46 @param source source code to be checked |
75 @param source source code to be checked |
47 @type list of str |
76 @type list of str |
48 @param filename name of the source file |
77 @param filename name of the source file |
49 @type str |
78 @type str |
50 @param tree AST tree of the source code |
79 @param tree AST tree of the source code |
57 @type list of str |
86 @type list of str |
58 @param repeat flag indicating to report each occurrence of a code |
87 @param repeat flag indicating to report each occurrence of a code |
59 @type bool |
88 @type bool |
60 """ |
89 """ |
61 self.__select = tuple(selected) |
90 self.__select = tuple(selected) |
62 self.__ignore = ('',) if selected else tuple(ignored) |
91 self.__ignore = ("",) if selected else tuple(ignored) |
63 self.__expected = expected[:] |
92 self.__expected = expected[:] |
64 self.__repeat = repeat |
93 self.__repeat = repeat |
65 self.__filename = filename |
94 self.__filename = filename |
66 self.__source = source[:] |
95 self.__source = source[:] |
67 self.__tree = copy.deepcopy(tree) |
96 self.__tree = copy.deepcopy(tree) |
68 |
97 |
69 # statistics counters |
98 # statistics counters |
70 self.counters = {} |
99 self.counters = {} |
71 |
100 |
72 # collection of detected errors |
101 # collection of detected errors |
73 self.errors = [] |
102 self.errors = [] |
74 |
103 |
75 self.__checkCodes = (code for code in self.Codes |
104 self.__checkCodes = (code for code in self.Codes if not self.__ignoreCode(code)) |
76 if not self.__ignoreCode(code)) |
105 |
77 |
|
78 def __ignoreCode(self, code): |
106 def __ignoreCode(self, code): |
79 """ |
107 """ |
80 Private method to check if the message code should be ignored. |
108 Private method to check if the message code should be ignored. |
81 |
109 |
82 @param code message code to check for |
110 @param code message code to check for |
83 @type str |
111 @type str |
84 @return flag indicating to ignore the given code |
112 @return flag indicating to ignore the given code |
85 @rtype bool |
113 @rtype bool |
86 """ |
114 """ |
87 return (code.startswith(self.__ignore) and |
115 return code.startswith(self.__ignore) and not code.startswith(self.__select) |
88 not code.startswith(self.__select)) |
116 |
89 |
|
90 def __error(self, lineNumber, offset, code, *args): |
117 def __error(self, lineNumber, offset, code, *args): |
91 """ |
118 """ |
92 Private method to record an issue. |
119 Private method to record an issue. |
93 |
120 |
94 @param lineNumber line number of the issue |
121 @param lineNumber line number of the issue |
95 @type int |
122 @type int |
96 @param offset position within line of the issue |
123 @param offset position within line of the issue |
97 @type int |
124 @type int |
98 @param code message code |
125 @param code message code |
100 @param args arguments for the message |
127 @param args arguments for the message |
101 @type list |
128 @type list |
102 """ |
129 """ |
103 if self.__ignoreCode(code): |
130 if self.__ignoreCode(code): |
104 return |
131 return |
105 |
132 |
106 # record the issue with one based line number |
133 # record the issue with one based line number |
107 errorInfo = { |
134 errorInfo = { |
108 "file": self.__filename, |
135 "file": self.__filename, |
109 "line": lineNumber + 1, |
136 "line": lineNumber + 1, |
110 "offset": offset, |
137 "offset": offset, |
111 "code": code, |
138 "code": code, |
112 "args": args, |
139 "args": args, |
113 } |
140 } |
114 |
141 |
115 if errorInfo not in self.errors: |
142 if errorInfo not in self.errors: |
116 # this issue was not seen before |
143 # this issue was not seen before |
117 if code in self.counters: |
144 if code in self.counters: |
118 self.counters[code] += 1 |
145 self.counters[code] += 1 |
119 else: |
146 else: |
120 self.counters[code] = 1 |
147 self.counters[code] = 1 |
121 |
148 |
122 # Don't care about expected codes |
149 # Don't care about expected codes |
123 if code in self.__expected: |
150 if code in self.__expected: |
124 return |
151 return |
125 |
152 |
126 if code and (self.counters[code] == 1 or self.__repeat): |
153 if code and (self.counters[code] == 1 or self.__repeat): |
127 self.errors.append(errorInfo) |
154 self.errors.append(errorInfo) |
128 |
155 |
129 def run(self): |
156 def run(self): |
130 """ |
157 """ |
131 Public method to check the given source against functions |
158 Public method to check the given source against functions |
132 to be replaced by 'pathlib' equivalents. |
159 to be replaced by 'pathlib' equivalents. |
133 """ |
160 """ |
134 if not self.__filename: |
161 if not self.__filename: |
135 # don't do anything, if essential data is missing |
162 # don't do anything, if essential data is missing |
136 return |
163 return |
137 |
164 |
138 if not self.__checkCodes: |
165 if not self.__checkCodes: |
139 # don't do anything, if no codes were selected |
166 # don't do anything, if no codes were selected |
140 return |
167 return |
141 |
168 |
142 # Add parent information |
169 # Add parent information |
143 for node in ast.walk(self.__tree): |
170 for node in ast.walk(self.__tree): |
144 for child in ast.iter_child_nodes(node): |
171 for child in ast.iter_child_nodes(node): |
145 child.parent = node # type: ignore |
172 child.parent = node # type: ignore |
146 |
173 |
147 visitor = SimplifyNodeVisitor(self.__error) |
174 visitor = SimplifyNodeVisitor(self.__error) |
148 visitor.visit(self.__tree) |
175 visitor.visit(self.__tree) |