From 62b4b9eecbcdd98d318eec522c8c48a3843be855 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Tue, 4 Mar 2014 20:39:42 +1000 Subject: [PATCH] Close #20839: pkgutil.find_loader now uses importlib.util.find_spec --- Doc/library/pkgutil.rst | 17 +++++++++-------- Lib/pkgutil.py | 17 +++++------------ Lib/test/test_pkgutil.py | 19 +++++++++++++++++++ Misc/NEWS | 9 ++++++++- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/Doc/library/pkgutil.rst b/Doc/library/pkgutil.rst index 22d44eb9bc7..10b7848dd3d 100644 --- a/Doc/library/pkgutil.rst +++ b/Doc/library/pkgutil.rst @@ -74,15 +74,17 @@ support. Retrieve a :pep:`302` module loader for the given *fullname*. - This is a 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. + This is a backwards compatibility wrapper around + :func:`importlib.util.find_spec` that converts most failures to + :exc:`ImportError` and only returns the loader rather than the full + :class:`ModuleSpec`. .. versionchanged:: 3.3 Updated to be based directly on :mod:`importlib` rather than relying on the package internal PEP 302 import emulation. + .. versionchanged:: 3.4 + Updated to be based on :pep:`451` .. function:: get_importer(path_item) @@ -109,14 +111,13 @@ support. not already imported, its containing package (if any) is imported, in order 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 Updated to be based directly on :mod:`importlib` rather than relying on the package internal PEP 302 import emulation. + .. versionchanged:: 3.4 + Updated to be based on :pep:`451` + .. function:: iter_importers(fullname='') diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py index 326657aa888..58cccdc974c 100644 --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -470,29 +470,22 @@ def get_loader(module_or_name): def find_loader(fullname): """Find a PEP 302 "loader" object for fullname - 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. + This is a backwards compatibility wrapper around + importlib.util.find_spec that converts most failures to ImportError + and only returns the loader rather than the full spec """ 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) + spec = importlib.util.find_spec(fullname) 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 raised ImportError msg = "Error while finding loader for {!r} ({}: {})" raise ImportError(msg.format(fullname, type(ex), ex)) from ex + return spec.loader def extend_path(path, name): diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index 7a1cd41886b..aafdf3c34a0 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -334,6 +334,25 @@ class ImportlibMigrationTests(unittest.TestCase): self.assertIsNotNone(pkgutil.get_loader("test.support")) 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): # We use an illegal path so *none* of the path hooks should fire with check_warnings() as w: diff --git a/Misc/NEWS b/Misc/NEWS index 8b68f00df79..75d29094098 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,13 @@ Core and Builtins - Issue #20786: Fix signatures for dict.__delitem__ and property.__delete__ builtins. +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). + Build ----- @@ -27,7 +34,7 @@ Build uninstalling pip (rather than failing) if the user has updated pip to a different version from the one bundled with ensurepip. -- Issue #20465: Update OS X and Windows installer builds to use +- Issue #20465: Update OS X and Windows installer builds to use SQLite 3.8.3.1.