##// END OF EJS Templates
import-checker: reset context to verify convention in function scope...
Yuya Nishihara -
r26965:1fa66d3a default
parent child Browse files
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 ast.walk(root):
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 == 0:
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 == 0:
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 == 0:
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 and node.col_offset == 0:
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