[Python-checkins] gh-97850: Remove deprecated functions from `importlib.utils` (#97898)

warsaw webhook-mailer at python.org
Thu Oct 6 20:57:19 EDT 2022


https://github.com/python/cpython/commit/27025e158c70331d0a8fb42fe234a2a6770850d1
commit: 27025e158c70331d0a8fb42fe234a2a6770850d1
branch: main
author: Nikita Sobolev <mail at sobolevn.me>
committer: warsaw <barry at python.org>
date: 2022-10-06T17:57:10-07:00
summary:

gh-97850: Remove deprecated functions from `importlib.utils` (#97898)

* gh-97850: Remove deprecated functions from `importlib.utils`

* Rebase and remove `set_package` from diff

files:
A Misc/NEWS.d/next/Library/2022-10-05-11-40-02.gh-issue-97850.NzdREm.rst
M Doc/library/importlib.rst
M Lib/importlib/util.py
M Lib/test/test_importlib/test_abc.py
M Lib/test/test_importlib/test_spec.py
M Lib/test/test_importlib/test_util.py

diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst
index a7c067c06e8e..3fc1531c0cdf 100644
--- a/Doc/library/importlib.rst
+++ b/Doc/library/importlib.rst
@@ -443,7 +443,7 @@ ABC hierarchy::
         from the import.  If the loader inserted a module and the load fails, it
         must be removed by the loader from :data:`sys.modules`; modules already
         in :data:`sys.modules` before the loader began execution should be left
-        alone (see :func:`importlib.util.module_for_loader`).
+        alone.
 
         The loader should set several attributes on the module
         (note that some of these attributes can change when a module is
@@ -1326,58 +1326,6 @@ an :term:`importer`.
 
    .. versionadded:: 3.5
 
-.. decorator:: module_for_loader
-
-    A :term:`decorator` for :meth:`importlib.abc.Loader.load_module`
-    to handle selecting the proper
-    module object to load with. The decorated method is expected to have a call
-    signature taking two positional arguments
-    (e.g. ``load_module(self, module)``) for which the second argument
-    will be the module **object** to be used by the loader.
-    Note that the decorator will not work on static methods because of the
-    assumption of two arguments.
-
-    The decorated method will take in the **name** of the module to be loaded
-    as expected for a :term:`loader`. If the module is not found in
-    :data:`sys.modules` then a new one is constructed. Regardless of where the
-    module came from, :attr:`__loader__` set to **self** and :attr:`__package__`
-    is set based on what :meth:`importlib.abc.InspectLoader.is_package` returns
-    (if available). These attributes are set unconditionally to support
-    reloading.
-
-    If an exception is raised by the decorated method and a module was added to
-    :data:`sys.modules`, then the module will be removed to prevent a partially
-    initialized module from being in left in :data:`sys.modules`. If the module
-    was already in :data:`sys.modules` then it is left alone.
-
-    .. versionchanged:: 3.3
-       :attr:`__loader__` and :attr:`__package__` are automatically set
-       (when possible).
-
-    .. versionchanged:: 3.4
-       Set :attr:`__name__`, :attr:`__loader__` :attr:`__package__`
-       unconditionally to support reloading.
-
-    .. deprecated:: 3.4
-       The import machinery now directly performs all the functionality
-       provided by this function.
-
-.. decorator:: set_loader
-
-   A :term:`decorator` for :meth:`importlib.abc.Loader.load_module`
-   to set the :attr:`__loader__`
-   attribute on the returned module. If the attribute is already set the
-   decorator does nothing. It is assumed that the first positional argument to
-   the wrapped method (i.e. ``self``) is what :attr:`__loader__` should be set
-   to.
-
-   .. versionchanged:: 3.4
-      Set ``__loader__`` if set to ``None``, as if the attribute does not
-      exist.
-
-   .. deprecated:: 3.4
-      The import machinery takes care of this automatically.
-
 .. function:: spec_from_loader(name, loader, *, origin=None, is_package=None)
 
    A factory function for creating a :class:`~importlib.machinery.ModuleSpec`
diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py
index 7f15b029b240..9e29c581b1db 100644
--- a/Lib/importlib/util.py
+++ b/Lib/importlib/util.py
@@ -11,12 +11,9 @@
 from ._bootstrap_external import source_from_cache
 from ._bootstrap_external import spec_from_file_location
 
-from contextlib import contextmanager
 import _imp
-import functools
 import sys
 import types
-import warnings
 
 
 def source_hash(source_bytes):
@@ -115,90 +112,6 @@ def find_spec(name, package=None):
             return spec
 
 
- at contextmanager
-def _module_to_load(name):
-    is_reload = name in sys.modules
-
-    module = sys.modules.get(name)
-    if not is_reload:
-        # This must be done before open() is called as the 'io' module
-        # implicitly imports 'locale' and would otherwise trigger an
-        # infinite loop.
-        module = type(sys)(name)
-        # This must be done before putting the module in sys.modules
-        # (otherwise an optimization shortcut in import.c becomes wrong)
-        module.__initializing__ = True
-        sys.modules[name] = module
-    try:
-        yield module
-    except Exception:
-        if not is_reload:
-            try:
-                del sys.modules[name]
-            except KeyError:
-                pass
-    finally:
-        module.__initializing__ = False
-
-
-def set_loader(fxn):
-    """Set __loader__ on the returned module.
-
-    This function is deprecated.
-
-    """
-    @functools.wraps(fxn)
-    def set_loader_wrapper(self, *args, **kwargs):
-        warnings.warn('The import system now takes care of this automatically; '
-                      'this decorator is slated for removal in Python 3.12',
-                      DeprecationWarning, stacklevel=2)
-        module = fxn(self, *args, **kwargs)
-        if getattr(module, '__loader__', None) is None:
-            module.__loader__ = self
-        return module
-    return set_loader_wrapper
-
-
-def module_for_loader(fxn):
-    """Decorator to handle selecting the proper module for loaders.
-
-    The decorated function is passed the module to use instead of the module
-    name. The module passed in to the function is either from sys.modules if
-    it already exists or is a new module. If the module is new, then __name__
-    is set the first argument to the method, __loader__ is set to self, and
-    __package__ is set accordingly (if self.is_package() is defined) will be set
-    before it is passed to the decorated function (if self.is_package() does
-    not work for the module it will be set post-load).
-
-    If an exception is raised and the decorator created the module it is
-    subsequently removed from sys.modules.
-
-    The decorator assumes that the decorated function takes the module name as
-    the second argument.
-
-    """
-    warnings.warn('The import system now takes care of this automatically; '
-                  'this decorator is slated for removal in Python 3.12',
-                  DeprecationWarning, stacklevel=2)
-    @functools.wraps(fxn)
-    def module_for_loader_wrapper(self, fullname, *args, **kwargs):
-        with _module_to_load(fullname) as module:
-            module.__loader__ = self
-            try:
-                is_package = self.is_package(fullname)
-            except (ImportError, AttributeError):
-                pass
-            else:
-                if is_package:
-                    module.__package__ = fullname
-                else:
-                    module.__package__ = fullname.rpartition('.')[0]
-            # If __package__ was not set above, __import__() will do it later.
-            return fxn(self, module, *args, **kwargs)
-
-    return module_for_loader_wrapper
-
-
 class _LazyModule(types.ModuleType):
 
     """A subclass of the module type which triggers loading upon attribute access."""
diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py
index 8641b6cc6830..3c9149c4e45a 100644
--- a/Lib/test/test_importlib/test_abc.py
+++ b/Lib/test/test_importlib/test_abc.py
@@ -771,13 +771,7 @@ def verify_code(self, code_object):
 
 
 class SourceOnlyLoaderTests(SourceLoaderTestHarness):
-
-    """Test importlib.abc.SourceLoader for source-only loading.
-
-    Reload testing is subsumed by the tests for
-    importlib.util.module_for_loader.
-
-    """
+    """Test importlib.abc.SourceLoader for source-only loading."""
 
     def test_get_source(self):
         # Verify the source code is returned as a string.
diff --git a/Lib/test/test_importlib/test_spec.py b/Lib/test/test_importlib/test_spec.py
index f1ab16c7b2a9..80aa3609c6f9 100644
--- a/Lib/test/test_importlib/test_spec.py
+++ b/Lib/test/test_importlib/test_spec.py
@@ -47,21 +47,6 @@ def exec_module(self, module):
         module.eggs = self.EGGS
 
 
-class LegacyLoader(TestLoader):
-
-    HAM = -1
-
-    with warnings.catch_warnings():
-        warnings.simplefilter("ignore", DeprecationWarning)
-
-        frozen_util = util['Frozen']
-
-        @frozen_util.module_for_loader
-        def load_module(self, module):
-            module.ham = self.HAM
-            return module
-
-
 class ModuleSpecTests:
 
     def setUp(self):
@@ -302,26 +287,6 @@ def exec_module(self, module):
                 loaded = self.bootstrap._load(self.spec)
             self.assertNotIn(self.spec.name, sys.modules)
 
-    def test_load_legacy(self):
-        with warnings.catch_warnings():
-            warnings.simplefilter("ignore", ImportWarning)
-            self.spec.loader = LegacyLoader()
-            with CleanImport(self.spec.name):
-                loaded = self.bootstrap._load(self.spec)
-
-            self.assertEqual(loaded.ham, -1)
-
-    def test_load_legacy_attributes(self):
-        with warnings.catch_warnings():
-            warnings.simplefilter("ignore", ImportWarning)
-            self.spec.loader = LegacyLoader()
-            with CleanImport(self.spec.name):
-                loaded = self.bootstrap._load(self.spec)
-
-            self.assertIs(loaded.__loader__, self.spec.loader)
-            self.assertEqual(loaded.__package__, self.spec.parent)
-            self.assertIs(loaded.__spec__, self.spec)
-
     def test_load_legacy_attributes_immutable(self):
         module = object()
         with warnings.catch_warnings():
@@ -387,19 +352,6 @@ def test_reload_init_module_attrs(self):
         self.assertFalse(hasattr(loaded, '__file__'))
         self.assertFalse(hasattr(loaded, '__cached__'))
 
-    def test_reload_legacy(self):
-        with warnings.catch_warnings():
-            warnings.simplefilter("ignore", ImportWarning)
-            self.spec.loader = LegacyLoader()
-            with CleanImport(self.spec.name):
-                loaded = self.bootstrap._load(self.spec)
-                reloaded = self.bootstrap._exec(self.spec, loaded)
-                installed = sys.modules[self.spec.name]
-
-        self.assertEqual(loaded.ham, -1)
-        self.assertIs(reloaded, loaded)
-        self.assertIs(installed, loaded)
-
 
 (Frozen_ModuleSpecMethodsTests,
  Source_ModuleSpecMethodsTests
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
index e70971e9d3bc..08a615ecf528 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -121,184 +121,6 @@ def test___cached__(self):
                    util=importlib_util)
 
 
-class ModuleForLoaderTests:
-
-    """Tests for importlib.util.module_for_loader."""
-
-    @classmethod
-    def module_for_loader(cls, func):
-        with warnings.catch_warnings():
-            warnings.simplefilter('ignore', DeprecationWarning)
-            return cls.util.module_for_loader(func)
-
-    def test_warning(self):
-        # Should raise a PendingDeprecationWarning when used.
-        with warnings.catch_warnings():
-            warnings.simplefilter('error', DeprecationWarning)
-            with self.assertRaises(DeprecationWarning):
-                func = self.util.module_for_loader(lambda x: x)
-
-    def return_module(self, name):
-        fxn = self.module_for_loader(lambda self, module: module)
-        return fxn(self, name)
-
-    def raise_exception(self, name):
-        def to_wrap(self, module):
-            raise ImportError
-        fxn = self.module_for_loader(to_wrap)
-        try:
-            fxn(self, name)
-        except ImportError:
-            pass
-
-    def test_new_module(self):
-        # Test that when no module exists in sys.modules a new module is
-        # created.
-        module_name = 'a.b.c'
-        with util.uncache(module_name):
-            module = self.return_module(module_name)
-            self.assertIn(module_name, sys.modules)
-        self.assertIsInstance(module, types.ModuleType)
-        self.assertEqual(module.__name__, module_name)
-
-    def test_reload(self):
-        # Test that a module is reused if already in sys.modules.
-        class FakeLoader:
-            def is_package(self, name):
-                return True
-            @self.module_for_loader
-            def load_module(self, module):
-                return module
-        name = 'a.b.c'
-        module = types.ModuleType('a.b.c')
-        module.__loader__ = 42
-        module.__package__ = 42
-        with util.uncache(name):
-            sys.modules[name] = module
-            loader = FakeLoader()
-            returned_module = loader.load_module(name)
-            self.assertIs(returned_module, sys.modules[name])
-            self.assertEqual(module.__loader__, loader)
-            self.assertEqual(module.__package__, name)
-
-    def test_new_module_failure(self):
-        # Test that a module is removed from sys.modules if added but an
-        # exception is raised.
-        name = 'a.b.c'
-        with util.uncache(name):
-            self.raise_exception(name)
-            self.assertNotIn(name, sys.modules)
-
-    def test_reload_failure(self):
-        # Test that a failure on reload leaves the module in-place.
-        name = 'a.b.c'
-        module = types.ModuleType(name)
-        with util.uncache(name):
-            sys.modules[name] = module
-            self.raise_exception(name)
-            self.assertIs(module, sys.modules[name])
-
-    def test_decorator_attrs(self):
-        def fxn(self, module): pass
-        wrapped = self.module_for_loader(fxn)
-        self.assertEqual(wrapped.__name__, fxn.__name__)
-        self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
-
-    def test_false_module(self):
-        # If for some odd reason a module is considered false, still return it
-        # from sys.modules.
-        class FalseModule(types.ModuleType):
-            def __bool__(self): return False
-
-        name = 'mod'
-        module = FalseModule(name)
-        with util.uncache(name):
-            self.assertFalse(module)
-            sys.modules[name] = module
-            given = self.return_module(name)
-            self.assertIs(given, module)
-
-    def test_attributes_set(self):
-        # __name__, __loader__, and __package__ should be set (when
-        # is_package() is defined; undefined implicitly tested elsewhere).
-        class FakeLoader:
-            def __init__(self, is_package):
-                self._pkg = is_package
-            def is_package(self, name):
-                return self._pkg
-            @self.module_for_loader
-            def load_module(self, module):
-                return module
-
-        name = 'pkg.mod'
-        with util.uncache(name):
-            loader = FakeLoader(False)
-            module = loader.load_module(name)
-            self.assertEqual(module.__name__, name)
-            self.assertIs(module.__loader__, loader)
-            self.assertEqual(module.__package__, 'pkg')
-
-        name = 'pkg.sub'
-        with util.uncache(name):
-            loader = FakeLoader(True)
-            module = loader.load_module(name)
-            self.assertEqual(module.__name__, name)
-            self.assertIs(module.__loader__, loader)
-            self.assertEqual(module.__package__, name)
-
-
-(Frozen_ModuleForLoaderTests,
- Source_ModuleForLoaderTests
- ) = util.test_both(ModuleForLoaderTests, util=importlib_util)
-
-
-class SetLoaderTests:
-
-    """Tests importlib.util.set_loader()."""
-
-    @property
-    def DummyLoader(self):
-        # Set DummyLoader on the class lazily.
-        class DummyLoader:
-            @self.util.set_loader
-            def load_module(self, module):
-                return self.module
-        self.__class__.DummyLoader = DummyLoader
-        return DummyLoader
-
-    def test_no_attribute(self):
-        loader = self.DummyLoader()
-        loader.module = types.ModuleType('blah')
-        try:
-            del loader.module.__loader__
-        except AttributeError:
-            pass
-        with warnings.catch_warnings():
-            warnings.simplefilter('ignore', DeprecationWarning)
-            self.assertEqual(loader, loader.load_module('blah').__loader__)
-
-    def test_attribute_is_None(self):
-        loader = self.DummyLoader()
-        loader.module = types.ModuleType('blah')
-        loader.module.__loader__ = None
-        with warnings.catch_warnings():
-            warnings.simplefilter('ignore', DeprecationWarning)
-            self.assertEqual(loader, loader.load_module('blah').__loader__)
-
-    def test_not_reset(self):
-        loader = self.DummyLoader()
-        loader.module = types.ModuleType('blah')
-        loader.module.__loader__ = 42
-        with warnings.catch_warnings():
-            warnings.simplefilter('ignore', DeprecationWarning)
-            self.assertEqual(42, loader.load_module('blah').__loader__)
-
-
-(Frozen_SetLoaderTests,
- Source_SetLoaderTests
- ) = util.test_both(SetLoaderTests, util=importlib_util)
-
-
 class ResolveNameTests:
 
     """Tests importlib.util.resolve_name()."""
diff --git a/Misc/NEWS.d/next/Library/2022-10-05-11-40-02.gh-issue-97850.NzdREm.rst b/Misc/NEWS.d/next/Library/2022-10-05-11-40-02.gh-issue-97850.NzdREm.rst
new file mode 100644
index 000000000000..5e759bc0995a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-10-05-11-40-02.gh-issue-97850.NzdREm.rst
@@ -0,0 +1,2 @@
+Remove deprecated :func:`importlib.utils.set_loader` and
+:func:`importlib.utils.module_for_loader` from :mod:`importlib.utils`.



More information about the Python-checkins mailing list