mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
Take the first step in resolving the messy pkgutil vs importlib edge cases by basing pkgutil explicitly on importlib, deprecating its internal import emulation and setting __main__.__loader__ correctly so that runpy still works (Affects #15343, #15314, #15357)
This commit is contained in:
parent
f96cf911a0
commit
85e729ec3b
7 changed files with 260 additions and 142 deletions
|
@ -3,7 +3,9 @@
|
|||
import os
|
||||
import sys
|
||||
import imp
|
||||
import importlib
|
||||
import os.path
|
||||
from warnings import warn
|
||||
from types import ModuleType
|
||||
|
||||
__all__ = [
|
||||
|
@ -168,6 +170,8 @@ class ImpImporter:
|
|||
"""
|
||||
|
||||
def __init__(self, path=None):
|
||||
warn("This emulation is deprecated, use 'importlib' instead",
|
||||
DeprecationWarning)
|
||||
self.path = path
|
||||
|
||||
def find_module(self, fullname, path=None):
|
||||
|
@ -232,6 +236,8 @@ class ImpLoader:
|
|||
code = source = None
|
||||
|
||||
def __init__(self, fullname, file, filename, etc):
|
||||
warn("This emulation is deprecated, use 'importlib' instead",
|
||||
DeprecationWarning)
|
||||
self.file = file
|
||||
self.filename = filename
|
||||
self.fullname = fullname
|
||||
|
@ -366,10 +372,6 @@ def get_importer(path_item):
|
|||
The returned importer is cached in sys.path_importer_cache
|
||||
if it was newly created by a path hook.
|
||||
|
||||
If there is no importer, a wrapper around the basic import
|
||||
machinery is returned. This wrapper is never inserted into
|
||||
the importer cache (None is inserted instead).
|
||||
|
||||
The cache (or part of it) can be cleared manually if a
|
||||
rescan of sys.path_hooks is necessary.
|
||||
"""
|
||||
|
@ -384,10 +386,7 @@ def get_importer(path_item):
|
|||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
importer = ImpImporter(path_item)
|
||||
except ImportError:
|
||||
importer = None
|
||||
importer = None
|
||||
return importer
|
||||
|
||||
|
||||
|
@ -395,55 +394,37 @@ def iter_importers(fullname=""):
|
|||
"""Yield PEP 302 importers for the given module name
|
||||
|
||||
If fullname contains a '.', the importers will be for the package
|
||||
containing fullname, otherwise they will be importers for sys.meta_path,
|
||||
sys.path, and Python's "classic" import machinery, in that order. If
|
||||
the named module is in a package, that package is imported as a side
|
||||
containing fullname, otherwise they will be all registered top level
|
||||
importers (i.e. those on both sys.meta_path and sys.path_hooks).
|
||||
|
||||
If the named module is in a package, that package is imported as a side
|
||||
effect of invoking this function.
|
||||
|
||||
Non PEP 302 mechanisms (e.g. the Windows registry) used by the
|
||||
standard import machinery to find files in alternative locations
|
||||
are partially supported, but are searched AFTER sys.path. Normally,
|
||||
these locations are searched BEFORE sys.path, preventing sys.path
|
||||
entries from shadowing them.
|
||||
|
||||
For this to cause a visible difference in behaviour, there must
|
||||
be a module or package name that is accessible via both sys.path
|
||||
and one of the non PEP 302 file system mechanisms. In this case,
|
||||
the emulation will find the former version, while the builtin
|
||||
import mechanism will find the latter.
|
||||
|
||||
Items of the following types can be affected by this discrepancy:
|
||||
imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY
|
||||
If no module name is specified, all top level importers are produced.
|
||||
"""
|
||||
if fullname.startswith('.'):
|
||||
raise ImportError("Relative module names not supported")
|
||||
msg = "Relative module name {!r} not supported".format(fullname)
|
||||
raise ImportError(msg)
|
||||
if '.' in fullname:
|
||||
# Get the containing package's __path__
|
||||
pkg = '.'.join(fullname.split('.')[:-1])
|
||||
if pkg not in sys.modules:
|
||||
__import__(pkg)
|
||||
path = getattr(sys.modules[pkg], '__path__', None) or []
|
||||
pkg_name = fullname.rpartition(".")[0]
|
||||
pkg = importlib.import_module(pkg)
|
||||
path = getattr(sys.modules[pkg], '__path__', None)
|
||||
if path is None:
|
||||
return
|
||||
else:
|
||||
for importer in sys.meta_path:
|
||||
yield importer
|
||||
path = sys.path
|
||||
for item in path:
|
||||
yield get_importer(item)
|
||||
if '.' not in fullname:
|
||||
yield ImpImporter()
|
||||
|
||||
def get_loader(module_or_name):
|
||||
"""Get a PEP 302 "loader" object for module_or_name
|
||||
|
||||
If the module or package is accessible via the normal import
|
||||
mechanism, a wrapper around the relevant part of that machinery
|
||||
is returned. Returns None if the module cannot be found or imported.
|
||||
Returns None if the module cannot be found or imported.
|
||||
If the named module is not already imported, its containing package
|
||||
(if any) is imported, in order to establish the package __path__.
|
||||
|
||||
This function uses iter_importers(), and is thus subject to the same
|
||||
limitations regarding platform-specific special import locations such
|
||||
as the Windows registry.
|
||||
"""
|
||||
if module_or_name in sys.modules:
|
||||
module_or_name = sys.modules[module_or_name]
|
||||
|
@ -457,22 +438,33 @@ def get_loader(module_or_name):
|
|||
fullname = module_or_name
|
||||
return find_loader(fullname)
|
||||
|
||||
|
||||
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):
|
||||
if importer is None:
|
||||
continue
|
||||
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 throws other errors for cases where
|
||||
# pkgutil previously threw ImportError
|
||||
msg = "Error while finding loader for {!r} ({}: {})"
|
||||
raise ImportError(msg.format(fullname, type(ex), ex)) from ex
|
||||
|
||||
|
||||
def extend_path(path, name):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue