Skip to content

Commit 2de5097

Browse files
authored
bpo-26131: Deprecate usage of load_module() (GH-23469)
Raise an ImportWarning when the import system falls back on load_module(). As for implementations of load_module(), raise a DeprecationWarning.
1 parent 79c1849 commit 2de5097

25 files changed

Lines changed: 3244 additions & 3033 deletions

‎Doc/whatsnew/3.10.rst‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,8 @@ Optimizations
394394
with ``gcc`` by up to 30%. See `this article
395395
<https://developers.redhat.com/blog/2020/06/25/red-hat-enterprise-linux-8-2-brings-faster-python-3-8-run-speeds/>`_
396396
for more details. (Contributed by Victor Stinner and Pablo Galindo in
397-
:issue:`38980`)
397+
:issue:`38980`.)
398+
398399

399400
* Function parameters and their annotations are no longer computed at runtime,
400401
but rather at compilation time. They are stored as a tuple of strings at the
@@ -421,6 +422,21 @@ Deprecated
421422
as appropriate to help identify code which needs updating during
422423
this transition.
423424

425+
* The various ``load_module()`` methods of :mod:`importlib` have been
426+
documented as deprecated since Python 3.6, but will now also trigger
427+
a :exc:`DeprecationWarning`. Use
428+
:meth:`~importlib.abc.Loader.exec_module` instead.
429+
(Contributed by Brett Cannon in :issue:`26131`.)
430+
431+
* :meth:`zimport.zipimporter.load_module` has been deprecated in
432+
preference for :meth:`~zipimport.zipimporter.exec_module`.
433+
(Contributed by Brett Cannon in :issue:`26131`.)
434+
435+
* The use of :meth:`~importlib.abc.Loader.load_module` by the import
436+
system now triggers an :exc:`ImportWarning` as
437+
:meth:`~importlib.abc.Loader.exec_module` is preferred.
438+
(Contributed by Brett Cannon in :issue:`26131`.)
439+
424440
* ``sqlite3.OptimizedUnicode`` has been undocumented and obsolete since Python
425441
3.3, when it was made an alias to :class:`str`. It is now deprecated,
426442
scheduled for removal in Python 3.12.

‎Lib/importlib/_abc.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def load_module(self, fullname):
3535
"""
3636
ifnothasattr(self, 'exec_module'):
3737
raiseImportError
38+
# Warning implemented in _load_module_shim().
3839
return_bootstrap._load_module_shim(self, fullname)
3940

4041
defmodule_repr(self, module):

‎Lib/importlib/_bootstrap.py‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
# reference any injected objects! This includes not only global code but also
2121
# anything specified at the class level.
2222

23+
def_object_name(obj):
24+
try:
25+
returnobj.__qualname__
26+
exceptAttributeError:
27+
returntype(obj).__qualname__
28+
2329
# Bootstrap-related code ######################################################
2430

2531
# Modules injected manually by _setup()
@@ -272,6 +278,9 @@ def _load_module_shim(self, fullname):
272278
This method is deprecated. Use loader.exec_module instead.
273279
274280
"""
281+
msg= ("the load_module() method is deprecated and slated for removal in "
282+
"Python 3.12; use exec_module() instead")
283+
_warnings.warn(msg, DeprecationWarning)
275284
spec=spec_from_loader(fullname, self)
276285
iffullnameinsys.modules:
277286
module=sys.modules[fullname]
@@ -612,9 +621,9 @@ def _exec(spec, module):
612621
else:
613622
_init_module_attrs(spec, module, override=True)
614623
ifnothasattr(spec.loader, 'exec_module'):
615-
# (issue19713) Once BuiltinImporter and ExtensionFileLoader
616-
# have exec_module() implemented, we can add a deprecation
617-
# warning here.
624+
msg= (f"{_object_name(spec.loader)}.exec_module() not found; "
625+
"falling back to load_module()")
626+
_warnings.warn(msg, ImportWarning)
618627
spec.loader.load_module(name)
619628
else:
620629
spec.loader.exec_module(module)
@@ -627,9 +636,8 @@ def _exec(spec, module):
627636

