Mercurial > hg
annotate contrib/import-checker.py @ 26956:4b56214ebb7a
import-checker: include lineno in warning message
This makes it easy to look for imports in function scope.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sun, 01 Nov 2015 15:46:06 +0900 |
parents | c4114e335b49 |
children | 5abba2c92da3 |
rev | line source |
---|---|
26954
f804bf27439b
import-checker: make it executable for convenience
Yuya Nishihara <yuya@tcha.org>
parents:
26781
diff
changeset
|
1 #!/usr/bin/env python |
f804bf27439b
import-checker: make it executable for convenience
Yuya Nishihara <yuya@tcha.org>
parents:
26781
diff
changeset
|
2 |
20036 | 3 import ast |
4 import os | |
5 import sys | |
6 | |
20198
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
7 # Import a minimal set of stdlib modules needed for list_stdlib_modules() |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
8 # to work when run from a virtualenv. The modules were chosen empirically |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
9 # so that the return value matches the return value without virtualenv. |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
10 import BaseHTTPServer |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
11 import zlib |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
12 |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
13 # Whitelist of modules that symbols can be directly imported from. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
14 allowsymbolimports = ( |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
15 '__future__', |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
16 'mercurial.i18n', |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
17 'mercurial.node', |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
18 ) |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
19 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
20 # Modules that must be aliased because they are commonly confused with |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
21 # common variables and can create aliasing and readability issues. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
22 requirealias = { |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
23 'ui': 'uimod', |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
24 } |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
25 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
26 def usingabsolute(root): |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
27 """Whether absolute imports are being used.""" |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
28 if sys.version_info[0] >= 3: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
29 return True |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
30 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
31 for node in ast.walk(root): |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
32 if isinstance(node, ast.ImportFrom): |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
33 if node.module == '__future__': |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
34 for n in node.names: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
35 if n.name == 'absolute_import': |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
36 return True |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
37 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
38 return False |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
39 |
22975
461342e1c8aa
import-checker: check modules for pure Python build correctly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
22974
diff
changeset
|
40 def dotted_name_of_path(path, trimpure=False): |
20036 | 41 """Given a relative path to a source file, return its dotted module name. |
42 | |
43 >>> dotted_name_of_path('mercurial/error.py') | |
44 'mercurial.error' | |
22975
461342e1c8aa
import-checker: check modules for pure Python build correctly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
22974
diff
changeset
|
45 >>> dotted_name_of_path('mercurial/pure/parsers.py', trimpure=True) |
461342e1c8aa
import-checker: check modules for pure Python build correctly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
22974
diff
changeset
|
46 'mercurial.parsers' |
20383
4990abb4729d
import-checker: fix names of dynamically loaded modules
Mads Kiilerich <madski@unity3d.com>
parents:
20238
diff
changeset
|
47 >>> dotted_name_of_path('zlibmodule.so') |
4990abb4729d
import-checker: fix names of dynamically loaded modules
Mads Kiilerich <madski@unity3d.com>
parents:
20238
diff
changeset
|
48 'zlib' |
20036 | 49 """ |
50 parts = path.split('/') | |
20391
466e4c574db0
import-checker: handle standard modules with arch in the filename
Mads Kiilerich <madski@unity3d.com>
parents:
20386
diff
changeset
|
51 parts[-1] = parts[-1].split('.', 1)[0] # remove .py and .so and .ARCH.so |
20383
4990abb4729d
import-checker: fix names of dynamically loaded modules
Mads Kiilerich <madski@unity3d.com>
parents:
20238
diff
changeset
|
52 if parts[-1].endswith('module'): |
4990abb4729d
import-checker: fix names of dynamically loaded modules
Mads Kiilerich <madski@unity3d.com>
parents:
20238
diff
changeset
|
53 parts[-1] = parts[-1][:-6] |
22975
461342e1c8aa
import-checker: check modules for pure Python build correctly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
22974
diff
changeset
|
54 if trimpure: |
461342e1c8aa
import-checker: check modules for pure Python build correctly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
22974
diff
changeset
|
55 return '.'.join(p for p in parts if p != 'pure') |
20036 | 56 return '.'.join(parts) |
57 | |
25173
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
58 def fromlocalfunc(modulename, localmods): |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
59 """Get a function to examine which locally defined module the |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
60 target source imports via a specified name. |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
61 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
62 `modulename` is an `dotted_name_of_path()`-ed source file path, |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
63 which may have `.__init__` at the end of it, of the target source. |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
64 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
65 `localmods` is a dict (or set), of which key is an absolute |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
66 `dotted_name_of_path()`-ed source file path of locally defined (= |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
67 Mercurial specific) modules. |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
68 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
69 This function assumes that module names not existing in |
26781
1aee2ab0f902
spelling: trivial spell checking
Mads Kiilerich <madski@unity3d.com>
parents:
26221
diff
changeset
|
70 `localmods` are from the Python standard library. |
25173
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
71 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
72 This function returns the function, which takes `name` argument, |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
73 and returns `(absname, dottedpath, hassubmod)` tuple if `name` |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
74 matches against locally defined module. Otherwise, it returns |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
75 False. |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
76 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
77 It is assumed that `name` doesn't have `.__init__`. |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
78 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
79 `absname` is an absolute module name of specified `name` |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
80 (e.g. "hgext.convert"). This can be used to compose prefix for sub |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
81 modules or so. |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
82 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
83 `dottedpath` is a `dotted_name_of_path()`-ed source file path |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
84 (e.g. "hgext.convert.__init__") of `name`. This is used to look |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
85 module up in `localmods` again. |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
86 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
87 `hassubmod` is whether it may have sub modules under it (for |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
88 convenient, even though this is also equivalent to "absname != |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
89 dottednpath") |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
90 |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
91 >>> localmods = {'foo.__init__': True, 'foo.foo1': True, |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
92 ... 'foo.bar.__init__': True, 'foo.bar.bar1': True, |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
93 ... 'baz.__init__': True, 'baz.baz1': True } |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
94 >>> fromlocal = fromlocalfunc('foo.xxx', localmods) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
95 >>> # relative |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
96 >>> fromlocal('foo1') |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
97 ('foo.foo1', 'foo.foo1', False) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
98 >>> fromlocal('bar') |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
99 ('foo.bar', 'foo.bar.__init__', True) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
100 >>> fromlocal('bar.bar1') |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
101 ('foo.bar.bar1', 'foo.bar.bar1', False) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
102 >>> # absolute |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
103 >>> fromlocal('baz') |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
104 ('baz', 'baz.__init__', True) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
105 >>> fromlocal('baz.baz1') |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
106 ('baz.baz1', 'baz.baz1', False) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
107 >>> # unknown = maybe standard library |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
108 >>> fromlocal('os') |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
109 False |
25701
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
110 >>> fromlocal(None, 1) |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
111 ('foo', 'foo.__init__', True) |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
112 >>> fromlocal2 = fromlocalfunc('foo.xxx.yyy', localmods) |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
113 >>> fromlocal2(None, 2) |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
114 ('foo', 'foo.__init__', True) |
25173
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
115 """ |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
116 prefix = '.'.join(modulename.split('.')[:-1]) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
117 if prefix: |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
118 prefix += '.' |
25701
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
119 def fromlocal(name, level=0): |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
120 # name is None when relative imports are used. |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
121 if name is None: |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
122 # If relative imports are used, level must not be absolute. |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
123 assert level > 0 |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
124 candidates = ['.'.join(modulename.split('.')[:-level])] |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
125 else: |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
126 # Check relative name first. |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
127 candidates = [prefix + name, name] |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
128 |
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
129 for n in candidates: |
25173
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
130 if n in localmods: |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
131 return (n, n, False) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
132 dottedpath = n + '.__init__' |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
133 if dottedpath in localmods: |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
134 return (n, dottedpath, True) |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
135 return False |
7358b5d9991e
import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25064
diff
changeset
|
136 return fromlocal |
20036 | 137 |
138 def list_stdlib_modules(): | |
139 """List the modules present in the stdlib. | |
140 | |
141 >>> mods = set(list_stdlib_modules()) | |
142 >>> 'BaseHTTPServer' in mods | |
143 True | |
144 | |
145 os.path isn't really a module, so it's missing: | |
146 | |
147 >>> 'os.path' in mods | |
148 False | |
149 | |
150 sys requires special treatment, because it's baked into the | |
151 interpreter, but it should still appear: | |
152 | |
153 >>> 'sys' in mods | |
154 True | |
155 | |
156 >>> 'collections' in mods | |
157 True | |
158 | |
159 >>> 'cStringIO' in mods | |
160 True | |
161 """ | |
162 for m in sys.builtin_module_names: | |
163 yield m | |
164 # These modules only exist on windows, but we should always | |
165 # consider them stdlib. | |
166 for m in ['msvcrt', '_winreg']: | |
167 yield m | |
168 # These get missed too | |
26062
7154a4a08b96
import-checker: ensure multiprocessing is treated as from stdlib
Augie Fackler <augie@google.com>
parents:
25734
diff
changeset
|
169 for m in 'ctypes', 'email', 'multiprocessing': |
20036 | 170 yield m |
171 yield 'builtins' # python3 only | |
24669
fbdbff1b486a
import-checker: force 'fcntl', 'grp', 'pwd', and 'termios' to stdlib modules
Matt Harbison <matt_harbison@yahoo.com>
parents:
24668
diff
changeset
|
172 for m in 'fcntl', 'grp', 'pwd', 'termios': # Unix only |
fbdbff1b486a
import-checker: force 'fcntl', 'grp', 'pwd', and 'termios' to stdlib modules
Matt Harbison <matt_harbison@yahoo.com>
parents:
24668
diff
changeset
|
173 yield m |
20197
761f2929a6ad
import-checker: refactor sys.path prefix check (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20038
diff
changeset
|
174 stdlib_prefixes = set([sys.prefix, sys.exec_prefix]) |
20198
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
175 # We need to supplement the list of prefixes for the search to work |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
176 # when run from within a virtualenv. |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
177 for mod in (BaseHTTPServer, zlib): |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
178 try: |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
179 # Not all module objects have a __file__ attribute. |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
180 filename = mod.__file__ |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
181 except AttributeError: |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
182 continue |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
183 dirname = os.path.dirname(filename) |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
184 for prefix in stdlib_prefixes: |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
185 if dirname.startswith(prefix): |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
186 # Then this directory is redundant. |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
187 break |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
188 else: |
f5393a9dc4e5
import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents:
20197
diff
changeset
|
189 stdlib_prefixes.add(dirname) |
20036 | 190 for libpath in sys.path: |
20201
bc3b48b0f5c8
import-checker: suppress check-code about any()
Augie Fackler <raf@durin42.com>
parents:
20200
diff
changeset
|
191 # We want to walk everything in sys.path that starts with |
bc3b48b0f5c8
import-checker: suppress check-code about any()
Augie Fackler <raf@durin42.com>
parents:
20200
diff
changeset
|
192 # something in stdlib_prefixes. check-code suppressed because |
bc3b48b0f5c8
import-checker: suppress check-code about any()
Augie Fackler <raf@durin42.com>
parents:
20200
diff
changeset
|
193 # the ast module used by this script implies the availability |
bc3b48b0f5c8
import-checker: suppress check-code about any()
Augie Fackler <raf@durin42.com>
parents:
20200
diff
changeset
|
194 # of any(). |
20238
81e905790b30
check-code: do not skip entire file, skip only one match instead
Simon Heimberg <simohe@besonet.ch>
parents:
20201
diff
changeset
|
195 if not any(libpath.startswith(p) for p in stdlib_prefixes): # no-py24 |
20036 | 196 continue |
197 for top, dirs, files in os.walk(libpath): | |
25733
f99c066f5f9a
import-checker: recurse into subtree of sys.path only if __init__.py exists
Yuya Nishihara <yuya@tcha.org>
parents:
25731
diff
changeset
|
198 for i, d in reversed(list(enumerate(dirs))): |
25734
9086d0c1def3
import-checker: exclude mercurial packages installed into the system path
Yuya Nishihara <yuya@tcha.org>
parents:
25733
diff
changeset
|
199 if (not os.path.exists(os.path.join(top, d, '__init__.py')) |
9086d0c1def3
import-checker: exclude mercurial packages installed into the system path
Yuya Nishihara <yuya@tcha.org>
parents:
25733
diff
changeset
|
200 or top == libpath and d in ('hgext', 'mercurial')): |
25733
f99c066f5f9a
import-checker: recurse into subtree of sys.path only if __init__.py exists
Yuya Nishihara <yuya@tcha.org>
parents:
25731
diff
changeset
|
201 del dirs[i] |
20036 | 202 for name in files: |
203 if name == '__init__.py': | |
204 continue | |
26221
ae65b1b4cb46
import-checker: use modern .endswith for multiple suffixes
Augie Fackler <augie@google.com>
parents:
26166
diff
changeset
|
205 if not name.endswith(('.py', '.so', '.pyc', '.pyo', '.pyd')): |
20036 | 206 continue |
207 full_path = os.path.join(top, name) | |
208 rel_path = full_path[len(libpath) + 1:] | |
209 mod = dotted_name_of_path(rel_path) | |
210 yield mod | |
211 | |
212 stdlib_modules = set(list_stdlib_modules()) | |
213 | |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
214 def imported_modules(source, modulename, localmods, ignore_nested=False): |
20036 | 215 """Given the source of a file as a string, yield the names |
216 imported by that file. | |
217 | |
20037
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
218 Args: |
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
219 source: The python source to examine as a string. |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
220 modulename: of specified python source (may have `__init__`) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
221 localmods: dict of locally defined module names (may have `__init__`) |
20037
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
222 ignore_nested: If true, import statements that do not start in |
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
223 column zero will be ignored. |
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
224 |
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
225 Returns: |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
226 A list of absolute module names imported by the given source. |
20037
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
227 |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
228 >>> modulename = 'foo.xxx' |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
229 >>> localmods = {'foo.__init__': True, |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
230 ... 'foo.foo1': True, 'foo.foo2': True, |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
231 ... 'foo.bar.__init__': True, 'foo.bar.bar1': True, |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
232 ... 'baz.__init__': True, 'baz.baz1': True } |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
233 >>> # standard library (= not locally defined ones) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
234 >>> sorted(imported_modules( |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
235 ... 'from stdlib1 import foo, bar; import stdlib2', |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
236 ... modulename, localmods)) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
237 [] |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
238 >>> # relative importing |
20037
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
239 >>> sorted(imported_modules( |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
240 ... 'import foo1; from bar import bar1', |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
241 ... modulename, localmods)) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
242 ['foo.bar.__init__', 'foo.bar.bar1', 'foo.foo1'] |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
243 >>> sorted(imported_modules( |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
244 ... 'from bar.bar1 import name1, name2, name3', |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
245 ... modulename, localmods)) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
246 ['foo.bar.bar1'] |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
247 >>> # absolute importing |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
248 >>> sorted(imported_modules( |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
249 ... 'from baz import baz1, name1', |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
250 ... modulename, localmods)) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
251 ['baz.__init__', 'baz.baz1'] |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
252 >>> # mixed importing, even though it shouldn't be recommended |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
253 >>> sorted(imported_modules( |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
254 ... 'import stdlib, foo1, baz', |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
255 ... modulename, localmods)) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
256 ['baz.__init__', 'foo.foo1'] |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
257 >>> # ignore_nested |
20037
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
258 >>> sorted(imported_modules( |
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
259 ... '''import foo |
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
260 ... def wat(): |
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
261 ... import bar |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
262 ... ''', modulename, localmods)) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
263 ['foo.__init__', 'foo.bar.__init__'] |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
264 >>> sorted(imported_modules( |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
265 ... '''import foo |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
266 ... def wat(): |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
267 ... import bar |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
268 ... ''', modulename, localmods, ignore_nested=True)) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
269 ['foo.__init__'] |
20036 | 270 """ |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
271 fromlocal = fromlocalfunc(modulename, localmods) |
20036 | 272 for node in ast.walk(ast.parse(source)): |
20037
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
273 if ignore_nested and getattr(node, 'col_offset', 0) > 0: |
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
274 continue |
20036 | 275 if isinstance(node, ast.Import): |
276 for n in node.names: | |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
277 found = fromlocal(n.name) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
278 if not found: |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
279 # this should import standard library |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
280 continue |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
281 yield found[1] |
20036 | 282 elif isinstance(node, ast.ImportFrom): |
25701
1f88c0f6ff5a
import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25660
diff
changeset
|
283 found = fromlocal(node.module, node.level) |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
284 if not found: |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
285 # this should import standard library |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
286 continue |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
287 |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
288 absname, dottedpath, hassubmod = found |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
289 yield dottedpath |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
290 if not hassubmod: |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
291 # examination of "node.names" should be redundant |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
292 # e.g.: from mercurial.node import nullid, nullrev |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
293 continue |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
294 |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
295 prefix = absname + '.' |
20036 | 296 for n in node.names: |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
297 found = fromlocal(prefix + n.name) |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
298 if not found: |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
299 # this should be a function or a property of "node.module" |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
300 continue |
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
301 yield found[1] |
20036 | 302 |
25702
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
303 def verify_import_convention(module, source): |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
304 """Verify imports match our established coding convention. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
305 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
306 We have 2 conventions: legacy and modern. The modern convention is in |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
307 effect when using absolute imports. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
308 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
309 The legacy convention only looks for mixed imports. The modern convention |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
310 is much more thorough. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
311 """ |
25702
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
312 root = ast.parse(source) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
313 absolute = usingabsolute(root) |
25702
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
314 |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
315 if absolute: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
316 return verify_modern_convention(module, root) |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
317 else: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
318 return verify_stdlib_on_own_line(root) |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
319 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
320 def verify_modern_convention(module, root): |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
321 """Verify a file conforms to the modern import convention rules. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
322 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
323 The rules of the modern convention are: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
324 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
325 * Ordering is stdlib followed by local imports. Each group is lexically |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
326 sorted. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
327 * Importing multiple modules via "import X, Y" is not allowed: use |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
328 separate import statements. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
329 * Importing multiple modules via "from X import ..." is allowed if using |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
330 parenthesis and one entry per line. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
331 * Only 1 relative import statement per import level ("from .", "from ..") |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
332 is allowed. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
333 * Relative imports from higher levels must occur before lower levels. e.g. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
334 "from .." must be before "from .". |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
335 * Imports from peer packages should use relative import (e.g. do not |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
336 "import mercurial.foo" from a "mercurial.*" module). |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
337 * Symbols can only be imported from specific modules (see |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
338 `allowsymbolimports`). For other modules, first import the module then |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
339 assign the symbol to a module-level variable. In addition, these imports |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
340 must be performed before other relative imports. This rule only |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
341 applies to import statements outside of any blocks. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
342 * Relative imports from the standard library are not allowed. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
343 * Certain modules must be aliased to alternate names to avoid aliasing |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
344 and readability problems. See `requirealias`. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
345 """ |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
346 topmodule = module.split('.')[0] |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
347 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
348 # Whether a local/non-stdlib import has been performed. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
349 seenlocal = False |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
350 # Whether a relative, non-symbol import has been seen. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
351 seennonsymbolrelative = False |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
352 # The last name to be imported (for sorting). |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
353 lastname = None |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
354 # Relative import levels encountered so far. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
355 seenlevels = set() |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
356 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
357 for node in ast.walk(root): |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
358 def msg(fmt, *args): |
26956
4b56214ebb7a
import-checker: include lineno in warning message
Yuya Nishihara <yuya@tcha.org>
parents:
26955
diff
changeset
|
359 return (fmt % args, node.lineno) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
360 if isinstance(node, ast.Import): |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
361 # Disallow "import foo, bar" and require separate imports |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
362 # for each module. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
363 if len(node.names) > 1: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
364 yield msg('multiple imported names: %s', |
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
365 ', '.join(n.name for n in node.names)) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
366 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
367 name = node.names[0].name |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
368 asname = node.names[0].asname |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
369 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
370 # Ignore sorting rules on imports inside blocks. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
371 if node.col_offset == 0: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
372 if lastname and name < lastname: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
373 yield msg('imports not lexically sorted: %s < %s', |
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
374 name, lastname) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
375 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
376 lastname = name |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
377 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
378 # stdlib imports should be before local imports. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
379 stdlib = name in stdlib_modules |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
380 if stdlib and seenlocal and node.col_offset == 0: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
381 yield msg('stdlib import follows local import: %s', name) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
382 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
383 if not stdlib: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
384 seenlocal = True |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
385 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
386 # Import of sibling modules should use relative imports. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
387 topname = name.split('.')[0] |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
388 if topname == topmodule: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
389 yield msg('import should be relative: %s', name) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
390 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
391 if name in requirealias and asname != requirealias[name]: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
392 yield msg('%s module must be "as" aliased to %s', |
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
393 name, requirealias[name]) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
394 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
395 elif isinstance(node, ast.ImportFrom): |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
396 # Resolve the full imported module name. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
397 if node.level > 0: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
398 fullname = '.'.join(module.split('.')[:-node.level]) |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
399 if node.module: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
400 fullname += '.%s' % node.module |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
401 else: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
402 assert node.module |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
403 fullname = node.module |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
404 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
405 topname = fullname.split('.')[0] |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
406 if topname == topmodule: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
407 yield msg('import should be relative: %s', fullname) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
408 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
409 # __future__ is special since it needs to come first and use |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
410 # symbol import. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
411 if fullname != '__future__': |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
412 if not fullname or fullname in stdlib_modules: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
413 yield msg('relative import of stdlib module') |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
414 else: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
415 seenlocal = True |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
416 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
417 # Direct symbol import is only allowed from certain modules and |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
418 # must occur before non-symbol imports. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
419 if node.module and node.col_offset == 0: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
420 if fullname not in allowsymbolimports: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
421 yield msg('direct symbol import from %s', fullname) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
422 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
423 if seennonsymbolrelative: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
424 yield msg('symbol import follows non-symbol import: %s', |
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
425 fullname) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
426 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
427 if not node.module: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
428 assert node.level |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
429 seennonsymbolrelative = True |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
430 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
431 # Only allow 1 group per level. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
432 if node.level in seenlevels and node.col_offset == 0: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
433 yield msg('multiple "from %s import" statements', |
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
434 '.' * node.level) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
435 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
436 # Higher-level groups come before lower-level groups. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
437 if any(node.level > l for l in seenlevels): |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
438 yield msg('higher-level import should come first: %s', |
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
439 fullname) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
440 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
441 seenlevels.add(node.level) |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
442 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
443 # Entries in "from .X import ( ... )" lists must be lexically |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
444 # sorted. |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
445 lastentryname = None |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
446 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
447 for n in node.names: |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
448 if lastentryname and n.name < lastentryname: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
449 yield msg('imports from %s not lexically sorted: %s < %s', |
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
450 fullname, n.name, lastentryname) |
25703
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
451 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
452 lastentryname = n.name |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
453 |
1a6a117d0b95
import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25702
diff
changeset
|
454 if n.name in requirealias and n.asname != requirealias[n.name]: |
26955
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
455 yield msg('%s from %s must be "as" aliased to %s', |
c4114e335b49
import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents:
26954
diff
changeset
|
456 n.name, fullname, requirealias[n.name]) |
25702
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
457 |
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
458 def verify_stdlib_on_own_line(root): |
20036 | 459 """Given some python source, verify that stdlib imports are done |
460 in separate statements from relative local module imports. | |
461 | |
462 Observing this limitation is important as it works around an | |
463 annoying lib2to3 bug in relative import rewrites: | |
464 http://bugs.python.org/issue19510. | |
465 | |
25702
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
466 >>> list(verify_stdlib_on_own_line(ast.parse('import sys, foo'))) |
26956
4b56214ebb7a
import-checker: include lineno in warning message
Yuya Nishihara <yuya@tcha.org>
parents:
26955
diff
changeset
|
467 [('mixed imports\\n stdlib: sys\\n relative: foo', 1)] |
25702
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
468 >>> list(verify_stdlib_on_own_line(ast.parse('import sys, os'))) |
20036 | 469 [] |
25702
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
470 >>> list(verify_stdlib_on_own_line(ast.parse('import foo, bar'))) |
20036 | 471 [] |
472 """ | |
25702
ab2c5163900e
import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25701
diff
changeset
|
473 for node in ast.walk(root): |
20036 | 474 if isinstance(node, ast.Import): |
20386
a05d31b040d7
import-checker: show stdlib and relative imports separately
Mads Kiilerich <madski@unity3d.com>
parents:
20383
diff
changeset
|
475 from_stdlib = {False: [], True: []} |
20036 | 476 for n in node.names: |
20386
a05d31b040d7
import-checker: show stdlib and relative imports separately
Mads Kiilerich <madski@unity3d.com>
parents:
20383
diff
changeset
|
477 from_stdlib[n.name in stdlib_modules].append(n.name) |
a05d31b040d7
import-checker: show stdlib and relative imports separately
Mads Kiilerich <madski@unity3d.com>
parents:
20383
diff
changeset
|
478 if from_stdlib[True] and from_stdlib[False]: |
a05d31b040d7
import-checker: show stdlib and relative imports separately
Mads Kiilerich <madski@unity3d.com>
parents:
20383
diff
changeset
|
479 yield ('mixed imports\n stdlib: %s\n relative: %s' % |
a05d31b040d7
import-checker: show stdlib and relative imports separately
Mads Kiilerich <madski@unity3d.com>
parents:
20383
diff
changeset
|
480 (', '.join(sorted(from_stdlib[True])), |
26956
4b56214ebb7a
import-checker: include lineno in warning message
Yuya Nishihara <yuya@tcha.org>
parents:
26955
diff
changeset
|
481 ', '.join(sorted(from_stdlib[False]))), node.lineno) |
20036 | 482 |
483 class CircularImport(Exception): | |
484 pass | |
485 | |
24490
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
486 def checkmod(mod, imports): |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
487 shortest = {} |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
488 visit = [[mod]] |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
489 while visit: |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
490 path = visit.pop(0) |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
491 for i in sorted(imports.get(path[-1], [])): |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
492 if len(path) < shortest.get(i, 1000): |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
493 shortest[i] = len(path) |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
494 if i in path: |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
495 if i == path[0]: |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
496 raise CircularImport(path) |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
497 continue |
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
498 visit.append(path + [i]) |
20036 | 499 |
20038
c65a6937b828
import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents:
20037
diff
changeset
|
500 def rotatecycle(cycle): |
c65a6937b828
import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents:
20037
diff
changeset
|
501 """arrange a cycle so that the lexicographically first module listed first |
c65a6937b828
import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents:
20037
diff
changeset
|
502 |
24488
4b3fc46097f7
import-checker: drop duplicate element from cycle
Matt Mackall <mpm@selenic.com>
parents:
24487
diff
changeset
|
503 >>> rotatecycle(['foo', 'bar']) |
20038
c65a6937b828
import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents:
20037
diff
changeset
|
504 ['bar', 'foo', 'bar'] |
c65a6937b828
import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents:
20037
diff
changeset
|
505 """ |
c65a6937b828
import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents:
20037
diff
changeset
|
506 lowest = min(cycle) |
c65a6937b828
import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents:
20037
diff
changeset
|
507 idx = cycle.index(lowest) |
24488
4b3fc46097f7
import-checker: drop duplicate element from cycle
Matt Mackall <mpm@selenic.com>
parents:
24487
diff
changeset
|
508 return cycle[idx:] + cycle[:idx] + [lowest] |
20036 | 509 |
510 def find_cycles(imports): | |
511 """Find cycles in an already-loaded import graph. | |
512 | |
25175
10e6c4b7121b
import-checker: don't treat modules as relative one if not found
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25174
diff
changeset
|
513 All module names recorded in `imports` should be absolute one. |
10e6c4b7121b
import-checker: don't treat modules as relative one if not found
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25174
diff
changeset
|
514 |
10e6c4b7121b
import-checker: don't treat modules as relative one if not found
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25174
diff
changeset
|
515 >>> imports = {'top.foo': ['top.bar', 'os.path', 'top.qux'], |
10e6c4b7121b
import-checker: don't treat modules as relative one if not found
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25174
diff
changeset
|
516 ... 'top.bar': ['top.baz', 'sys'], |
10e6c4b7121b
import-checker: don't treat modules as relative one if not found
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25174
diff
changeset
|
517 ... 'top.baz': ['top.foo'], |
10e6c4b7121b
import-checker: don't treat modules as relative one if not found
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25174
diff
changeset
|
518 ... 'top.qux': ['top.foo']} |
20036 | 519 >>> print '\\n'.join(sorted(find_cycles(imports))) |
24487
642d245ff537
import-checker: fix rotatecycle
Matt Mackall <mpm@selenic.com>
parents:
22975
diff
changeset
|
520 top.bar -> top.baz -> top.foo -> top.bar |
642d245ff537
import-checker: fix rotatecycle
Matt Mackall <mpm@selenic.com>
parents:
22975
diff
changeset
|
521 top.foo -> top.qux -> top.foo |
20036 | 522 """ |
24491
784b278b349c
import-checker: rotatecycle is actually the canonical cycle key
Matt Mackall <mpm@selenic.com>
parents:
24490
diff
changeset
|
523 cycles = set() |
20036 | 524 for mod in sorted(imports.iterkeys()): |
525 try: | |
24490
fb4639d5268e
import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents:
24489
diff
changeset
|
526 checkmod(mod, imports) |
25660
328739ea70c3
global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents:
25175
diff
changeset
|
527 except CircularImport as e: |
20036 | 528 cycle = e.args[0] |
24491
784b278b349c
import-checker: rotatecycle is actually the canonical cycle key
Matt Mackall <mpm@selenic.com>
parents:
24490
diff
changeset
|
529 cycles.add(" -> ".join(rotatecycle(cycle))) |
784b278b349c
import-checker: rotatecycle is actually the canonical cycle key
Matt Mackall <mpm@selenic.com>
parents:
24490
diff
changeset
|
530 return cycles |
20036 | 531 |
532 def _cycle_sortkey(c): | |
533 return len(c), c | |
534 | |
535 def main(argv): | |
25063
723e364488f4
import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
24669
diff
changeset
|
536 if len(argv) < 2 or (argv[1] == '-' and len(argv) > 2): |
723e364488f4
import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
24669
diff
changeset
|
537 print 'Usage: %s {-|file [file] [file] ...}' |
20036 | 538 return 1 |
25063
723e364488f4
import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
24669
diff
changeset
|
539 if argv[1] == '-': |
723e364488f4
import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
24669
diff
changeset
|
540 argv = argv[:1] |
723e364488f4
import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
24669
diff
changeset
|
541 argv.extend(l.rstrip() for l in sys.stdin.readlines()) |
25064
3bbbadf69d0a
import-checker: loop to get list of locally defined modules at first
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25063
diff
changeset
|
542 localmods = {} |
20036 | 543 used_imports = {} |
544 any_errors = False | |
545 for source_path in argv[1:]: | |
25064
3bbbadf69d0a
import-checker: loop to get list of locally defined modules at first
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25063
diff
changeset
|
546 modname = dotted_name_of_path(source_path, trimpure=True) |
3bbbadf69d0a
import-checker: loop to get list of locally defined modules at first
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25063
diff
changeset
|
547 localmods[modname] = source_path |
3bbbadf69d0a
import-checker: loop to get list of locally defined modules at first
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25063
diff
changeset
|
548 for modname, source_path in sorted(localmods.iteritems()): |
20036 | 549 f = open(source_path) |
550 src = f.read() | |
20037
957b43371928
import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents:
20036
diff
changeset
|
551 used_imports[modname] = sorted( |
25174
86298718b01c
import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25173
diff
changeset
|
552 imported_modules(src, modname, localmods, ignore_nested=True)) |
26956
4b56214ebb7a
import-checker: include lineno in warning message
Yuya Nishihara <yuya@tcha.org>
parents:
26955
diff
changeset
|
553 for error, lineno in verify_import_convention(modname, src): |
20036 | 554 any_errors = True |
26956
4b56214ebb7a
import-checker: include lineno in warning message
Yuya Nishihara <yuya@tcha.org>
parents:
26955
diff
changeset
|
555 print '%s:%d: %s' % (source_path, lineno, error) |
20036 | 556 f.close() |
557 cycles = find_cycles(used_imports) | |
558 if cycles: | |
559 firstmods = set() | |
560 for c in sorted(cycles, key=_cycle_sortkey): | |
561 first = c.split()[0] | |
562 # As a rough cut, ignore any cycle that starts with the | |
563 # same module as some other cycle. Otherwise we see lots | |
564 # of cycles that are effectively duplicates. | |
565 if first in firstmods: | |
566 continue | |
567 print 'Import cycle:', c | |
568 firstmods.add(first) | |
569 any_errors = True | |
25731
cd1daab5d036
import-checker.py: exit with code 0 if no error is detected
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
25703
diff
changeset
|
570 return any_errors != 0 |
20036 | 571 |
572 if __name__ == '__main__': | |
573 sys.exit(int(main(sys.argv))) |