Skip to content

Commit 9cb31d6

Browse files
authored
bpo-42137: have ModuleType.__repr__ prefer __spec__ over module_repr() (GH-24953)
This is to work towards the removal of the use of module_repr() in Python 3.12 (documented as deprecated since 3.4).
1 parent 3ba3d51 commit 9cb31d6

File tree

7 files changed

+147
-153
lines changed

7 files changed

+147
-153
lines changed

Doc/library/importlib.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ Functions
208208
.. versionadded:: 3.4
209209
.. versionchanged:: 3.7
210210
:exc:`ModuleNotFoundError` is raised when the module being reloaded lacks
211-
a :class:`ModuleSpec`.
211+
a :class:`~importlib.machinery.ModuleSpec`.
212212

213213

214214
:mod:`importlib.abc` -- Abstract base classes related to import
@@ -1591,19 +1591,19 @@ an :term:`importer`.
15911591

15921592
.. function:: spec_from_loader(name, loader, *, origin=None, is_package=None)
15931593

1594-
A factory function for creating a :class:`ModuleSpec` instance based
1595-
on a loader. The parameters have the same meaning as they do for
1596-
ModuleSpec. The function uses available :term:`loader` APIs, such as
1594+
A factory function for creating a :class:`~importlib.machinery.ModuleSpec`
1595+
instance based on a loader. The parameters have the same meaning as they do
1596+
for ModuleSpec. The function uses available :term:`loader` APIs, such as
15971597
:meth:`InspectLoader.is_package`, to fill in any missing
15981598
information on the spec.
15991599

16001600
.. versionadded:: 3.4
16011601

16021602
.. function:: spec_from_file_location(name, location, *, loader=None, submodule_search_locations=None)
16031603

1604-
A factory function for creating a :class:`ModuleSpec` instance based
1605-
on the path to a file. Missing information will be filled in on the
1606-
spec by making use of loader APIs and by the implication that the
1604+
A factory function for creating a :class:`~importlib.machinery.ModuleSpec`
1605+
instance based on the path to a file. Missing information will be filled in
1606+
on the spec by making use of loader APIs and by the implication that the
16071607
module will be file-based.
16081608

16091609
.. versionadded:: 3.4

Doc/whatsnew/3.10.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,12 @@ Deprecated
10041004
:meth:`~importlib.abc.Loader.exec_module` is preferred.
10051005
(Contributed by Brett Cannon in :issue:`26131`.)
10061006
1007+
* The import system now uses the ``__spec__`` attribute on modules before
1008+
falling back on :meth:`~importlib.abc.Loader.module_repr` for a module's
1009+
``__repr__()`` method. Removal of the use of ``module_repr()`` is scheduled
1010+
for Python 3.12.
1011+
(Contributed by Brett Cannon in :issue:`42137`.)
1012+
10071013
* ``sqlite3.OptimizedUnicode`` has been undocumented and obsolete since Python
10081014
3.3, when it was made an alias to :class:`str`. It is now deprecated,
10091015
scheduled for removal in Python 3.12.

Lib/importlib/_bootstrap.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def _requires_frozen_wrapper(self, fullname):
275275
def _load_module_shim(self, fullname):
276276
"""Load the specified module into sys.modules and return it.
277277
278-
This method is deprecated. Use loader.exec_module instead.
278+
This method is deprecated. Use loader.exec_module() instead.
279279
280280
"""
281281
msg = ("the load_module() method is deprecated and slated for removal in "
@@ -292,24 +292,16 @@ def _load_module_shim(self, fullname):
292292
# Module specifications #######################################################
293293

294294
def _module_repr(module):
295-
# The implementation of ModuleType.__repr__().
295+
"""The implementation of ModuleType.__repr__()."""
296296
loader = getattr(module, '__loader__', None)
297-
if hasattr(loader, 'module_repr'):
298-
# As soon as BuiltinImporter, FrozenImporter, and NamespaceLoader
299-
# drop their implementations for module_repr. we can add a
300-
# deprecation warning here.
297+
if spec := getattr(module, "__spec__", None):
298+
return _module_repr_from_spec(spec)
299+
elif hasattr(loader, 'module_repr'):
301300
try:
302301
return loader.module_repr(module)
303302
except Exception:
304303
pass
305-
try:
306-
spec = module.__spec__
307-
except AttributeError:
308-
pass
309-
else:
310-
if spec is not None:
311-
return _module_repr_from_spec(spec)
312-
304+
# Fall through to a catch-all which always succeeds.
313305
# We could use module.__class__.__name__ instead of 'module' in the
314306
# various repr permutations.
315307
try:

Lib/test/test_importlib/frozen/test_loader.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,14 +160,6 @@ def test_module_repr(self):
160160
self.assertEqual(repr_str,
161161
"<module '__hello__' (frozen)>")
162162

163-
def test_module_repr_indirect(self):
164-
with warnings.catch_warnings():
165-
warnings.simplefilter("ignore", DeprecationWarning)
166-
with util.uncache('__hello__'), captured_stdout():
167-
module = self.machinery.FrozenImporter.load_module('__hello__')
168-
self.assertEqual(repr(module),
169-
"<module '__hello__' (frozen)>")
170-
171163
# No way to trigger an error in a frozen module.
172164
test_state_after_failure = None
173165

Lib/test/test_importlib/test_namespace_pkgs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ def test_cant_import_other(self):
8282

8383
def test_module_repr(self):
8484
import foo.one
85-
self.assertEqual(repr(foo), "<module 'foo' (namespace)>")
85+
self.assertEqual(foo.__spec__.loader.module_repr(foo),
86+
"<module 'foo' (namespace)>")
8687

8788

8889
class DynamicPathNamespacePackage(NamespacePackageTest):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The import system now prefers using ``__spec__`` for ``ModuleType.__repr__``
2+
over ``module_repr()``.

0 commit comments

Comments
 (0)