628637

629638
def_load_backward_compatible(spec):
630-
# (issue19713) Once BuiltinImporter and ExtensionFileLoader
631-
# have exec_module() implemented, we can add a deprecation
632-
# warning here.
639+
# It is assumed that all callers have been warned about using load_module()
640+
# appropriately before calling this function.
633641
try:
634642
spec.loader.load_module(spec.name)
635643
except:
@@ -668,6 +676,9 @@ def _load_unlocked(spec):
668676
ifspec.loaderisnotNone:
669677
# Not a namespace package.
670678
ifnothasattr(spec.loader, 'exec_module'):
679+
msg= (f"{_object_name(spec.loader)}.exec_module() not found; "
680+
"falling back to load_module()")
681+
_warnings.warn(msg, ImportWarning)
671682
return_load_backward_compatible(spec)
672683

673684
module=module_from_spec(spec)
@@ -851,6 +862,7 @@ def load_module(cls, fullname):
851862
This method is deprecated. Use exec_module() instead.
852863
853864
"""
865+
# Warning about deprecation implemented in _load_module_shim().
854866
return_load_module_shim(cls, fullname)
855867

856868
@classmethod

‎Lib/importlib/_bootstrap_external.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,8 @@ def exec_module(self, module):
832832
_bootstrap._call_with_frames_removed(exec, code, module.__dict__)
833833

834834
defload_module(self, fullname):
835-
"""This module is deprecated."""
835+
"""This method is deprecated."""
836+
# Warning implemented in _load_module_shim().
836837
return_bootstrap._load_module_shim(self, fullname)
837838

838839

@@ -1007,7 +1008,7 @@ def load_module(self, fullname):
10071008
"""
10081009
# The only reason for this method is for the name check.
10091010
# Issue #14857: Avoid the zero-argument form of super so the implementation
1010-
# of that form can be updated without breaking the frozen module
1011+
# of that form can be updated without breaking the frozen module.
10111012
returnsuper(FileLoader, self).load_module(fullname)
10121013

10131014
@_check_name
@@ -1253,6 +1254,7 @@ def load_module(self, fullname):
12531254
# The import system never calls this method.
12541255
_bootstrap._verbose_message('namespace module loaded with path {!r}',
12551256
self._path)
1257+
# Warning implemented in _load_module_shim().
12561258
return_bootstrap._load_module_shim(self, fullname)
12571259

12581260

‎Lib/test/test_importlib/builtin/test_loader.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
importsys
77
importtypes
88
importunittest
9+
importwarnings
910

1011
@unittest.skipIf(util.BUILTINS.good_nameisNone, 'no reasonable builtin module')
1112
classLoaderTests(abc.LoaderTests):
@@ -24,7 +25,9 @@ def verify(self, module):
2425
self.assertIn(module.__name__, sys.modules)
2526

2627
defload_module(self, name):
27-
returnself.machinery.BuiltinImporter.load_module(name)
28+
withwarnings.catch_warnings():
29+
warnings.simplefilter("ignore", DeprecationWarning)
30+
returnself.machinery.BuiltinImporter.load_module(name)
2831

2932
deftest_module(self):
3033
# Common case.

‎Lib/test/test_importlib/extension/test_loader.py‎

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
fromwarningsimportcatch_warnings
12
from .. importabc
23
from .. importutil
34

@@ -7,6 +8,7 @@
78
importsys
89
importtypes
910
importunittest
11+
importwarnings
1012
importimportlib.util
1113
importimportlib
1214
fromtest.support.script_helperimportassert_python_failure
@@ -20,14 +22,18 @@ def setUp(self):
2022
util.EXTENSIONS.file_path)
2123

