Skip to content

Commit

Permalink
gh-94619: Remove long deprecated methods module_repr() and load_modul…
Browse files Browse the repository at this point in the history
…e() (#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>
  • Loading branch information
warsaw and brettcannon committed Aug 5, 2022
1 parent 44f1f63 commit e1182bc
Show file tree
Hide file tree
Showing 12 changed files with 11 additions and 180 deletions.
14 changes: 0 additions & 14 deletions Doc/library/importlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -493,20 +493,6 @@ ABC hierarchy::
other responsibilities of :meth:`load_module` when
: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

Expand Down
20 changes: 4 additions & 16 deletions Doc/reference/import.rst
Original file line number Diff line number Diff line change
Expand Up @@ -676,22 +676,10 @@ Here are the exact rules used:

* Otherwise, just use the module's ``__name__`` in the repr.

.. versionchanged:: 3.4
Use of :meth:`loader.module_repr() <importlib.abc.Loader.module_repr>`
has been deprecated and the module spec is now used by the import
machinery to generate a module 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.
.. versionchanged:: 3.12
Use of :meth:`module_repr`, having been deprecated since Python 3.4, was
removed in Python 3.12 and is no longer called during the resolution of a
module's repr.

.. _pyc-invalidation:

Expand Down
14 changes: 0 additions & 14 deletions Lib/importlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,3 @@ def load_module(self, fullname):
raise ImportError
# Warning implemented in _load_module_shim().
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
6 changes: 0 additions & 6 deletions Lib/importlib/_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,11 +296,6 @@ def _module_repr(module):
loader = getattr(module, '__loader__', None)
if spec := getattr(module, "__spec__", None):
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.
try:
name = module.__name__
Expand Down Expand Up @@ -582,7 +577,6 @@ def module_from_spec(spec):

def _module_repr_from_spec(spec):
"""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
if spec.origin is None:
if spec.loader is None:
Expand Down
13 changes: 1 addition & 12 deletions Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -1339,22 +1339,11 @@ def append(self, item):

# 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
# https://github.com/python/cpython/issues/92054
class NamespaceLoader:
def __init__(self, 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):
return True

Expand Down
8 changes: 0 additions & 8 deletions Lib/test/test_importlib/frozen/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,6 @@ def test_lacking_parent(self):
expected=value))
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):
name = '__hello__'
module, output = self.exec_module(name)
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_importlib/source/test_file_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ class Tester(self.abc.FileLoader):
def get_code(self, _): pass
def get_source(self, _): pass
def is_package(self, _): pass
def module_repr(self, _): pass

path = 'some_path'
name = 'some_name'
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_importlib/test_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,6 @@ def test_module_repr(self):
mod = types.ModuleType('blah')
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
with self.assertRaises(NotImplementedError):
self.ins.module_repr(mod)
original_repr = repr(mod)
mod.__loader__ = self.ins
# Should still return a proper repr.
Expand Down
7 changes: 0 additions & 7 deletions Lib/test/test_importlib/test_namespace_pkgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,6 @@ def test_cant_import_other(self):
with self.assertRaises(ImportError):
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):
paths = ['portion1']
Expand Down
95 changes: 0 additions & 95 deletions Lib/test/test_importlib/test_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,101 +407,6 @@ def test_reload_legacy(self):
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:

def setUp(self):
Expand Down
10 changes: 5 additions & 5 deletions Lib/test/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
import sys
ModuleType = type(sys)


class FullLoader:
@classmethod
def module_repr(cls, m):
return "<module '{}' (crafted)>".format(m.__name__)
pass


class BareLoader:
pass
Expand Down Expand Up @@ -236,7 +236,7 @@ def test_module_repr_with_full_loader(self):
# Yes, a class not an instance.
m.__loader__ = FullLoader
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):
# Because the loader has no module_repr(), use the file name.
Expand All @@ -252,7 +252,7 @@ def test_module_repr_with_full_loader_and_filename(self):
# Yes, a class not an instance.
m.__loader__ = FullLoader
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):
self.assertEqual(repr(sys), "<module 'sys' (built-in)>")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove the long-deprecated `module_repr()` from `importlib`.

0 comments on commit e1182bc

Please sign in to comment.