changeset 25701:1f88c0f6ff5a

import-checker: resolve relative imports "from . import X" will produce an ImportFrom ast node with .module = None. This resulted in a run-time error from attempting to concatenate None with a str. Another problem with relative imports is that the prefix may be dynamic based on the "level" attribute of the import. e.g. "from ." has level 1 and "from .." has level 2. We teach the "fromlocal" function how to cope with relative imports. Where appropriate, the consumer passes in the level so relative module names may be resolved properly.
author Gregory Szorc <gregory.szorc@gmail.com>
date Sun, 28 Jun 2015 09:36:58 -0700
parents 0fca47b206f6
children ab2c5163900e
files contrib/import-checker.py
diffstat 1 files changed, 17 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/import-checker.py	Fri Jun 26 23:23:10 2015 -0400
+++ b/contrib/import-checker.py	Sun Jun 28 09:36:58 2015 -0700
@@ -78,13 +78,26 @@
     >>> # unknown = maybe standard library
     >>> fromlocal('os')
     False
+    >>> fromlocal(None, 1)
+    ('foo', 'foo.__init__', True)
+    >>> fromlocal2 = fromlocalfunc('foo.xxx.yyy', localmods)
+    >>> fromlocal2(None, 2)
+    ('foo', 'foo.__init__', True)
     """
     prefix = '.'.join(modulename.split('.')[:-1])
     if prefix:
         prefix += '.'
-    def fromlocal(name):
-        # check relative name at first
-        for n in prefix + name, name:
+    def fromlocal(name, level=0):
+        # name is None when relative imports are used.
+        if name is None:
+            # If relative imports are used, level must not be absolute.
+            assert level > 0
+            candidates = ['.'.join(modulename.split('.')[:-level])]
+        else:
+            # Check relative name first.
+            candidates = [prefix + name, name]
+
+        for n in candidates:
             if n in localmods:
                 return (n, n, False)
             dottedpath = n + '.__init__'
@@ -239,7 +252,7 @@
                     continue
                 yield found[1]
         elif isinstance(node, ast.ImportFrom):
-            found = fromlocal(node.module)
+            found = fromlocal(node.module, node.level)
             if not found:
                 # this should import standard library
                 continue