2224
defload_module(self, fullname):
23-
returnself.loader.load_module(fullname)
25+
withwarnings.catch_warnings():
26+
warnings.simplefilter("ignore", DeprecationWarning)
27+
returnself.loader.load_module(fullname)
2428

2529
deftest_load_module_API(self):
2630
# Test the default argument for load_module().
27-
self.loader.load_module()
28-
self.loader.load_module(None)
29-
withself.assertRaises(ImportError):
30-
self.load_module('XXX')
31+
withwarnings.catch_warnings():
32+
warnings.simplefilter("ignore", DeprecationWarning)
33+
self.loader.load_module()
34+
self.loader.load_module(None)
35+
withself.assertRaises(ImportError):
36+
self.load_module('XXX')
3137

3238
deftest_equality(self):
3339
other=self.machinery.ExtensionFileLoader(util.EXTENSIONS.name,
@@ -94,6 +100,21 @@ def setUp(self):
94100
self.loader=self.machinery.ExtensionFileLoader(
95101
self.name, self.spec.origin)
96102

103+
defload_module(self):
104+
'''Load the module from the test extension'''
105+
withwarnings.catch_warnings():
106+
warnings.simplefilter("ignore", DeprecationWarning)
107+
returnself.loader.load_module(self.name)
108+
109+
defload_module_by_name(self, fullname):
110+
'''Load a module from the test extension by name'''
111+
origin=self.spec.origin
112+
loader=self.machinery.ExtensionFileLoader(fullname, origin)
113+
spec=importlib.util.spec_from_loader(fullname, loader)
114+
module=importlib.util.module_from_spec(spec)
115+
loader.exec_module(module)
116+
returnmodule
117+
97118
# No extension module as __init__ available for testing.
98119
test_package=None
99120

@@ -157,19 +178,6 @@ def test_try_registration(self):
157178
withself.assertRaises(SystemError):
158179
module.call_state_registration_func(2)
159180

160-
defload_module(self):
161-
'''Load the module from the test extension'''
162-
returnself.loader.load_module(self.name)
163-
164-
defload_module_by_name(self, fullname):
165-
'''Load a module from the test extension by name'''
166-
origin=self.spec.origin
167-
loader=self.machinery.ExtensionFileLoader(fullname, origin)
168-
spec=importlib.util.spec_from_loader(fullname, loader)
169-
module=importlib.util.module_from_spec(spec)
170-
loader.exec_module(module)
171-
returnmodule
172-
173181
deftest_load_submodule(self):
174182
'''Test loading a simulated submodule'''
175183
module=self.load_module_by_name('pkg.'+self.name)

‎Lib/test/test_importlib/frozen/test_loader.py‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,23 @@ def test_module_repr(self):
161161
"<module '__hello__' (frozen)>")
162162

163163
deftest_module_repr_indirect(self):
164-
withutil.uncache('__hello__'), captured_stdout():
165-
module=self.machinery.FrozenImporter.load_module('__hello__')
166-
self.assertEqual(repr(module),
167-
"<module '__hello__' (frozen)>")
164+
withwarnings.catch_warnings():
165+
warnings.simplefilter("ignore", DeprecationWarning)
166+
withutil.uncache('__hello__'), captured_stdout():
167+
module=self.machinery.FrozenImporter.load_module('__hello__')
168+
self.assertEqual(repr(module),
169+
"<module '__hello__' (frozen)>")
168170

169171
# No way to trigger an error in a frozen module.
170172
test_state_after_failure=None
171173

172174
deftest_unloadable(self):
173-
assertself.machinery.FrozenImporter.find_module('_not_real') isNone
174-
withself.assertRaises(ImportError) ascm:
175-
self.machinery.FrozenImporter.load_module('_not_real')
176-
self.assertEqual(cm.exception.name, '_not_real')
175+
withwarnings.catch_warnings():
176+
warnings.simplefilter("ignore", DeprecationWarning)
177+
assertself.machinery.FrozenImporter.find_module('_not_real') isNone
178+
withself.assertRaises(ImportError) ascm:
179+
self.machinery.FrozenImporter.load_module('_not_real')
180+
self.assertEqual(cm.exception.name, '_not_real')
177181

178182

179183
(Frozen_LoaderTests,

‎Lib/test/test_importlib/import_/test___loader__.py‎

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
importsys
33
importtypes
44
importunittest
5+
importwarnings
56

67
from .. importutil
78

@@ -45,25 +46,29 @@ def load_module(self, fullname):
4546
classLoaderAttributeTests:
4647

4748
deftest___loader___missing(self):
48-
module=types.ModuleType('blah')
49-
try:
50-
delmodule.__loader__
51-
exceptAttributeError:
52-
pass
53-
loader=LoaderMock()
54-
loader.module=module
55-
withutil.uncache('blah'), util.import_state(meta_path=[loader]):
56-
module=self.__import__('blah')
57-
self.assertEqual(loader, module.__loader__)
49+
withwarnings.catch_warnings():
50+
warnings.simplefilter("ignore", ImportWarning)
51+
module=types.ModuleType('blah')
52+
try:
53+
delmodule.__loader__
54+
exceptAttributeError:
55+
pass
56+
loader=LoaderMock()
57+
loader.module=module
58+
withutil.uncache('blah'), util.import_state(meta_path=[loader]):
59+
module=self.__import__('blah')
60+
self.assertEqual(loader, module.__loader__)
5861

5962
deftest___loader___is_None(self):
60-
module=types.ModuleType('blah')
61-
module.__loader__=None
62-
loader=LoaderMock()
63-
loader.module=module
64-
withutil.uncache('blah'), util.import_state(meta_path=[loader]):
65-
returned_module=self.__import__('blah')
66-
self.assertEqual(loader, module.__loader__)
63+
withwarnings.catch_warnings():
64+
warnings.simplefilter("ignore", ImportWarning)
65+
module=types.ModuleType('blah')
66+
module.__loader__=None
67+
loader=LoaderMock()
68+
loader.module=module
69+
withutil.uncache('blah'), util.import_state(meta_path=[loader]):
70+
returned_module=self.__import__('blah')
71+
self.assertEqual(loader, module.__loader__)
6772

6873

6974
(Frozen_Tests,

‎Lib/test/test_importlib/import_/test___package__.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ def __init__(self, parent):
9898
classUsing__package__PEP302(Using__package__):
9999
mock_modules=util.mock_modules
100100

101+
deftest_using___package__(self):
102+
withwarnings.catch_warnings():
103+
warnings.simplefilter("ignore", ImportWarning)
104+
super().test_using___package__()
105+
106+
deftest_spec_fallback(self):
107+
withwarnings.catch_warnings():
108+
warnings.simplefilter("ignore", ImportWarning)
109+
super().test_spec_fallback()
110+
101111

102112
(Frozen_UsingPackagePEP302,
103113
Source_UsingPackagePEP302
@@ -155,6 +165,21 @@ def test_submodule(self):
155165
classSetting__package__PEP302(Setting__package__, unittest.TestCase):
156166
mock_modules=util.mock_modules
157167

168+
deftest_top_level(self):
169+
withwarnings.catch_warnings():
170+
warnings.simplefilter("ignore", ImportWarning)
171+
super().test_top_level()
172+
173+
deftest_package(self):
174+
withwarnings.catch_warnings():
175+
warnings.simplefilter("ignore", ImportWarning)
176+
super().test_package()
177+
178+
deftest_submodule(self):
179+
withwarnings.catch_warnings():
180+
warnings.simplefilter("ignore", ImportWarning)
181+
super().test_submodule()
182+
158183
classSetting__package__PEP451(Setting__package__, unittest.TestCase):
159184
mock_modules=util.mock_spec
160185

0 commit comments

Comments
 (0)