def find_loader(fullname):
"""Find a PEP 302 "loader" object for fullname
- If fullname contains dots, path must be the containing package's __path__.
- Returns None if the module cannot be found or imported. This function uses
- iter_importers(), and is thus subject to the same limitations regarding
- platform-specific special import locations such as the Windows registry.
+ This is s convenience wrapper around :func:`importlib.find_loader` that
+ sets the *path* argument correctly when searching for submodules, and
+ also ensures parent packages (if any) are imported before searching for
+ submodules.
"""
- for importer in iter_importers(fullname):
- loader = importer.find_module(fullname)
- if loader is not None:
- return loader
-
- return None
+ if fullname.startswith('.'):
+ msg = "Relative module name {!r} not supported".format(fullname)
+ raise ImportError(msg)
+ path = None
+ pkg_name = fullname.rpartition(".")[0]
+ if pkg_name:
+ pkg = importlib.import_module(pkg_name)
+ path = getattr(pkg, "__path__", None)
+ if path is None:
+ return None
+ try:
+ return importlib.find_loader(fullname, path)
+ except (ImportError, AttributeError, TypeError, ValueError) as ex:
+ # This hack fixes an impedance mismatch between pkgutil and
+ # importlib, where the latter raises other errors for cases where
- # pkgutil previously threw ImportError
++ # pkgutil previously raised ImportError
+ msg = "Error while finding loader for {!r} ({}: {})"
+ raise ImportError(msg.format(fullname, type(ex), ex)) from ex
def extend_path(path, name):