gh-94619: Remove long deprecated methods module_repr() and load_module() (#94624)

* gh-94619: Remove long deprecated methods module_repr() and load_module()

Closes #94619

* Update Misc/NEWS.d/next/Library/2022-07-06-14-57-33.gh-issue-94619.PRqKVX.rst

Fix typo

Co-authored-by: Brett Cannon <brett@python.org>

Co-authored-by: Brett Cannon <brett@python.org>
This commit is contained in:
Barry Warsaw 2022-08-04 17:24:26 -07:00 committed by GitHub
parent 44f1f63ad5
commit e1182bc377
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 11 additions and 180 deletions

View file

@ -493,20 +493,6 @@ ABC hierarchy::
other responsibilities of :meth:`load_module` when other responsibilities of :meth:`load_module` when
:meth:`exec_module` is implemented. :meth:`exec_module` is implemented.
.. method:: module_repr(module)
A legacy method which when implemented calculates and returns the given
module's representation, as a string. The module type's default
:meth:`__repr__` will use the result of this method as appropriate.
.. versionadded:: 3.3
.. versionchanged:: 3.4
Made optional instead of an abstractmethod.
.. deprecated:: 3.4
The import machinery now takes care of this automatically.
.. class:: ResourceLoader .. class:: ResourceLoader

View file

@ -676,22 +676,10 @@ Here are the exact rules used:
* Otherwise, just use the module's ``__name__`` in the repr. * Otherwise, just use the module's ``__name__`` in the repr.
.. versionchanged:: 3.4 .. versionchanged:: 3.12
Use of :meth:`loader.module_repr() <importlib.abc.Loader.module_repr>` Use of :meth:`module_repr`, having been deprecated since Python 3.4, was
has been deprecated and the module spec is now used by the import removed in Python 3.12 and is no longer called during the resolution of a
machinery to generate a module repr. module's repr.
For backward compatibility with Python 3.3, the module repr will be
generated by calling the loader's
:meth:`~importlib.abc.Loader.module_repr` method, if defined, before
trying either approach described above. However, the method is deprecated.
.. versionchanged:: 3.10
Calling :meth:`~importlib.abc.Loader.module_repr` now occurs after trying to
use a module's ``__spec__`` attribute but before falling back on
``__file__``. Use of :meth:`~importlib.abc.Loader.module_repr` is slated to
stop in Python 3.12.
.. _pyc-invalidation: .. _pyc-invalidation:

View file

@ -38,17 +38,3 @@ class Loader(metaclass=abc.ABCMeta):
raise ImportError raise ImportError
# Warning implemented in _load_module_shim(). # Warning implemented in _load_module_shim().
return _bootstrap._load_module_shim(self, fullname) return _bootstrap._load_module_shim(self, fullname)
def module_repr(self, module):
"""Return a module's repr.
Used by the module type when the method does not raise
NotImplementedError.
This method is deprecated.
"""
warnings.warn("importlib.abc.Loader.module_repr() is deprecated and "
"slated for removal in Python 3.12", DeprecationWarning)
# The exception will cause ModuleType.__repr__ to ignore this method.
raise NotImplementedError

View file

@ -296,11 +296,6 @@ def _module_repr(module):
loader = getattr(module, '__loader__', None) loader = getattr(module, '__loader__', None)
if spec := getattr(module, "__spec__", None): if spec := getattr(module, "__spec__", None):
return _module_repr_from_spec(spec) return _module_repr_from_spec(spec)
elif hasattr(loader, 'module_repr'):
try:
return loader.module_repr(module)
except Exception:
pass
# Fall through to a catch-all which always succeeds. # Fall through to a catch-all which always succeeds.
try: try:
name = module.__name__ name = module.__name__
@ -582,7 +577,6 @@ def module_from_spec(spec):
def _module_repr_from_spec(spec): def _module_repr_from_spec(spec):
"""Return the repr to use for the module.""" """Return the repr to use for the module."""
# We mostly replicate _module_repr() using the spec attributes.
name = '?' if spec.name is None else spec.name name = '?' if spec.name is None else spec.name
if spec.origin is None: if spec.origin is None:
if spec.loader is None: if spec.loader is None:

View file

@ -1339,22 +1339,11 @@ class _NamespacePath:
# This class is actually exposed publicly in a namespace package's __loader__ # This class is actually exposed publicly in a namespace package's __loader__
# attribute, so it should be available through a non-private name. # attribute, so it should be available through a non-private name.
# https://bugs.python.org/issue35673 # https://github.com/python/cpython/issues/92054
class NamespaceLoader: class NamespaceLoader:
def __init__(self, name, path, path_finder): def __init__(self, name, path, path_finder):
self._path = _NamespacePath(name, path, path_finder) self._path = _NamespacePath(name, path, path_finder)
@staticmethod
def module_repr(module):
"""Return repr for the module.
The method is deprecated. The import machinery does the job itself.
"""
_warnings.warn("NamespaceLoader.module_repr() is deprecated and "
"slated for removal in Python 3.12", DeprecationWarning)
return '<module {!r} (namespace)>'.format(module.__name__)
def is_package(self, fullname): def is_package(self, fullname):
return True return True

View file

@ -103,14 +103,6 @@ class ExecModuleTests(abc.LoaderTests):
expected=value)) expected=value))
self.assertEqual(output, 'Hello world!\n') self.assertEqual(output, 'Hello world!\n')
def test_module_repr(self):
name = '__hello__'
module, output = self.exec_module(name)
with deprecated():
repr_str = self.machinery.FrozenImporter.module_repr(module)
self.assertEqual(repr_str,
"<module '__hello__' (frozen)>")
def test_module_repr_indirect(self): def test_module_repr_indirect(self):
name = '__hello__' name = '__hello__'
module, output = self.exec_module(name) module, output = self.exec_module(name)

