[PATCH 4 of 6] import-checker: guess what module is imported by list of locally defined ones
FUJIWARA Katsunori
foozy at lares.dti.ne.jp
Wed May 13 11:53:57 CDT 2015
# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1431535750 -32400
# Thu May 14 01:49:10 2015 +0900
# Node ID 9b0ee3a4e1c6ca73bd12c0d7ab16b066c807428c
# Parent be2b8d8ae72918035dbb1ab15bf6c6b3919dd40e
import-checker: guess what module is imported by list of locally defined ones
Before this patch, to guess what module is imported in each modules,
"checkmod()" examines whether imported module is one of standard
Python library or not.
But this causes some problems below:
- cycle via some modules may be overlooked
name of some mercurial specific modules collides against one of
standard library (e.g. commands, parser and formatter), and this
aborts detection for some cycles.
- it is difficult to list up enough module names of standard
library in the portable and robust way
for example, see fbdbff1b486a of Windows environment
To avoid problems above, this patch guesses what module is imported by
list of locally defined (= mercurial specific) ones.
This logic is reasonable, because locally defined module should be
imported via "import xxxx" (without "from yyyy"), even if its name
collides against one of standard library.
It is assumed that all locally defined modules are correctly specified
to "import-checker.py" at once.
diff --git a/contrib/import-checker.py b/contrib/import-checker.py
--- a/contrib/import-checker.py
+++ b/contrib/import-checker.py
@@ -217,14 +217,16 @@ def verify_stdlib_on_own_line(source, mo
class CircularImport(Exception):
pass
-def checkmod(mod, imports):
+def checkmod(mod, imports, localmods):
+ prefix = getprefix(mod)
+ fromlocal = fromlocalfunc(mod, localmods)
shortest = {}
visit = [[mod]]
while visit:
path = visit.pop(0)
for i in sorted(imports.get(path[-1], [])):
- if i not in stdlib_modules and not i.startswith('mercurial.'):
- i = mod.rsplit('.', 1)[0] + '.' + i
+ if fromlocal(i):
+ i = prefix + i
if len(path) < shortest.get(i, 1000):
shortest[i] = len(path)
if i in path:
@@ -243,21 +245,25 @@ def rotatecycle(cycle):
idx = cycle.index(lowest)
return cycle[idx:] + cycle[:idx] + [lowest]
-def find_cycles(imports):
+def find_cycles(imports, localmods):
"""Find cycles in an already-loaded import graph.
+ >>> localmods = {'top.foo': True,
+ ... 'top.bar': True,
+ ... 'top.baz': True,
+ ... 'top.qux': True}
>>> imports = {'top.foo': ['bar', 'os.path', 'qux'],
... 'top.bar': ['baz', 'sys'],
... 'top.baz': ['foo'],
... 'top.qux': ['foo']}
- >>> print '\\n'.join(sorted(find_cycles(imports)))
+ >>> print '\\n'.join(sorted(find_cycles(imports, localmods)))
top.bar -> top.baz -> top.foo -> top.bar
top.foo -> top.qux -> top.foo
"""
cycles = set()
for mod in sorted(imports.iterkeys()):
try:
- checkmod(mod, imports)
+ checkmod(mod, imports, localmods)
except CircularImport, e:
cycle = e.args[0]
cycles.add(" -> ".join(rotatecycle(cycle)))
@@ -288,7 +294,7 @@ def main(argv):
any_errors = True
print source_path, error
f.close()
- cycles = find_cycles(used_imports)
+ cycles = find_cycles(used_imports, localmods)
if cycles:
firstmods = set()
for c in sorted(cycles, key=_cycle_sortkey):
diff --git a/tests/test-module-imports.t b/tests/test-module-imports.t
--- a/tests/test-module-imports.t
+++ b/tests/test-module-imports.t
@@ -21,3 +21,4 @@ these may expose other cycles.
$ hg locate 'mercurial/**.py' | sed 's-\\-/-g' | python "$import_checker" -
Import cycle: mercurial.cmdutil -> mercurial.context -> mercurial.subrepo -> mercurial.cmdutil
+ Import cycle: mercurial.commands -> mercurial.commandserver -> mercurial.dispatch -> mercurial.commands
More information about the Mercurial-devel
mailing list