summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrett Cannon <bcannon@gmail.com>2009-02-06 02:47:33 (GMT)
committerBrett Cannon <bcannon@gmail.com>2009-02-06 02:47:33 (GMT)
commit7f9876c0daf73b1da3a7bc3ab62add0d1b10706a (patch)
tree65c2e0a352b057d075cb21fc70af5e0b0194a123
parent5c6d7877c58cfe348b2a302d3bd924cd096a7ba2 (diff)
downloadcpython-7f9876c0daf73b1da3a7bc3ab62add0d1b10706a.zip
cpython-7f9876c0daf73b1da3a7bc3ab62add0d1b10706a.tar.gz
cpython-7f9876c0daf73b1da3a7bc3ab62add0d1b10706a.tar.bz2
Initial, untested stab at writing a common denominator function for __import__
and import_module.
-rw-r--r--Lib/importlib/NOTES13
-rw-r--r--Lib/importlib/_bootstrap.py43
2 files changed, 56 insertions, 0 deletions
diff --git a/Lib/importlib/NOTES b/Lib/importlib/NOTES
index 538e874..3b000ff 100644
--- a/Lib/importlib/NOTES
+++ b/Lib/importlib/NOTES
@@ -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.
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 8eff65c..2107e9e 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -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