[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