Show More
@@ -1,6 +1,7 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | |
|
3 | 3 | import ast |
|
4 | import collections | |
|
4 | 5 | import os |
|
5 | 6 | import sys |
|
6 | 7 | |
@@ -37,6 +38,17 b' def usingabsolute(root):' | |||
|
37 | 38 | |
|
38 | 39 | return False |
|
39 | 40 | |
|
41 | def walklocal(root): | |
|
42 | """Recursively yield all descendant nodes but not in a different scope""" | |
|
43 | todo = collections.deque(ast.iter_child_nodes(root)) | |
|
44 | yield root, False | |
|
45 | while todo: | |
|
46 | node = todo.popleft() | |
|
47 | newscope = isinstance(node, ast.FunctionDef) | |
|
48 | if not newscope: | |
|
49 | todo.extend(ast.iter_child_nodes(node)) | |
|
50 | yield node, newscope | |
|
51 | ||
|
40 | 52 | def dotted_name_of_path(path, trimpure=False): |
|
41 | 53 | """Given a relative path to a source file, return its dotted module name. |
|
42 | 54 | |
@@ -324,7 +336,7 b' def verify_import_convention(module, sou' | |||
|
324 | 336 | else: |
|
325 | 337 | return verify_stdlib_on_own_line(root) |
|
326 | 338 | |
|
327 | def verify_modern_convention(module, root): | |
|
339 | def verify_modern_convention(module, root, root_col_offset=0): | |
|
328 | 340 | """Verify a file conforms to the modern import convention rules. |
|
329 | 341 | |
|
330 | 342 | The rules of the modern convention are: |
@@ -361,10 +373,15 b' def verify_modern_convention(module, roo' | |||
|
361 | 373 | # Relative import levels encountered so far. |
|
362 | 374 | seenlevels = set() |
|
363 | 375 | |
|
364 |
for node in |
|
|
376 | for node, newscope in walklocal(root): | |
|
365 | 377 | def msg(fmt, *args): |
|
366 | 378 | return (fmt % args, node.lineno) |
|
367 | if isinstance(node, ast.Import): | |
|
379 | if newscope: | |
|
380 | # Check for local imports in function | |
|
381 | for r in verify_modern_convention(module, node, | |
|
382 | node.col_offset + 4): | |
|
383 | yield r | |
|
384 | elif isinstance(node, ast.Import): | |
|
368 | 385 | # Disallow "import foo, bar" and require separate imports |
|
369 | 386 | # for each module. |
|
370 | 387 | if len(node.names) > 1: |
@@ -375,7 +392,7 b' def verify_modern_convention(module, roo' | |||
|
375 | 392 | asname = node.names[0].asname |
|
376 | 393 | |
|
377 | 394 | # Ignore sorting rules on imports inside blocks. |
|
378 |
if node.col_offset == |
|
|
395 | if node.col_offset == root_col_offset: | |
|
379 | 396 | if lastname and name < lastname: |
|
380 | 397 | yield msg('imports not lexically sorted: %s < %s', |
|
381 | 398 | name, lastname) |
@@ -384,7 +401,7 b' def verify_modern_convention(module, roo' | |||
|
384 | 401 | |
|
385 | 402 | # stdlib imports should be before local imports. |
|
386 | 403 | stdlib = name in stdlib_modules |
|
387 |
if stdlib and seenlocal and node.col_offset == |
|
|
404 | if stdlib and seenlocal and node.col_offset == root_col_offset: | |
|
388 | 405 | yield msg('stdlib import follows local import: %s', name) |
|
389 | 406 | |
|
390 | 407 | if not stdlib: |
@@ -423,7 +440,7 b' def verify_modern_convention(module, roo' | |||
|
423 | 440 | |
|
424 | 441 | # Direct symbol import is only allowed from certain modules and |
|
425 | 442 | # must occur before non-symbol imports. |
|
426 |
if node.module and node.col_offset == |
|
|
443 | if node.module and node.col_offset == root_col_offset: | |
|
427 | 444 | if fullname not in allowsymbolimports: |
|
428 | 445 | yield msg('direct symbol import from %s', fullname) |
|
429 | 446 | |
@@ -436,7 +453,8 b' def verify_modern_convention(module, roo' | |||
|
436 | 453 | seennonsymbolrelative = True |
|
437 | 454 | |
|
438 | 455 | # Only allow 1 group per level. |
|
439 |
if node.level in seenlevels |
|
|
456 | if (node.level in seenlevels | |
|
457 | and node.col_offset == root_col_offset): | |
|
440 | 458 | yield msg('multiple "from %s import" statements', |
|
441 | 459 | '.' * node.level) |
|
442 | 460 |
@@ -74,6 +74,17 b' Run additional tests for the import chec' | |||
|
74 | 74 | > from . import levelpriority # should not cause cycle |
|
75 | 75 | > EOF |
|
76 | 76 | |
|
77 | $ cat > testpackage/subpackage/localimport.py << EOF | |
|
78 | > from __future__ import absolute_import | |
|
79 | > from . import foo | |
|
80 | > def bar(): | |
|
81 | > # should not cause "higher-level import should come first" | |
|
82 | > from .. import unsorted | |
|
83 | > # but other errors should be detected | |
|
84 | > from .. import more | |
|
85 | > import testpackage.subpackage.levelpriority | |
|
86 | > EOF | |
|
87 | ||
|
77 | 88 | $ cat > testpackage/sortedentries.py << EOF |
|
78 | 89 | > from __future__ import absolute_import |
|
79 | 90 | > from . import ( |
@@ -105,6 +116,8 b' Run additional tests for the import chec' | |||
|
105 | 116 | testpackage/sortedentries.py:2: imports from testpackage not lexically sorted: bar < foo |
|
106 | 117 | testpackage/stdafterlocal.py:3: stdlib import follows local import: os |
|
107 | 118 | testpackage/subpackage/levelpriority.py:3: higher-level import should come first: testpackage |
|
119 | testpackage/subpackage/localimport.py:7: multiple "from .. import" statements | |
|
120 | testpackage/subpackage/localimport.py:8: import should be relative: testpackage.subpackage.levelpriority | |
|
108 | 121 | testpackage/symbolimport.py:2: direct symbol import from testpackage.unsorted |
|
109 | 122 | testpackage/unsorted.py:3: imports not lexically sorted: os < sys |
|
110 | 123 | [1] |
General Comments 0
You need to be logged in to leave comments.
Login now