import-checker: allow import of child modules from package root
authorYuya Nishihara <yuya@tcha.org>
Sun, 01 Nov 2015 00:37:22 +0900
changeset 26964 5abba2c92da3
parent 26963 de5ae97ce9f4
child 26965 1fa66d3ad28d
import-checker: allow import of child modules from package root I got the following error by rewriting hgweb/__init__.py to use absolute_import, which is obviously wrong: Import cycle: mercurial.hgweb.__init__ -> mercurial.hgweb.__init__ "from foo import bar" should not make a cycle if "foo" is a package and if "bar" is a module or a package. On the other hand, it should be detected as a cycle if "bar" is a non-module name. Both cases are doc-tested already, so this patch does not add new doctest.
contrib/import-checker.py
tests/test-module-imports.t
--- a/contrib/import-checker.py	Mon Nov 02 17:17:33 2015 -0600
+++ b/contrib/import-checker.py	Sun Nov 01 00:37:22 2015 +0900
@@ -239,7 +239,7 @@
     >>> sorted(imported_modules(
     ...        'import foo1; from bar import bar1',
     ...        modulename, localmods))
-    ['foo.bar.__init__', 'foo.bar.bar1', 'foo.foo1']
+    ['foo.bar.bar1', 'foo.foo1']
     >>> sorted(imported_modules(
     ...        'from bar.bar1 import name1, name2, name3',
     ...        modulename, localmods))
@@ -286,19 +286,26 @@
                 continue
 
             absname, dottedpath, hassubmod = found
-            yield dottedpath
             if not hassubmod:
+                # "dottedpath" is not a package; must be imported
+                yield dottedpath
                 # examination of "node.names" should be redundant
                 # e.g.: from mercurial.node import nullid, nullrev
                 continue
 
+            modnotfound = False
             prefix = absname + '.'
             for n in node.names:
                 found = fromlocal(prefix + n.name)
                 if not found:
                     # this should be a function or a property of "node.module"
+                    modnotfound = True
                     continue
                 yield found[1]
+            if modnotfound:
+                # "dottedpath" is a package, but imported because of non-module
+                # lookup
+                yield dottedpath
 
 def verify_import_convention(module, source):
     """Verify imports match our established coding convention.
--- a/tests/test-module-imports.t	Mon Nov 02 17:17:33 2015 -0600
+++ b/tests/test-module-imports.t	Sun Nov 01 00:37:22 2015 +0900
@@ -68,6 +68,12 @@
   > from .. import parent
   > EOF
 
+  $ touch testpackage/subpackage/foo.py
+  $ cat > testpackage/subpackage/__init__.py << EOF
+  > from __future__ import absolute_import
+  > from . import levelpriority  # should not cause cycle
+  > EOF
+
   $ cat > testpackage/sortedentries.py << EOF
   > from __future__ import absolute_import
   > from . import (