mirror of https://github.com/python/cpython
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:
parent
4875433682
commit
27025e158c
|
@ -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`
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()."""
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Remove deprecated :func:`importlib.utils.set_loader` and
|
||||
:func:`importlib.utils.module_for_loader` from :mod:`importlib.utils`.
|
Loading…
Reference in New Issue