Show More
@@ -170,38 +170,94 b' def list_stdlib_modules():' | |||||
170 |
|
170 | |||
171 | stdlib_modules = set(list_stdlib_modules()) |
|
171 | stdlib_modules = set(list_stdlib_modules()) | |
172 |
|
172 | |||
173 | def imported_modules(source, ignore_nested=False): |
|
173 | def imported_modules(source, modulename, localmods, ignore_nested=False): | |
174 | """Given the source of a file as a string, yield the names |
|
174 | """Given the source of a file as a string, yield the names | |
175 | imported by that file. |
|
175 | imported by that file. | |
176 |
|
176 | |||
177 | Args: |
|
177 | Args: | |
178 | source: The python source to examine as a string. |
|
178 | source: The python source to examine as a string. | |
|
179 | modulename: of specified python source (may have `__init__`) | |||
|
180 | localmods: dict of locally defined module names (may have `__init__`) | |||
179 | ignore_nested: If true, import statements that do not start in |
|
181 | ignore_nested: If true, import statements that do not start in | |
180 | column zero will be ignored. |
|
182 | column zero will be ignored. | |
181 |
|
183 | |||
182 | Returns: |
|
184 | Returns: | |
183 | A list of module names imported by the given source. |
|
185 | A list of absolute module names imported by the given source. | |
184 |
|
186 | |||
|
187 | >>> modulename = 'foo.xxx' | |||
|
188 | >>> localmods = {'foo.__init__': True, | |||
|
189 | ... 'foo.foo1': True, 'foo.foo2': True, | |||
|
190 | ... 'foo.bar.__init__': True, 'foo.bar.bar1': True, | |||
|
191 | ... 'baz.__init__': True, 'baz.baz1': True } | |||
|
192 | >>> # standard library (= not locally defined ones) | |||
|
193 | >>> sorted(imported_modules( | |||
|
194 | ... 'from stdlib1 import foo, bar; import stdlib2', | |||
|
195 | ... modulename, localmods)) | |||
|
196 | [] | |||
|
197 | >>> # relative importing | |||
185 | >>> sorted(imported_modules( |
|
198 | >>> sorted(imported_modules( | |
186 |
... |
|
199 | ... 'import foo1; from bar import bar1', | |
187 | ['baz.bar', 'foo', 'foo.qux'] |
|
200 | ... modulename, localmods)) | |
|
201 | ['foo.bar.__init__', 'foo.bar.bar1', 'foo.foo1'] | |||
|
202 | >>> sorted(imported_modules( | |||
|
203 | ... 'from bar.bar1 import name1, name2, name3', | |||
|
204 | ... modulename, localmods)) | |||
|
205 | ['foo.bar.bar1'] | |||
|
206 | >>> # absolute importing | |||
|
207 | >>> sorted(imported_modules( | |||
|
208 | ... 'from baz import baz1, name1', | |||
|
209 | ... modulename, localmods)) | |||
|
210 | ['baz.__init__', 'baz.baz1'] | |||
|
211 | >>> # mixed importing, even though it shouldn't be recommended | |||
|
212 | >>> sorted(imported_modules( | |||
|
213 | ... 'import stdlib, foo1, baz', | |||
|
214 | ... modulename, localmods)) | |||
|
215 | ['baz.__init__', 'foo.foo1'] | |||
|
216 | >>> # ignore_nested | |||
188 | >>> sorted(imported_modules( |
|
217 | >>> sorted(imported_modules( | |
189 | ... '''import foo |
|
218 | ... '''import foo | |
190 | ... def wat(): |
|
219 | ... def wat(): | |
191 | ... import bar |
|
220 | ... import bar | |
192 | ... ''', ignore_nested=True)) |
|
221 | ... ''', modulename, localmods)) | |
193 | ['foo'] |
|
222 | ['foo.__init__', 'foo.bar.__init__'] | |
|
223 | >>> sorted(imported_modules( | |||
|
224 | ... '''import foo | |||
|
225 | ... def wat(): | |||
|
226 | ... import bar | |||
|
227 | ... ''', modulename, localmods, ignore_nested=True)) | |||
|
228 | ['foo.__init__'] | |||
194 | """ |
|
229 | """ | |
|
230 | fromlocal = fromlocalfunc(modulename, localmods) | |||
195 | for node in ast.walk(ast.parse(source)): |
|
231 | for node in ast.walk(ast.parse(source)): | |
196 | if ignore_nested and getattr(node, 'col_offset', 0) > 0: |
|
232 | if ignore_nested and getattr(node, 'col_offset', 0) > 0: | |
197 | continue |
|
233 | continue | |
198 | if isinstance(node, ast.Import): |
|
234 | if isinstance(node, ast.Import): | |
199 | for n in node.names: |
|
235 | for n in node.names: | |
200 |
|
|
236 | found = fromlocal(n.name) | |
|
237 | if not found: | |||
|
238 | # this should import standard library | |||
|
239 | continue | |||
|
240 | yield found[1] | |||
201 | elif isinstance(node, ast.ImportFrom): |
|
241 | elif isinstance(node, ast.ImportFrom): | |
202 |
|
|
242 | found = fromlocal(node.module) | |
|
243 | if not found: | |||
|
244 | # this should import standard library | |||
|
245 | continue | |||
|
246 | ||||
|
247 | absname, dottedpath, hassubmod = found | |||
|
248 | yield dottedpath | |||
|
249 | if not hassubmod: | |||
|
250 | # examination of "node.names" should be redundant | |||
|
251 | # e.g.: from mercurial.node import nullid, nullrev | |||
|
252 | continue | |||
|
253 | ||||
|
254 | prefix = absname + '.' | |||
203 | for n in node.names: |
|
255 | for n in node.names: | |
204 |
|
|
256 | found = fromlocal(prefix + n.name) | |
|
257 | if not found: | |||
|
258 | # this should be a function or a property of "node.module" | |||
|
259 | continue | |||
|
260 | yield found[1] | |||
205 |
|
261 | |||
206 | def verify_stdlib_on_own_line(source): |
|
262 | def verify_stdlib_on_own_line(source): | |
207 | """Given some python source, verify that stdlib imports are done |
|
263 | """Given some python source, verify that stdlib imports are done | |
@@ -297,7 +353,7 b' def main(argv):' | |||||
297 | f = open(source_path) |
|
353 | f = open(source_path) | |
298 | src = f.read() |
|
354 | src = f.read() | |
299 | used_imports[modname] = sorted( |
|
355 | used_imports[modname] = sorted( | |
300 | imported_modules(src, ignore_nested=True)) |
|
356 | imported_modules(src, modname, localmods, ignore_nested=True)) | |
301 | for error in verify_stdlib_on_own_line(src): |
|
357 | for error in verify_stdlib_on_own_line(src): | |
302 | any_errors = True |
|
358 | any_errors = True | |
303 | print source_path, error |
|
359 | print source_path, error |
@@ -37,3 +37,4 b' these may expose other cycles.' | |||||
37 | stdlib: formatter |
|
37 | stdlib: formatter | |
38 | relative: config, error, scmutil, util |
|
38 | relative: config, error, scmutil, util | |
39 | Import cycle: mercurial.cmdutil -> mercurial.context -> mercurial.subrepo -> mercurial.cmdutil |
|
39 | Import cycle: mercurial.cmdutil -> mercurial.context -> mercurial.subrepo -> mercurial.cmdutil | |
|
40 | Import cycle: mercurial.commands -> mercurial.commandserver -> mercurial.dispatch -> mercurial.commands |
General Comments 0
You need to be logged in to leave comments.
Login now