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

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

* Rebase and remove `set_package` from diff
This commit is contained in:
Nikita Sobolev 2022-10-07 03:57:10 +03:00 committed by GitHub
parent 4875433682
commit 27025e158c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 4 additions and 373 deletions

View File

@ -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`

View File

@ -11,12 +11,9 @@ from ._bootstrap_external import decode_source
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
@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."""

View File

@ -771,13 +771,7 @@ class SourceLoaderTestHarness:
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.

View File

@ -47,21 +47,6 @@ class NewLoader(TestLoader):
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 @@ class ModuleSpecMethodsTests:
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 @@ class ModuleSpecMethodsTests:
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

View File

@ -121,184 +121,6 @@ class ModuleFromSpecTests:
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()."""

View File

@ -0,0 +1,2 @@
Remove deprecated :func:`importlib.utils.set_loader` and
:func:`importlib.utils.module_for_loader` from :mod:`importlib.utils`.