16 Class implementing a checker for import statements. |
17 Class implementing a checker for import statements. |
17 """ |
18 """ |
18 Codes = [ |
19 Codes = [ |
19 ## Local imports |
20 ## Local imports |
20 "I101", "I102", "I103", |
21 "I101", "I102", "I103", |
|
22 |
|
23 ## Imports order |
|
24 "I201", "I202", "I203", "I204", |
21 ] |
25 ] |
22 |
26 |
23 def __init__(self, source, filename, tree, select, ignore, expected, |
27 def __init__(self, source, filename, tree, select, ignore, expected, |
24 repeat, args): |
28 repeat, args): |
25 """ |
29 """ |
196 from .LocalImportVisitor import LocalImportVisitor |
201 from .LocalImportVisitor import LocalImportVisitor |
197 |
202 |
198 visitor = LocalImportVisitor(self.__args, self) |
203 visitor = LocalImportVisitor(self.__args, self) |
199 visitor.visit(copy.deepcopy(self.__tree)) |
204 visitor.visit(copy.deepcopy(self.__tree)) |
200 for violation in visitor.violations: |
205 for violation in visitor.violations: |
201 node = violation[0] |
206 if not self.__ignoreCode(violation[1]): |
202 reason = violation[1] |
207 node = violation[0] |
203 self.__error(node.lineno - 1, node.col_offset, reason) |
208 reason = violation[1] |
|
209 self.__error(node.lineno - 1, node.col_offset, reason) |
|
210 |
|
211 ####################################################################### |
|
212 ## Import order |
|
213 ## |
|
214 ## adapted from: flake8-alphabetize v0.0.17 |
|
215 ####################################################################### |
|
216 |
|
217 def __checkImportOrder(self): |
|
218 """ |
|
219 Private method to check the order of import statements. |
|
220 """ |
|
221 from .ImportNode import ImportNode |
|
222 |
|
223 errors = [] |
|
224 imports = [] |
|
225 importNodes, listNode = self.__findNodes(self.__tree) |
|
226 |
|
227 # check for an error in '__all__' |
|
228 allError = self.__findErrorInAll(listNode) |
|
229 if allError is not None: |
|
230 errors.append(allError) |
|
231 |
|
232 for importNode in importNodes: |
|
233 if ( |
|
234 isinstance(importNode, ast.Import) and |
|
235 len(importNode.names) > 1 |
|
236 ): |
|
237 # skip suck imports because its already handled by pycodestyle |
|
238 continue |
|
239 |
|
240 imports.append(ImportNode( |
|
241 self.__args.get("ApplicationPackageNames", []), |
|
242 importNode, self)) |
|
243 |
|
244 lenImports = len(imports) |
|
245 if lenImports > 0: |
|
246 p = imports[0] |
|
247 if p.error is not None: |
|
248 errors.append(p.error) |
|
249 |
|
250 if lenImports > 1: |
|
251 for n in imports[1:]: |
|
252 if n.error is not None: |
|
253 errors.append(n.error) |
|
254 |
|
255 if n == p: |
|
256 errors.append((n.node, "I203", str(p), str(n))) |
|
257 elif n < p: |
|
258 errors.append((n.node, "I201", str(n), str(p))) |
|
259 |
|
260 p = n |
|
261 |
|
262 for error in errors: |
|
263 if not self.__ignoreCode(error[1]): |
|
264 node = error[0] |
|
265 reason = error[1] |
|
266 args = error[2:] |
|
267 self.__error(node.lineno - 1, node.col_offset, reason, *args) |
|
268 |
|
269 def __findNodes(self, tree): |
|
270 """ |
|
271 Private method to find all import and import from nodes of the given |
|
272 tree. |
|
273 |
|
274 @param tree reference to the ast node tree to be parsed |
|
275 @type ast.AST |
|
276 @return tuple containing a list of import nodes and the '__all__' node |
|
277 @rtype tuple of (ast.Import | ast.ImportFrom, ast.List | ast.Tuple) |
|
278 """ |
|
279 importNodes = [] |
|
280 listNode = None |
|
281 |
|
282 if isinstance(tree, ast.Module): |
|
283 body = tree.body |
|
284 |
|
285 for n in body: |
|
286 if isinstance(n, (ast.Import, ast.ImportFrom)): |
|
287 importNodes.append(n) |
|
288 |
|
289 elif isinstance(n, ast.Assign): |
|
290 for t in n.targets: |
|
291 if isinstance(t, ast.Name) and t.id == "__all__": |
|
292 value = n.value |
|
293 |
|
294 if isinstance(value, (ast.List, ast.Tuple)): |
|
295 listNode = value |
|
296 |
|
297 return importNodes, listNode |
|
298 |
|
299 def __findErrorInAll(self, node): |
|
300 """ |
|
301 Private method to check the '__all__' node for errors. |
|
302 |
|
303 @param node reference to the '__all__' node |
|
304 @type ast.List or ast.Tuple |
|
305 @return tuple containing a reference to the node and an error code |
|
306 @rtype rtype tuple of (ast.List | ast.Tuple, str) |
|
307 """ |
|
308 if node is not None: |
|
309 actualList = [] |
|
310 for el in node.elts: |
|
311 if isinstance(el, ast.Constant): |
|
312 actualList.append(el.value) |
|
313 elif isinstance(el, ast.Str): |
|
314 actualList.append(el.s) |
|
315 else: |
|
316 # Can't handle anything that isn't a string literal |
|
317 return None |
|
318 |
|
319 expectedList = sorted(actualList) |
|
320 if expectedList != actualList: |
|
321 return (node, "I204", ", ".join(expectedList)) |
|
322 |
|
323 return None |