From 876fc7fcec9a79a11546b7588d3683a5ccb4d31c Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 20 Oct 2021 14:05:29 -0700 Subject: bpo-35673: Add a public alias for namespace package __loader__ attribute (#29049) Rename namespace package __loader__ class to be public. Make the old name, i.e. _NamespaceLoader, an alias for the public name, for backward compatibility. --- Doc/library/importlib.rst | 18 ++++++++++++++++++ Lib/importlib/_bootstrap.py | 4 ++-- Lib/importlib/_bootstrap_external.py | 12 +++++++++--- Lib/importlib/abc.py | 2 +- Lib/importlib/machinery.py | 1 + Lib/test/test_importlib/test_namespace_pkgs.py | 7 +++++++ .../Library/2021-10-18-18-12-47.bpo-35673.KOkHWe.rst | 4 ++++ 7 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-10-18-18-12-47.bpo-35673.KOkHWe.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 1923057..a25f514 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1394,6 +1394,24 @@ find and load modules. .. versionadded:: 3.4 +.. class:: NamespaceLoader(name, path, path_finder): + + A concrete implementation of :class:`importlib.abc.InspectLoader` for + namespace packages. This is an alias for a private class and is only made + public for introspecting the ``__loader__`` attribute on namespace + packages:: + + >>> from importlib.machinery import NamespaceLoader + >>> import my_namespace + >>> isinstance(my_namespace.__loader__, NamespaceLoader) + True + >>> import importlib.abc + >>> isinstance(my_namespace.__loader__, importlib.abc.Loader) + True + + .. versionadded:: 3.11 + + .. class:: ModuleSpec(name, loader, *, origin=None, loader_state=None, is_package=None) A specification for a module's import-system-related state. This is diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 889f08f..afb95f4 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -507,9 +507,9 @@ def _init_module_attrs(spec, module, *, override=False): if spec.submodule_search_locations is not None: if _bootstrap_external is None: raise NotImplementedError - _NamespaceLoader = _bootstrap_external._NamespaceLoader + NamespaceLoader = _bootstrap_external.NamespaceLoader - loader = _NamespaceLoader.__new__(_NamespaceLoader) + loader = NamespaceLoader.__new__(NamespaceLoader) loader._path = spec.submodule_search_locations spec.loader = loader # While the docs say that module.__file__ is not set for diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index c9692b5..ef4f23a 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1279,8 +1279,10 @@ class _NamespacePath: self._path.append(item) -# We use this exclusively in module_from_spec() for backward-compatibility. -class _NamespaceLoader: +# This class is actually exposed publicly in a namespace package's __loader__ +# attribute, so it should be available through a non-private name. +# https://bugs.python.org/issue35673 +class NamespaceLoader: def __init__(self, name, path, path_finder): self._path = _NamespacePath(name, path, path_finder) @@ -1291,7 +1293,7 @@ class _NamespaceLoader: The method is deprecated. The import machinery does the job itself. """ - _warnings.warn("_NamespaceLoader.module_repr() is deprecated and " + _warnings.warn("NamespaceLoader.module_repr() is deprecated and " "slated for removal in Python 3.12", DeprecationWarning) return ''.format(module.__name__) @@ -1327,6 +1329,10 @@ class _NamespaceLoader: return NamespaceReader(self._path) +# We use this exclusively in module_from_spec() for backward-compatibility. +_NamespaceLoader = NamespaceLoader + + # Finders ##################################################################### class PathFinder: diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index 0b4a3f8..1d6843b 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -213,7 +213,7 @@ class InspectLoader(Loader): exec_module = _bootstrap_external._LoaderBasics.exec_module load_module = _bootstrap_external._LoaderBasics.load_module -_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter) +_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter, machinery.NamespaceLoader) class ExecutionLoader(InspectLoader): diff --git a/Lib/importlib/machinery.py b/Lib/importlib/machinery.py index 9a7757f..d9a19a1 100644 --- a/Lib/importlib/machinery.py +++ b/Lib/importlib/machinery.py @@ -12,6 +12,7 @@ from ._bootstrap_external import FileFinder from ._bootstrap_external import SourceFileLoader from ._bootstrap_external import SourcelessFileLoader from ._bootstrap_external import ExtensionFileLoader +from ._bootstrap_external import NamespaceLoader def all_suffixes(): diff --git a/Lib/test/test_importlib/test_namespace_pkgs.py b/Lib/test/test_importlib/test_namespace_pkgs.py index 3fe3ddc..f802832 100644 --- a/Lib/test/test_importlib/test_namespace_pkgs.py +++ b/Lib/test/test_importlib/test_namespace_pkgs.py @@ -1,5 +1,7 @@ import contextlib import importlib +import importlib.abc +import importlib.machinery import os import sys import unittest @@ -342,6 +344,11 @@ class LoaderTests(NamespacePackageTest): expected_path = os.path.join(self.root, 'portion1', 'foo') self.assertEqual(foo.__path__[0], expected_path) + def test_loader_abc(self): + import foo + self.assertTrue(isinstance(foo.__loader__, importlib.abc.Loader)) + self.assertTrue(isinstance(foo.__loader__, importlib.machinery.NamespaceLoader)) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2021-10-18-18-12-47.bpo-35673.KOkHWe.rst b/Misc/NEWS.d/next/Library/2021-10-18-18-12-47.bpo-35673.KOkHWe.rst new file mode 100644 index 0000000..e7d6a5f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-10-18-18-12-47.bpo-35673.KOkHWe.rst @@ -0,0 +1,4 @@ +Improve the introspectability of the ``__loader__`` attribute for namespace +packages. :class:`importlib.machinery.NamespaceLoader` is now public, and +implements the :class:`importlib.abc.InspectLoader` interface. ``_NamespaceLoader`` +is kept for backward compatibility. -- cgit v0.12