View file

@ -51,7 +51,6 @@ class SimpleTest(abc.LoaderTests):
def get_code(self, _): pass def get_code(self, _): pass
def get_source(self, _): pass def get_source(self, _): pass
def is_package(self, _): pass def is_package(self, _): pass
def module_repr(self, _): pass
path = 'some_path' path = 'some_path'
name = 'some_name' name = 'some_name'

View file

@ -221,8 +221,6 @@ class LoaderDefaultsTests(ABCTestHarness):
mod = types.ModuleType('blah') mod = types.ModuleType('blah')
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning) warnings.simplefilter("ignore", DeprecationWarning)
with self.assertRaises(NotImplementedError):
self.ins.module_repr(mod)
original_repr = repr(mod) original_repr = repr(mod)
mod.__loader__ = self.ins mod.__loader__ = self.ins
# Should still return a proper repr. # Should still return a proper repr.

View file

@ -79,13 +79,6 @@ class SingleNamespacePackage(NamespacePackageTest):
with self.assertRaises(ImportError): with self.assertRaises(ImportError):
import foo.two import foo.two
def test_module_repr(self):
import foo.one
with warnings.catch_warnings():
warnings.simplefilter("ignore")
self.assertEqual(foo.__spec__.loader.module_repr(foo),
"<module 'foo' (namespace)>")
class DynamicPathNamespacePackage(NamespacePackageTest): class DynamicPathNamespacePackage(NamespacePackageTest):
paths = ['portion1'] paths = ['portion1']

View file

@ -407,101 +407,6 @@ class ModuleSpecMethodsTests:
machinery=machinery) machinery=machinery)
class ModuleReprTests:
@property
def bootstrap(self):
return self.init._bootstrap
def setUp(self):
self.module = type(os)('spam')
self.spec = self.machinery.ModuleSpec('spam', TestLoader())
def test_module___loader___module_repr(self):
class Loader:
def module_repr(self, module):
return '<delicious {}>'.format(module.__name__)
self.module.__loader__ = Loader()
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr, '<delicious spam>')
def test_module___loader___module_repr_bad(self):
class Loader(TestLoader):
def module_repr(self, module):
raise Exception
self.module.__loader__ = Loader()
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr,
'<module {!r} (<TestLoader object>)>'.format('spam'))
def test_module___spec__(self):
origin = 'in a hole, in the ground'
self.spec.origin = origin
self.module.__spec__ = self.spec
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr, '<module {!r} ({})>'.format('spam', origin))
def test_module___spec___location(self):
location = 'in_a_galaxy_far_far_away.py'
self.spec.origin = location
self.spec._set_fileattr = True
self.module.__spec__ = self.spec
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr,
'<module {!r} from {!r}>'.format('spam', location))
def test_module___spec___no_origin(self):
self.spec.loader = TestLoader()
self.module.__spec__ = self.spec
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr,
'<module {!r} (<TestLoader object>)>'.format('spam'))
def test_module___spec___no_origin_no_loader(self):
self.spec.loader = None
self.module.__spec__ = self.spec
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr, '<module {!r}>'.format('spam'))
def test_module_no_name(self):
del self.module.__name__
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr, '<module {!r}>'.format('?'))
def test_module_with_file(self):
filename = 'e/i/e/i/o/spam.py'
self.module.__file__ = filename
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr,
'<module {!r} from {!r}>'.format('spam', filename))
def test_module_no_file(self):
self.module.__loader__ = TestLoader()
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr,
'<module {!r} (<TestLoader object>)>'.format('spam'))
def test_module_no_file_no_loader(self):
modrepr = self.bootstrap._module_repr(self.module)
self.assertEqual(modrepr, '<module {!r}>'.format('spam'))
(Frozen_ModuleReprTests,
Source_ModuleReprTests
) = test_util.test_both(ModuleReprTests, init=init, util=util,
machinery=machinery)
class FactoryTests: class FactoryTests:
def setUp(self): def setUp(self):

View file

@ -8,10 +8,10 @@ from test.support.script_helper import assert_python_ok
import sys import sys
ModuleType = type(sys) ModuleType = type(sys)
class FullLoader: class FullLoader:
@classmethod pass
def module_repr(cls, m):
return "<module '{}' (crafted)>".format(m.__name__)
class BareLoader: class BareLoader:
pass pass
@ -236,7 +236,7 @@ a = A(destroyed)"""
# Yes, a class not an instance. # Yes, a class not an instance.
m.__loader__ = FullLoader m.__loader__ = FullLoader
self.assertEqual( self.assertEqual(
repr(m), "<module 'foo' (crafted)>") repr(m), "<module 'foo' (<class 'test.test_module.FullLoader'>)>")
def test_module_repr_with_bare_loader_and_filename(self): def test_module_repr_with_bare_loader_and_filename(self):
# Because the loader has no module_repr(), use the file name. # Because the loader has no module_repr(), use the file name.
@ -252,7 +252,7 @@ a = A(destroyed)"""
# Yes, a class not an instance. # Yes, a class not an instance.
m.__loader__ = FullLoader m.__loader__ = FullLoader
m.__file__ = '/tmp/foo.py' m.__file__ = '/tmp/foo.py'
self.assertEqual(repr(m), "<module 'foo' (crafted)>") self.assertEqual(repr(m), "<module 'foo' from '/tmp/foo.py'>")
def test_module_repr_builtin(self): def test_module_repr_builtin(self):
self.assertEqual(repr(sys), "<module 'sys' (built-in)>") self.assertEqual(repr(sys), "<module 'sys' (built-in)>")

View file

@ -0,0 +1 @@
Remove the long-deprecated `module_repr()` from `importlib`.