##// 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 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 import ast
3 import ast
4 import collections
4 import os
5 import os
5 import sys
6 import sys
6
7
@@ -37,6 +38,17 b' def usingabsolute(root):'
37
38
38 return False
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 def dotted_name_of_path(path, trimpure=False):
52 def dotted_name_of_path(path, trimpure=False):
41 """Given a relative path to a source file, return its dotted module name.
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 else:
336 else:
325 return verify_stdlib_on_own_line(root)
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 """Verify a file conforms to the modern import convention rules.
340 """Verify a file conforms to the modern import convention rules.
329
341
330 The rules of the modern convention are:
342 The rules of the modern convention are:
@@ -361,10 +373,15 b' def verify_modern_convention(module, roo'
361 # Relative import levels encountered so far.
373 # Relative import levels encountered so far.
362 seenlevels = set()
374 seenlevels = set()
363
375
364 for node in ast.walk(root):
376 for node, newscope in walklocal(root):
365 def msg(fmt, *args):
377 def msg(fmt, *args):
366 return (fmt % args, node.lineno)
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 # Disallow "import foo, bar" and require separate imports
385 # Disallow "import foo, bar" and require separate imports
369 # for each module.
386 # for each module.
370 if len(node.names) > 1:
387 if len(node.names) > 1:
@@ -375,7 +392,7 b' def verify_modern_convention(module, roo'
375 asname = node.names[0].asname
392 asname = node.names[0].asname
376
393
377 # Ignore sorting rules on imports inside blocks.
394 # Ignore sorting rules on imports inside blocks.
378 if node.col_offset == 0:
395 if node.col_offset == root_col_offset:
379 if lastname and name < lastname:
396 if lastname and name < lastname:
380 yield msg('imports not lexically sorted: %s < %s',
397 yield msg('imports not lexically sorted: %s < %s',
381 name, lastname)
398 name, lastname)
@@ -384,7 +401,7 b' def verify_modern_convention(module, roo'
384
401
385 # stdlib imports should be before local imports.
402 # stdlib imports should be before local imports.
386 stdlib = name in stdlib_modules
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 yield msg('stdlib import follows local import: %s', name)
405 yield msg('stdlib import follows local import: %s', name)
389
406
390 if not stdlib:
407 if not stdlib:
@@ -423,7 +440,7 b' def verify_modern_convention(module, roo'
423
440
424 # Direct symbol import is only allowed from certain modules and
441 # Direct symbol import is only allowed from certain modules and
425 # must occur before non-symbol imports.
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 if fullname not in allowsymbolimports:
444 if fullname not in allowsymbolimports:
428 yield msg('direct symbol import from %s', fullname)
445 yield msg('direct symbol import from %s', fullname)
429
446
@@ -436,7 +453,8 b' def verify_modern_convention(module, roo'
436 seennonsymbolrelative = True
453 seennonsymbolrelative = True
437
454
438 # Only allow 1 group per level.
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 yield msg('multiple "from %s import" statements',
458 yield msg('multiple "from %s import" statements',
441 '.' * node.level)
459 '.' * node.level)
442
460
@@ -74,6 +74,17 b' Run additional tests for the import chec'
74 > from . import levelpriority # should not cause cycle
74 > from . import levelpriority # should not cause cycle
75 > EOF
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 $ cat > testpackage/sortedentries.py << EOF
88 $ cat > testpackage/sortedentries.py << EOF
78 > from __future__ import absolute_import
89 > from __future__ import absolute_import
79 > from . import (
90 > from . import (
@@ -105,6 +116,8 b' Run additional tests for the import chec'
105 testpackage/sortedentries.py:2: imports from testpackage not lexically sorted: bar < foo
116 testpackage/sortedentries.py:2: imports from testpackage not lexically sorted: bar < foo
106 testpackage/stdafterlocal.py:3: stdlib import follows local import: os
117 testpackage/stdafterlocal.py:3: stdlib import follows local import: os
107 testpackage/subpackage/levelpriority.py:3: higher-level import should come first: testpackage
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 testpackage/symbolimport.py:2: direct symbol import from testpackage.unsorted
121 testpackage/symbolimport.py:2: direct symbol import from testpackage.unsorted
109 testpackage/unsorted.py:3: imports not lexically sorted: os < sys
122 testpackage/unsorted.py:3: imports not lexically sorted: os < sys
110 [1]
123 [1]
General Comments 0
You need to be logged in to leave comments. Login now