Commit dc855b7b authored by Nick Coghlan's avatar Nick Coghlan

Close #20839: pkgutil.find_loader now uses importlib.util.find_spec

parent c913a7a6
...@@ -74,15 +74,17 @@ support. ...@@ -74,15 +74,17 @@ support.
Retrieve a :pep:`302` module loader for the given *fullname*. Retrieve a :pep:`302` module loader for the given *fullname*.
This is a convenience wrapper around :func:`importlib.find_loader` that This is a backwards compatibility wrapper around
sets the *path* argument correctly when searching for submodules, and :func:`importlib.util.find_spec` that converts most failures to
also ensures parent packages (if any) are imported before searching for :exc:`ImportError` and only returns the loader rather than the full
submodules. :class:`ModuleSpec`.
.. versionchanged:: 3.3 .. versionchanged:: 3.3
Updated to be based directly on :mod:`importlib` rather than relying Updated to be based directly on :mod:`importlib` rather than relying
on the package internal PEP 302 import emulation. on the package internal PEP 302 import emulation.
.. versionchanged:: 3.4
Updated to be based on :pep:`451`
.. function:: get_importer(path_item) .. function:: get_importer(path_item)
...@@ -109,14 +111,13 @@ support. ...@@ -109,14 +111,13 @@ support.
not already imported, its containing package (if any) is imported, in order not already imported, its containing package (if any) is imported, in order
to establish the package ``__path__``. to establish the package ``__path__``.
This function uses :func:`iter_importers`, and is thus subject to the same
limitations regarding platform-specific special import locations such as the
Windows registry.
.. versionchanged:: 3.3 .. versionchanged:: 3.3
Updated to be based directly on :mod:`importlib` rather than relying Updated to be based directly on :mod:`importlib` rather than relying
on the package internal PEP 302 import emulation. on the package internal PEP 302 import emulation.
.. versionchanged:: 3.4
Updated to be based on :pep:`451`
.. function:: iter_importers(fullname='') .. function:: iter_importers(fullname='')
......
...@@ -470,29 +470,22 @@ def get_loader(module_or_name): ...@@ -470,29 +470,22 @@ def get_loader(module_or_name):
def find_loader(fullname): def find_loader(fullname):
"""Find a PEP 302 "loader" object for fullname """Find a PEP 302 "loader" object for fullname
This is s convenience wrapper around :func:`importlib.find_loader` that This is a backwards compatibility wrapper around
sets the *path* argument correctly when searching for submodules, and importlib.util.find_spec that converts most failures to ImportError
also ensures parent packages (if any) are imported before searching for and only returns the loader rather than the full spec
submodules.
""" """
if fullname.startswith('.'): if fullname.startswith('.'):
msg = "Relative module name {!r} not supported".format(fullname) msg = "Relative module name {!r} not supported".format(fullname)
raise ImportError(msg) 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: try:
return importlib.find_loader(fullname, path) spec = importlib.util.find_spec(fullname)
except (ImportError, AttributeError, TypeError, ValueError) as ex: except (ImportError, AttributeError, TypeError, ValueError) as ex:
# This hack fixes an impedance mismatch between pkgutil and # This hack fixes an impedance mismatch between pkgutil and
# importlib, where the latter raises other errors for cases where # importlib, where the latter raises other errors for cases where
# pkgutil previously raised ImportError # pkgutil previously raised ImportError
msg = "Error while finding loader for {!r} ({}: {})" msg = "Error while finding loader for {!r} ({}: {})"
raise ImportError(msg.format(fullname, type(ex), ex)) from ex raise ImportError(msg.format(fullname, type(ex), ex)) from ex
return spec.loader
def extend_path(path, name): def extend_path(path, name):
......
...@@ -334,6 +334,25 @@ class ImportlibMigrationTests(unittest.TestCase): ...@@ -334,6 +334,25 @@ class ImportlibMigrationTests(unittest.TestCase):
self.assertIsNotNone(pkgutil.get_loader("test.support")) self.assertIsNotNone(pkgutil.get_loader("test.support"))
self.assertEqual(len(w.warnings), 0) self.assertEqual(len(w.warnings), 0)
def test_get_loader_handles_missing_loader_attribute(self):
global __loader__
this_loader = __loader__
del __loader__
try:
with check_warnings() as w:
self.assertIsNotNone(pkgutil.get_loader(__name__))
self.assertEqual(len(w.warnings), 0)
finally:
__loader__ = this_loader
def test_find_loader_avoids_emulation(self):
with check_warnings() as w:
self.assertIsNotNone(pkgutil.find_loader("sys"))
self.assertIsNotNone(pkgutil.find_loader("os"))
self.assertIsNotNone(pkgutil.find_loader("test.support"))
self.assertEqual(len(w.warnings), 0)
def test_get_importer_avoids_emulation(self): def test_get_importer_avoids_emulation(self):
# We use an illegal path so *none* of the path hooks should fire # We use an illegal path so *none* of the path hooks should fire
with check_warnings() as w: with check_warnings() as w:
......
...@@ -20,6 +20,10 @@ Core and Builtins ...@@ -20,6 +20,10 @@ Core and Builtins
Library Library
------- -------
- Issue #20839: Don't trigger a DeprecationWarning in the still supported
pkgutil.get_loader() API when __loader__ isn't set on a module (nor
when pkgutil.find_loader() is called directly).
- Issue #20778: Fix modulefinder to work with bytecode-only modules. - Issue #20778: Fix modulefinder to work with bytecode-only modules.
- Issue #20791: copy.copy() now doesn't make a copy when the input is - Issue #20791: copy.copy() now doesn't make a copy when the input is
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment