annotate contrib/import-checker.py @ 32426:06aa645e2372

repoview: move '_getdynamicblock' next to 'hideablerevs' There are the two functions that extensions should use to augment the hidding logic. It seem better to have them together at the top of the file.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Sat, 20 May 2017 19:43:29 +0200
parents 0906b85bf222
children 95085d747db8
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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
28703
a274c4f9087a py3: use print_function in import-checker
timeless <timeless@mozdev.org>
parents: 28702
diff changeset
3 from __future__ import absolute_import, print_function
28702
e44f671018e3 py3: use absolute_import in import-checker
timeless <timeless@mozdev.org>
parents: 28700
diff changeset
4
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
5 import ast
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
6 import collections
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
7 import os
28922
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
8 import re
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
9 import sys
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
10
20198
f5393a9dc4e5 import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents: 20197
diff changeset
11 # 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
12 # 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
13 # so that the return value matches the return value without virtualenv.
29211
b42c2a66a698 py3: make contrib/import-checker.py get along with itself
Yuya Nishihara <yuya@tcha.org>
parents: 29208
diff changeset
14 if True: # disable lexical sorting checks
b42c2a66a698 py3: make contrib/import-checker.py get along with itself
Yuya Nishihara <yuya@tcha.org>
parents: 29208
diff changeset
15 import BaseHTTPServer
b42c2a66a698 py3: make contrib/import-checker.py get along with itself
Yuya Nishihara <yuya@tcha.org>
parents: 29208
diff changeset
16 import zlib
20198
f5393a9dc4e5 import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents: 20197
diff changeset
17
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
18 # 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
19 allowsymbolimports = (
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
20 '__future__',
27018
e5be48dd8215 import-checker: allow symbol imports from hgweb.common and .request
Yuya Nishihara <yuya@tcha.org>
parents: 26965
diff changeset
21 'mercurial.hgweb.common',
e5be48dd8215 import-checker: allow symbol imports from hgweb.common and .request
Yuya Nishihara <yuya@tcha.org>
parents: 26965
diff changeset
22 'mercurial.hgweb.request',
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
23 'mercurial.i18n',
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
24 'mercurial.node',
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
32419
d02888308235 import-checker: add a way to directly import certain symbols
Siddharth Agarwal <sid0@fb.com>
parents: 32374
diff changeset
27 # Whitelist of symbols that can be directly imported.
32420
0906b85bf222 demandimport: move to separate package
Siddharth Agarwal <sid0@fb.com>
parents: 32419
diff changeset
28 directsymbols = (
0906b85bf222 demandimport: move to separate package
Siddharth Agarwal <sid0@fb.com>
parents: 32419
diff changeset
29 'demandimport',
0906b85bf222 demandimport: move to separate package
Siddharth Agarwal <sid0@fb.com>
parents: 32419
diff changeset
30 )
32419
d02888308235 import-checker: add a way to directly import certain symbols
Siddharth Agarwal <sid0@fb.com>
parents: 32374
diff changeset
31
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
32 # 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
33 # 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
34 requirealias = {
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
35 'ui': 'uimod',
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
36 }
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 def usingabsolute(root):
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
39 """Whether absolute imports are being used."""
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
40 if sys.version_info[0] >= 3:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
41 return True
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
42
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
43 for node in ast.walk(root):
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
44 if isinstance(node, ast.ImportFrom):
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
45 if node.module == '__future__':
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
46 for n in node.names:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
47 if n.name == 'absolute_import':
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
48 return True
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
49
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
50 return False
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
51
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
52 def walklocal(root):
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
53 """Recursively yield all descendant nodes but not in a different scope"""
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
54 todo = collections.deque(ast.iter_child_nodes(root))
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
55 yield root, False
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
56 while todo:
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
57 node = todo.popleft()
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
58 newscope = isinstance(node, ast.FunctionDef)
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
59 if not newscope:
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
60 todo.extend(ast.iter_child_nodes(node))
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
61 yield node, newscope
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
62
32374
194b0f781132 import-checker: drop workaround for pure modules
Yuya Nishihara <yuya@tcha.org>
parents: 32372
diff changeset
63 def dotted_name_of_path(path):
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
64 """Given a relative path to a source file, return its dotted module name.
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
65
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
66 >>> dotted_name_of_path('mercurial/error.py')
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
67 'mercurial.error'
20383
4990abb4729d import-checker: fix names of dynamically loaded modules
Mads Kiilerich <madski@unity3d.com>
parents: 20238
diff changeset
68 >>> dotted_name_of_path('zlibmodule.so')
4990abb4729d import-checker: fix names of dynamically loaded modules
Mads Kiilerich <madski@unity3d.com>
parents: 20238
diff changeset
69 'zlib'
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
70 """
27620
0c60843b55b5 import-checker: normalize directory separator to get module name on Windows
Yuya Nishihara <yuya@tcha.org>
parents: 27520
diff changeset
71 parts = path.replace(os.sep, '/').split('/')
20391
466e4c574db0 import-checker: handle standard modules with arch in the filename
Mads Kiilerich <madski@unity3d.com>
parents: 20386
diff changeset
72 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
73 if parts[-1].endswith('module'):
4990abb4729d import-checker: fix names of dynamically loaded modules
Mads Kiilerich <madski@unity3d.com>
parents: 20238
diff changeset
74 parts[-1] = parts[-1][:-6]
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
75 return '.'.join(parts)
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
76
25173
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
77 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
78 """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
79 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
80
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
81 `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
82 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
83
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
84 `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
85 `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
86 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
87
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
88 This function assumes that module names not existing in
26781
1aee2ab0f902 spelling: trivial spell checking
Mads Kiilerich <madski@unity3d.com>
parents: 26221
diff changeset
89 `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
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 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
92 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
93 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
94 False.
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
95
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
96 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
97
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
98 `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
99 (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
100 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
101
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
102 `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
103 (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
104 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
105
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
106 `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
107 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
108 dottednpath")
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
109
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
110 >>> 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
111 ... '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
112 ... '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
113 >>> 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
114 >>> # relative
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
115 >>> 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
116 ('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
117 >>> 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
118 ('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
119 >>> 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
120 ('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
121 >>> # absolute
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
122 >>> 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
123 ('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
124 >>> 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
125 ('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
126 >>> # 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
127 >>> 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
128 False
25701
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
129 >>> fromlocal(None, 1)
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
130 ('foo', 'foo.__init__', True)
29122
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
131 >>> fromlocal('foo1', 1)
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
132 ('foo.foo1', 'foo.foo1', False)
25701
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
133 >>> fromlocal2 = fromlocalfunc('foo.xxx.yyy', localmods)
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
134 >>> fromlocal2(None, 2)
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
135 ('foo', 'foo.__init__', True)
29122
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
136 >>> fromlocal2('bar2', 1)
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
137 False
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
138 >>> fromlocal2('bar', 2)
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
139 ('foo.bar', 'foo.bar.__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
140 """
7358b5d9991e import-checker: add utility to examine what module is imported easily
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25064
diff changeset
141 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
142 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
143 prefix += '.'
25701
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
144 def fromlocal(name, level=0):
29374
7712fcde2d56 import-checker: increase portability for python 2.6.x
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29234
diff changeset
145 # name is false value when relative imports are used.
7712fcde2d56 import-checker: increase portability for python 2.6.x
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29234
diff changeset
146 if not name:
25701
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
147 # 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
148 assert level > 0
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
149 candidates = ['.'.join(modulename.split('.')[:-level])]
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
150 else:
29122
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
151 if not level:
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
152 # Check relative name first.
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
153 candidates = [prefix + name, name]
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
154 else:
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
155 candidates = ['.'.join(modulename.split('.')[:-level]) +
660d8d4ec7aa import-checker: recognize relative imports from parents of current package
liscju <piotr.listkiewicz@gmail.com>
parents: 28922
diff changeset
156 '.' + name]
25701
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
157
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
158 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
159 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
160 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
161 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
162 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
163 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
164 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
165 return fromlocal
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
166
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
167 def list_stdlib_modules():
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
168 """List the modules present in the stdlib.
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
169
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
170 >>> mods = set(list_stdlib_modules())
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
171 >>> 'BaseHTTPServer' in mods
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
172 True
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
173
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
174 os.path isn't really a module, so it's missing:
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
175
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
176 >>> 'os.path' in mods
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
177 False
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
178
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
179 sys requires special treatment, because it's baked into the
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
180 interpreter, but it should still appear:
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
181
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
182 >>> 'sys' in mods
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
183 True
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
184
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
185 >>> 'collections' in mods
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
186 True
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
187
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
188 >>> 'cStringIO' in mods
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
189 True
29395
4c8026babe8c import-checker: ensure cffi is always a system module
Augie Fackler <raf@durin42.com>
parents: 29374
diff changeset
190
4c8026babe8c import-checker: ensure cffi is always a system module
Augie Fackler <raf@durin42.com>
parents: 29374
diff changeset
191 >>> 'cffi' in mods
4c8026babe8c import-checker: ensure cffi is always a system module
Augie Fackler <raf@durin42.com>
parents: 29374
diff changeset
192 True
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
193 """
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
194 for m in sys.builtin_module_names:
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
195 yield m
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
196 # These modules only exist on windows, but we should always
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
197 # consider them stdlib.
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
198 for m in ['msvcrt', '_winreg']:
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
199 yield m
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
200 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
201 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
202 yield m
28713
806d260c6f3b tests: fix builtin module test on pypy
Maciej Fijalkowski <fijall@gmail.com>
parents: 28704
diff changeset
203 for m in 'cPickle', 'datetime': # in Python (not C) on PyPy
806d260c6f3b tests: fix builtin module test on pypy
Maciej Fijalkowski <fijall@gmail.com>
parents: 28704
diff changeset
204 yield m
29395
4c8026babe8c import-checker: ensure cffi is always a system module
Augie Fackler <raf@durin42.com>
parents: 29374
diff changeset
205 for m in ['cffi']:
4c8026babe8c import-checker: ensure cffi is always a system module
Augie Fackler <raf@durin42.com>
parents: 29374
diff changeset
206 yield m
32291
bd872f64a8ba cleanup: use set literals
Martin von Zweigbergk <martinvonz@google.com>
parents: 32212
diff changeset
207 stdlib_prefixes = {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
208 # 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
209 # 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
210 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
211 try:
f5393a9dc4e5 import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents: 20197
diff changeset
212 # 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
213 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
214 except AttributeError:
f5393a9dc4e5 import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents: 20197
diff changeset
215 continue
f5393a9dc4e5 import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents: 20197
diff changeset
216 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
217 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
218 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
219 # 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
220 break
f5393a9dc4e5 import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents: 20197
diff changeset
221 else:
f5393a9dc4e5 import-checker: make test-module-imports.t work using virtualenv (issue4129)
Chris Jerdonek <chris.jerdonek@gmail.com>
parents: 20197
diff changeset
222 stdlib_prefixes.add(dirname)
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
223 for libpath in sys.path:
20201
bc3b48b0f5c8 import-checker: suppress check-code about any()
Augie Fackler <raf@durin42.com>
parents: 20200
diff changeset
224 # We want to walk everything in sys.path that starts with
28700
35ad5bcdeb7e py24: remove check-code py24 notation
timeless <timeless@mozdev.org>
parents: 28400
diff changeset
225 # something in stdlib_prefixes.
35ad5bcdeb7e py24: remove check-code py24 notation
timeless <timeless@mozdev.org>
parents: 28400
diff changeset
226 if not any(libpath.startswith(p) for p in stdlib_prefixes):
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
227 continue
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
228 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
229 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
230 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
231 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
232 del dirs[i]
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
233 for name in files:
26221
ae65b1b4cb46 import-checker: use modern .endswith for multiple suffixes
Augie Fackler <augie@google.com>
parents: 26166
diff changeset
234 if not name.endswith(('.py', '.so', '.pyc', '.pyo', '.pyd')):
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
235 continue
27621
39845b064041 import-checker: list package directory as stdlib module
Yuya Nishihara <yuya@tcha.org>
parents: 27620
diff changeset
236 if name.startswith('__init__.py'):
39845b064041 import-checker: list package directory as stdlib module
Yuya Nishihara <yuya@tcha.org>
parents: 27620
diff changeset
237 full_path = top
39845b064041 import-checker: list package directory as stdlib module
Yuya Nishihara <yuya@tcha.org>
parents: 27620
diff changeset
238 else:
39845b064041 import-checker: list package directory as stdlib module
Yuya Nishihara <yuya@tcha.org>
parents: 27620
diff changeset
239 full_path = os.path.join(top, name)
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
240 rel_path = full_path[len(libpath) + 1:]
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
241 mod = dotted_name_of_path(rel_path)
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
242 yield mod
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
243
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
244 stdlib_modules = set(list_stdlib_modules())
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
245
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
246 def imported_modules(source, modulename, f, localmods, ignore_nested=False):
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
247 """Given the source of a file as a string, yield the names
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
248 imported by that file.
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
249
20037
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
250 Args:
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
251 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
252 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
253 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
254 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
255 column zero will be ignored.
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
256
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
257 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
258 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
259
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
260 >>> f = 'foo/xxx.py'
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
261 >>> 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
262 >>> 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
263 ... '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
264 ... '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
265 ... '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
266 >>> # 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
267 >>> 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
268 ... 'from stdlib1 import foo, bar; import stdlib2',
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
269 ... modulename, f, localmods))
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
270 []
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
271 >>> # relative importing
20037
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
272 >>> 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
273 ... 'import foo1; from bar import bar1',
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
274 ... modulename, f, localmods))
26964
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
275 ['foo.bar.bar1', 'foo.foo1']
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
276 >>> 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
277 ... 'from bar.bar1 import name1, name2, name3',
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
278 ... modulename, f, localmods))
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
279 ['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
280 >>> # 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
281 >>> 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
282 ... 'from baz import baz1, name1',
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
283 ... modulename, f, localmods))
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 ['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
285 >>> # 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
286 >>> 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
287 ... 'import stdlib, foo1, baz',
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
288 ... modulename, f, localmods))
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
289 ['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
290 >>> # ignore_nested
20037
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
291 >>> sorted(imported_modules(
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
292 ... '''import foo
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
293 ... def wat():
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
294 ... import bar
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
295 ... ''', modulename, f, localmods))
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
296 ['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
297 >>> 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
298 ... '''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
299 ... 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
300 ... import bar
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
301 ... ''', modulename, f, localmods, ignore_nested=True))
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
302 ['foo.__init__']
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
303 """
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
304 fromlocal = fromlocalfunc(modulename, localmods)
28921
02ee31a50002 import-checker: track filenames for SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28920
diff changeset
305 for node in ast.walk(ast.parse(source, f)):
20037
957b43371928 import-checker: ignore nested imports
Augie Fackler <raf@durin42.com>
parents: 20036
diff changeset
306 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
307 continue
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
308 if isinstance(node, ast.Import):
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
309 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
310 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
311 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
312 # 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
313 continue
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
314 yield found[1]
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
315 elif isinstance(node, ast.ImportFrom):
25701
1f88c0f6ff5a import-checker: resolve relative imports
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
316 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
317 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
318 # 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
319 continue
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
320
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
321 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
322 if not hassubmod:
26964
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
323 # "dottedpath" is not a package; must be imported
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
324 yield dottedpath
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
325 # 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
326 # 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
327 continue
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
328
26964
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
329 modnotfound = False
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
330 prefix = absname + '.'
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
331 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
332 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
333 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
334 # this should be a function or a property of "node.module"
26964
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
335 modnotfound = True
25174
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
336 continue
86298718b01c import-checker: make imported_modules yield absolute dotted_name_of_path
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 25173
diff changeset
337 yield found[1]
26964
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
338 if modnotfound:
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
339 # "dottedpath" is a package, but imported because of non-module
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
340 # lookup
5abba2c92da3 import-checker: allow import of child modules from package root
Yuya Nishihara <yuya@tcha.org>
parents: 26956
diff changeset
341 yield dottedpath
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
342
27272
69308357ecd1 import-checker: allow absolute imports of sub modules from local packages
Yuya Nishihara <yuya@tcha.org>
parents: 27018
diff changeset
343 def verify_import_convention(module, source, localmods):
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
344 """Verify imports match our established coding convention.
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 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
347 effect when using absolute imports.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
348
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
349 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
350 is much more thorough.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
351 """
25702
ab2c5163900e import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25701
diff changeset
352 root = ast.parse(source)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
353 absolute = usingabsolute(root)
25702
ab2c5163900e import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25701
diff changeset
354
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
355 if absolute:
27272
69308357ecd1 import-checker: allow absolute imports of sub modules from local packages
Yuya Nishihara <yuya@tcha.org>
parents: 27018
diff changeset
356 return verify_modern_convention(module, root, localmods)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
357 else:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
358 return verify_stdlib_on_own_line(root)
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
359
27272
69308357ecd1 import-checker: allow absolute imports of sub modules from local packages
Yuya Nishihara <yuya@tcha.org>
parents: 27018
diff changeset
360 def verify_modern_convention(module, root, localmods, root_col_offset=0):
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
361 """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
362
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
363 The rules of the modern convention are:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
364
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
365 * 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
366 sorted.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
367 * 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
368 separate import statements.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
369 * 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
370 parenthesis and one entry per line.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
371 * 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
372 is allowed.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
373 * 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
374 "from .." must be before "from .".
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
375 * 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
376 "import mercurial.foo" from a "mercurial.*" module).
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
377 * 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
378 `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
379 assign the symbol to a module-level variable. In addition, these imports
29208
cba8bc11ed10 import-checker: extend check of symbol-import order to all local modules
Yuya Nishihara <yuya@tcha.org>
parents: 29207
diff changeset
380 must be performed before other local imports. This rule only
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
381 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
382 * 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
383 * 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
384 and readability problems. See `requirealias`.
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 topmodule = module.split('.')[0]
27272
69308357ecd1 import-checker: allow absolute imports of sub modules from local packages
Yuya Nishihara <yuya@tcha.org>
parents: 27018
diff changeset
387 fromlocal = fromlocalfunc(module, localmods)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
388
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
389 # Whether a local/non-stdlib import has been performed.
28330
f3fb24e36d61 import-checker: report local with stdlib late warning
timeless <timeless@mozdev.org>
parents: 27621
diff changeset
390 seenlocal = None
29208
cba8bc11ed10 import-checker: extend check of symbol-import order to all local modules
Yuya Nishihara <yuya@tcha.org>
parents: 29207
diff changeset
391 # Whether a local/non-stdlib, non-symbol import has been seen.
cba8bc11ed10 import-checker: extend check of symbol-import order to all local modules
Yuya Nishihara <yuya@tcha.org>
parents: 29207
diff changeset
392 seennonsymbollocal = False
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
393 # 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
394 lastname = None
30590
74eecb93c617 import-checker: do not enforce lexical sort accross stdlib/local boundary
Pierre-Yves David <pierre-yves.david@ens-lyon.org>
parents: 29395
diff changeset
395 laststdlib = None
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
396 # Relative import levels encountered so far.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
397 seenlevels = set()
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
398
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
399 for node, newscope in walklocal(root):
26955
c4114e335b49 import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents: 26954
diff changeset
400 def msg(fmt, *args):
26956
4b56214ebb7a import-checker: include lineno in warning message
Yuya Nishihara <yuya@tcha.org>
parents: 26955
diff changeset
401 return (fmt % args, node.lineno)
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
402 if newscope:
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
403 # Check for local imports in function
27272
69308357ecd1 import-checker: allow absolute imports of sub modules from local packages
Yuya Nishihara <yuya@tcha.org>
parents: 27018
diff changeset
404 for r in verify_modern_convention(module, node, localmods,
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
405 node.col_offset + 4):
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
406 yield r
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
407 elif isinstance(node, ast.Import):
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
408 # 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
409 # for each module.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
410 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
411 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
412 ', '.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
413
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
414 name = node.names[0].name
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
415 asname = node.names[0].asname
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
416
30590
74eecb93c617 import-checker: do not enforce lexical sort accross stdlib/local boundary
Pierre-Yves David <pierre-yves.david@ens-lyon.org>
parents: 29395
diff changeset
417 stdlib = name in stdlib_modules
74eecb93c617 import-checker: do not enforce lexical sort accross stdlib/local boundary
Pierre-Yves David <pierre-yves.david@ens-lyon.org>
parents: 29395
diff changeset
418
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
419 # Ignore sorting rules on imports inside blocks.
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
420 if node.col_offset == root_col_offset:
30590
74eecb93c617 import-checker: do not enforce lexical sort accross stdlib/local boundary
Pierre-Yves David <pierre-yves.david@ens-lyon.org>
parents: 29395
diff changeset
421 if lastname and name < lastname and laststdlib == stdlib:
26955
c4114e335b49 import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents: 26954
diff changeset
422 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
423 name, lastname)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
424
30590
74eecb93c617 import-checker: do not enforce lexical sort accross stdlib/local boundary
Pierre-Yves David <pierre-yves.david@ens-lyon.org>
parents: 29395
diff changeset
425 lastname = name
74eecb93c617 import-checker: do not enforce lexical sort accross stdlib/local boundary
Pierre-Yves David <pierre-yves.david@ens-lyon.org>
parents: 29395
diff changeset
426 laststdlib = stdlib
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
427
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
428 # stdlib imports should be before local imports.
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
429 if stdlib and seenlocal and node.col_offset == root_col_offset:
28330
f3fb24e36d61 import-checker: report local with stdlib late warning
timeless <timeless@mozdev.org>
parents: 27621
diff changeset
430 yield msg('stdlib import "%s" follows local import: %s',
f3fb24e36d61 import-checker: report local with stdlib late warning
timeless <timeless@mozdev.org>
parents: 27621
diff changeset
431 name, seenlocal)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
432
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
433 if not stdlib:
28330
f3fb24e36d61 import-checker: report local with stdlib late warning
timeless <timeless@mozdev.org>
parents: 27621
diff changeset
434 seenlocal = name
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 # 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
437 topname = name.split('.')[0]
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
438 if topname == topmodule:
26955
c4114e335b49 import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents: 26954
diff changeset
439 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
440
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
441 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
442 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
443 name, requirealias[name])
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
444
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
445 elif isinstance(node, ast.ImportFrom):
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
446 # Resolve the full imported module name.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
447 if node.level > 0:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
448 fullname = '.'.join(module.split('.')[:-node.level])
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
449 if node.module:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
450 fullname += '.%s' % node.module
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
451 else:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
452 assert node.module
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
453 fullname = node.module
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
454
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
455 topname = fullname.split('.')[0]
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
456 if topname == topmodule:
26955
c4114e335b49 import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents: 26954
diff changeset
457 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
458
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
459 # __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
460 # symbol import.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
461 if fullname != '__future__':
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
462 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
463 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
464 else:
28330
f3fb24e36d61 import-checker: report local with stdlib late warning
timeless <timeless@mozdev.org>
parents: 27621
diff changeset
465 seenlocal = fullname
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
466
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
467 # 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
468 # must occur before non-symbol imports.
29207
a09098c61fea import-checker: always build a list of imported symbols
Yuya Nishihara <yuya@tcha.org>
parents: 29122
diff changeset
469 found = fromlocal(node.module, node.level)
a09098c61fea import-checker: always build a list of imported symbols
Yuya Nishihara <yuya@tcha.org>
parents: 29122
diff changeset
470 if found and found[2]: # node.module is a package
a09098c61fea import-checker: always build a list of imported symbols
Yuya Nishihara <yuya@tcha.org>
parents: 29122
diff changeset
471 prefix = found[0] + '.'
32419
d02888308235 import-checker: add a way to directly import certain symbols
Siddharth Agarwal <sid0@fb.com>
parents: 32374
diff changeset
472 symbols = (n.name for n in node.names
d02888308235 import-checker: add a way to directly import certain symbols
Siddharth Agarwal <sid0@fb.com>
parents: 32374
diff changeset
473 if not fromlocal(prefix + n.name))
29207
a09098c61fea import-checker: always build a list of imported symbols
Yuya Nishihara <yuya@tcha.org>
parents: 29122
diff changeset
474 else:
32419
d02888308235 import-checker: add a way to directly import certain symbols
Siddharth Agarwal <sid0@fb.com>
parents: 32374
diff changeset
475 symbols = (n.name for n in node.names)
d02888308235 import-checker: add a way to directly import certain symbols
Siddharth Agarwal <sid0@fb.com>
parents: 32374
diff changeset
476 symbols = [sym for sym in symbols if sym not in directsymbols]
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
477 if node.module and node.col_offset == root_col_offset:
27272
69308357ecd1 import-checker: allow absolute imports of sub modules from local packages
Yuya Nishihara <yuya@tcha.org>
parents: 27018
diff changeset
478 if symbols and fullname not in allowsymbolimports:
27273
5d5b98346fc2 import-checker: tell which symbol causes "direct symbol import"
Yuya Nishihara <yuya@tcha.org>
parents: 27272
diff changeset
479 yield msg('direct symbol import %s from %s',
5d5b98346fc2 import-checker: tell which symbol causes "direct symbol import"
Yuya Nishihara <yuya@tcha.org>
parents: 27272
diff changeset
480 ', '.join(symbols), fullname)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
481
29208
cba8bc11ed10 import-checker: extend check of symbol-import order to all local modules
Yuya Nishihara <yuya@tcha.org>
parents: 29207
diff changeset
482 if symbols and seennonsymbollocal:
26955
c4114e335b49 import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents: 26954
diff changeset
483 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
484 fullname)
29208
cba8bc11ed10 import-checker: extend check of symbol-import order to all local modules
Yuya Nishihara <yuya@tcha.org>
parents: 29207
diff changeset
485 if not symbols and fullname not in stdlib_modules:
cba8bc11ed10 import-checker: extend check of symbol-import order to all local modules
Yuya Nishihara <yuya@tcha.org>
parents: 29207
diff changeset
486 seennonsymbollocal = True
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
487
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
488 if not node.module:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
489 assert node.level
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
490
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
491 # Only allow 1 group per level.
26965
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
492 if (node.level in seenlevels
1fa66d3ad28d import-checker: reset context to verify convention in function scope
Yuya Nishihara <yuya@tcha.org>
parents: 26964
diff changeset
493 and node.col_offset == root_col_offset):
26955
c4114e335b49 import-checker: extract function to generate a formatted warning
Yuya Nishihara <yuya@tcha.org>
parents: 26954
diff changeset
494 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
495 '.' * node.level)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
496
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
497 # 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
498 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
499 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
500 fullname)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
501
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
502 seenlevels.add(node.level)
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
503
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
504 # 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
505 # sorted.
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
506 lastentryname = None
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
507
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
508 for n in node.names:
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
509 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
510 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
511 fullname, n.name, lastentryname)
25703
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
512
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
513 lastentryname = n.name
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
514
1a6a117d0b95 import-checker: establish modern import convention
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25702
diff changeset
515 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
516 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
517 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
518
ab2c5163900e import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25701
diff changeset
519 def verify_stdlib_on_own_line(root):
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
520 """Given some python source, verify that stdlib imports are done
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
521 in separate statements from relative local module imports.
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
522
25702
ab2c5163900e import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25701
diff changeset
523 >>> 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
524 [('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
525 >>> list(verify_stdlib_on_own_line(ast.parse('import sys, os')))
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
526 []
25702
ab2c5163900e import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25701
diff changeset
527 >>> list(verify_stdlib_on_own_line(ast.parse('import foo, bar')))
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
528 []
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
529 """
25702
ab2c5163900e import-checker: establish new function for verifying import conventions
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25701
diff changeset
530 for node in ast.walk(root):
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
531 if isinstance(node, ast.Import):
20386
a05d31b040d7 import-checker: show stdlib and relative imports separately
Mads Kiilerich <madski@unity3d.com>
parents: 20383
diff changeset
532 from_stdlib = {False: [], True: []}
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
533 for n in node.names:
20386
a05d31b040d7 import-checker: show stdlib and relative imports separately
Mads Kiilerich <madski@unity3d.com>
parents: 20383
diff changeset
534 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
535 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
536 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
537 (', '.join(sorted(from_stdlib[True])),
26956
4b56214ebb7a import-checker: include lineno in warning message
Yuya Nishihara <yuya@tcha.org>
parents: 26955
diff changeset
538 ', '.join(sorted(from_stdlib[False]))), node.lineno)
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
539
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
540 class CircularImport(Exception):
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
541 pass
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
542
24490
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
543 def checkmod(mod, imports):
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
544 shortest = {}
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
545 visit = [[mod]]
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
546 while visit:
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
547 path = visit.pop(0)
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
548 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
549 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
550 shortest[i] = len(path)
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
551 if i in path:
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
552 if i == path[0]:
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
553 raise CircularImport(path)
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
554 continue
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
555 visit.append(path + [i])
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
556
20038
c65a6937b828 import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents: 20037
diff changeset
557 def rotatecycle(cycle):
c65a6937b828 import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents: 20037
diff changeset
558 """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
559
24488
4b3fc46097f7 import-checker: drop duplicate element from cycle
Matt Mackall <mpm@selenic.com>
parents: 24487
diff changeset
560 >>> rotatecycle(['foo', 'bar'])
20038
c65a6937b828 import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents: 20037
diff changeset
561 ['bar', 'foo', 'bar']
c65a6937b828 import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents: 20037
diff changeset
562 """
c65a6937b828 import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents: 20037
diff changeset
563 lowest = min(cycle)
c65a6937b828 import-checker: try a little harder to show fewer cycles
Augie Fackler <raf@durin42.com>
parents: 20037
diff changeset
564 idx = cycle.index(lowest)
24488
4b3fc46097f7 import-checker: drop duplicate element from cycle
Matt Mackall <mpm@selenic.com>
parents: 24487
diff changeset
565 return cycle[idx:] + cycle[:idx] + [lowest]
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
566
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
567 def find_cycles(imports):
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
568 """Find cycles in an already-loaded import graph.
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
569
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
570 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
571
28703
a274c4f9087a py3: use print_function in import-checker
timeless <timeless@mozdev.org>
parents: 28702
diff changeset
572 >>> from __future__ import print_function
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
573 >>> 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
574 ... '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
575 ... '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
576 ... 'top.qux': ['top.foo']}
28703
a274c4f9087a py3: use print_function in import-checker
timeless <timeless@mozdev.org>
parents: 28702
diff changeset
577 >>> print('\\n'.join(sorted(find_cycles(imports))))
24487
642d245ff537 import-checker: fix rotatecycle
Matt Mackall <mpm@selenic.com>
parents: 22975
diff changeset
578 top.bar -> top.baz -> top.foo -> top.bar
642d245ff537 import-checker: fix rotatecycle
Matt Mackall <mpm@selenic.com>
parents: 22975
diff changeset
579 top.foo -> top.qux -> top.foo
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
580 """
24491
784b278b349c import-checker: rotatecycle is actually the canonical cycle key
Matt Mackall <mpm@selenic.com>
parents: 24490
diff changeset
581 cycles = set()
28704
1fa6fdb72275 py3: handle iter/iterkeys+iteritems python3 divergence in import-checker
timeless <timeless@mozdev.org>
parents: 28703
diff changeset
582 for mod in sorted(imports.keys()):
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
583 try:
24490
fb4639d5268e import-checker: make search algorithm non-recursive breadth-first
Matt Mackall <mpm@selenic.com>
parents: 24489
diff changeset
584 checkmod(mod, imports)
25660
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25175
diff changeset
585 except CircularImport as e:
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
586 cycle = e.args[0]
24491
784b278b349c import-checker: rotatecycle is actually the canonical cycle key
Matt Mackall <mpm@selenic.com>
parents: 24490
diff changeset
587 cycles.add(" -> ".join(rotatecycle(cycle)))
784b278b349c import-checker: rotatecycle is actually the canonical cycle key
Matt Mackall <mpm@selenic.com>
parents: 24490
diff changeset
588 return cycles
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
589
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
590 def _cycle_sortkey(c):
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
591 return len(c), c
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
592
28922
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
593 def embedded(f, modname, src):
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
594 """Extract embedded python code
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
595
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
596 >>> def test(fn, lines):
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
597 ... for s, m, f, l in embedded(fn, "example", lines):
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
598 ... print("%s %s %s" % (m, f, l))
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
599 ... print(repr(s))
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
600 >>> lines = [
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
601 ... 'comment',
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
602 ... ' >>> from __future__ import print_function',
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
603 ... " >>> ' multiline",
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
604 ... " ... string'",
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
605 ... ' ',
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
606 ... 'comment',
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
607 ... ' $ cat > foo.py <<EOF',
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
608 ... ' > from __future__ import print_function',
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
609 ... ' > EOF',
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
610 ... ]
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
611 >>> test("example.t", lines)
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
612 example[2] doctest.py 2
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
613 "from __future__ import print_function\\n' multiline\\nstring'\\n"
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
614 example[7] foo.py 7
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
615 'from __future__ import print_function\\n'
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
616 """
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
617 inlinepython = 0
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
618 shpython = 0
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
619 script = []
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
620 prefix = 6
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
621 t = ''
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
622 n = 0
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
623 for l in src:
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
624 n += 1
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
625 if not l.endswith(b'\n'):
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
626 l += b'\n'
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
627 if l.startswith(b' >>> '): # python inlines
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
628 if shpython:
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
629 print("%s:%d: Parse Error" % (f, n))
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
630 if not inlinepython:
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
631 # We've just entered a Python block.
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
632 inlinepython = n
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
633 t = 'doctest.py'
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
634 script.append(l[prefix:])
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
635 continue
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
636 if l.startswith(b' ... '): # python inlines
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
637 script.append(l[prefix:])
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
638 continue
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
639 cat = re.search(r"\$ \s*cat\s*>\s*(\S+\.py)\s*<<\s*EOF", l)
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
640 if cat:
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
641 if inlinepython:
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
642 yield ''.join(script), ("%s[%d]" %
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
643 (modname, inlinepython)), t, inlinepython
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
644 script = []
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
645 inlinepython = 0
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
646 shpython = n
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
647 t = cat.group(1)
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
648 continue
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
649 if shpython and l.startswith(b' > '): # sh continuation
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
650 if l == b' > EOF\n':
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
651 yield ''.join(script), ("%s[%d]" %
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
652 (modname, shpython)), t, shpython
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
653 script = []
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
654 shpython = 0
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
655 else:
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
656 script.append(l[4:])
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
657 continue
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
658 if inlinepython and l == b' \n':
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
659 yield ''.join(script), ("%s[%d]" %
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
660 (modname, inlinepython)), t, inlinepython
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
661 script = []
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
662 inlinepython = 0
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
663 continue
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
664
28919
a94f34306bb9 import-checker: refactor source reading
timeless <timeless@mozdev.org>
parents: 28713
diff changeset
665 def sources(f, modname):
28922
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
666 """Yields possibly multiple sources from a filepath
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
667
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
668 input: filepath, modulename
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
669 yields: script(string), modulename, filepath, linenumber
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
670
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
671 For embedded scripts, the modulename and filepath will be different
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
672 from the function arguments. linenumber is an offset relative to
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
673 the input file.
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
674 """
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
675 py = False
29234
393aef802535 tests: enable import checker for all python files (including no .py files)
Yuya Nishihara <yuya@tcha.org>
parents: 29211
diff changeset
676 if not f.endswith('.t'):
28919
a94f34306bb9 import-checker: refactor source reading
timeless <timeless@mozdev.org>
parents: 28713
diff changeset
677 with open(f) as src:
28922
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
678 yield src.read(), modname, f, 0
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
679 py = True
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
680 if py or f.endswith('.t'):
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
681 with open(f) as src:
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
682 for script, modname, t, line in embedded(f, modname, src):
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
683 yield script, modname, t, line
28919
a94f34306bb9 import-checker: refactor source reading
timeless <timeless@mozdev.org>
parents: 28713
diff changeset
684
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
685 def main(argv):
25063
723e364488f4 import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 24669
diff changeset
686 if len(argv) < 2 or (argv[1] == '-' and len(argv) > 2):
28703
a274c4f9087a py3: use print_function in import-checker
timeless <timeless@mozdev.org>
parents: 28702
diff changeset
687 print('Usage: %s {-|file [file] [file] ...}')
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
688 return 1
25063
723e364488f4 import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 24669
diff changeset
689 if argv[1] == '-':
723e364488f4 import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 24669
diff changeset
690 argv = argv[:1]
723e364488f4 import-checker: add xargs like mode
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 24669
diff changeset
691 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
692 localmods = {}
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
693 used_imports = {}
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
694 any_errors = False
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
695 for source_path in argv[1:]:
32374
194b0f781132 import-checker: drop workaround for pure modules
Yuya Nishihara <yuya@tcha.org>
parents: 32372
diff changeset
696 modname = dotted_name_of_path(source_path)
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
697 localmods[modname] = source_path
28919
a94f34306bb9 import-checker: refactor source reading
timeless <timeless@mozdev.org>
parents: 28713
diff changeset
698 for localmodname, source_path in sorted(localmods.items()):
28922
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
699 for src, modname, name, line in sources(source_path, localmodname):
28920
cdf331b54eb8 import-checker: track SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28919
diff changeset
700 try:
cdf331b54eb8 import-checker: track SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28919
diff changeset
701 used_imports[modname] = sorted(
28922
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
702 imported_modules(src, modname, name, localmods,
28920
cdf331b54eb8 import-checker: track SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28919
diff changeset
703 ignore_nested=True))
cdf331b54eb8 import-checker: track SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28919
diff changeset
704 for error, lineno in verify_import_convention(modname, src,
cdf331b54eb8 import-checker: track SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28919
diff changeset
705 localmods):
cdf331b54eb8 import-checker: track SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28919
diff changeset
706 any_errors = True
28922
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
707 print('%s:%d: %s' % (source_path, lineno + line, error))
28920
cdf331b54eb8 import-checker: track SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28919
diff changeset
708 except SyntaxError as e:
cdf331b54eb8 import-checker: track SyntaxErrors
timeless <timeless@mozdev.org>
parents: 28919
diff changeset
709 print('%s:%d: SyntaxError: %s' %
28922
4ec62a084e5c import-checker: parse python code from .t files
timeless <timeless@mozdev.org>
parents: 28921
diff changeset
710 (source_path, e.lineno + line, e))
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
711 cycles = find_cycles(used_imports)
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
712 if cycles:
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
713 firstmods = set()
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
714 for c in sorted(cycles, key=_cycle_sortkey):
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
715 first = c.split()[0]
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
716 # As a rough cut, ignore any cycle that starts with the
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
717 # same module as some other cycle. Otherwise we see lots
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
718 # of cycles that are effectively duplicates.
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
719 if first in firstmods:
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
720 continue
28703
a274c4f9087a py3: use print_function in import-checker
timeless <timeless@mozdev.org>
parents: 28702
diff changeset
721 print('Import cycle:', c)
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
722 firstmods.add(first)
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
723 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
724 return any_errors != 0
20036
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
725
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
726 if __name__ == '__main__':
e5d51fa51aba contrib: add an import checker
Augie Fackler <raf@durin42.com>
parents:
diff changeset
727 sys.exit(int(main(sys.argv)))