]> granicus.if.org Git - python/commitdiff
Initial, untested stab at writing a common denominator function for __import__
authorBrett Cannon <bcannon@gmail.com>
Fri, 6 Feb 2009 02:47:33 +0000 (02:47 +0000)
committerBrett Cannon <bcannon@gmail.com>
Fri, 6 Feb 2009 02:47:33 +0000 (02:47 +0000)
and import_module.

Lib/importlib/NOTES
Lib/importlib/_bootstrap.py

index 538e8740eff603921c69388b1de7456e71bbfefb..3b000ff7ed21b92482fcbefcd30e0fc2fd24e8aa 100644 (file)
@@ -15,6 +15,19 @@ to do
 
     + Create a greatest common denominator function for __import__/import_module
       that takes in an absolute module name and performs the import.
+
+      - Needs of __import__
+
+        * Figure out caller's package.
+        * Import module.
+        * Set __package__.
+        * Figure out what module to return.
+
+      - Needs of import_module
+
+        * Resolve name/level.
+        * Import module.
+
     + Use GCD import for __import__.
     + Use GCD import for import_module.
 
index 8eff65c9ee5f7bcdbfe6df7aaa9031309169721d..2107e9e377ebef0fc74e82a1250b92a8ff64ab1d 100644 (file)
@@ -676,6 +676,48 @@ class ImportLockContext(object):
         imp.release_lock()
 
 
+def _gcd_import(name, package=None, level=0):
+    """Import and return the module based on its name, the package the call is
+    being made from, and the level adjustment.
+
+    This function represents the greatest common denominator of functionality
+    between import_module and __import__.
+    """
+    if package and package not in sys.modules:
+        msg = "Parent module {0!r} not loaded, cannot perform relative import"
+        raise SystemError(msg.format(package))
+    dot = len(package)
+    if level > 0:
+        for x in range(level, 1, -1):
+            try:
+                dot = package.rindex('.', 0, dot)
+            except AttributeError:
+                raise ValueError("__package__ not set to a string")
+            except ValueError:
+                raise ValueError("attempted relative import beyond top-level "
+                                 "package")
+        name = "{0}.{1}".format(package[:dot], name)
+    with ImportLockContext():
+        try:
+            return sys.modules[name]
+        except KeyError:
+            pass
+        parent = name.rpartition('.')[0]
+        path = None
+        if parent:
+            if parent not in sys.modules:
+                parent_module = _gcd_import(parent)
+            else:
+                parent_module = sys.modules[parent]
+            path = parent_module.__path__
+        for finder in sys.meta_path + [PathFinder]:
+            loader = finder.find_module(name, path)
+            if loader:  # XXX Worth checking for None explicitly?
+                return loader.load_module(name)
+        else:
+            raise ImportError("No module named {0}".format(name))
+
+
 class Import(object):
 
     """Class that implements the __import__ interface.
@@ -950,6 +992,7 @@ class Import(object):
         (e.g. has a value of 2 for ``from .. import foo``).
 
         """
+        # TODO(brett.cannon) outdated check; just care that level >= 0
         if not name and level < 1:
             raise ValueError("Empty module name")
         is_pkg = True if '__path__' in globals else False