From 7f9876c0daf73b1da3a7bc3ab62add0d1b10706a Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 6 Feb 2009 02:47:33 +0000 Subject: [PATCH] Initial, untested stab at writing a common denominator function for __import__ and import_module. --- Lib/importlib/NOTES | 13 +++++++++++ Lib/importlib/_bootstrap.py | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/Lib/importlib/NOTES b/Lib/importlib/NOTES index 538e8740eff..3b000ff7ed2 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 8eff65c9ee5..2107e9e377e 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -676,6 +676,48 @@ def __exit__(self, exc_type, exc_value, exc_traceback): 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 @@ def __call__(self, name, globals={}, locals={}, fromlist=[], level=0): (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