Skip to content

Commit f97dc80

Browse files
authored
bpo-43672: raise ImportWarning when calling find_loader() (pythonGH-25119)
1 parent ad442a6 commit f97dc80

File tree

13 files changed

+612
-618
lines changed

13 files changed

+612
-618
lines changed

Doc/reference/import.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,8 @@ a list containing the portion.
904904
``find_loader()`` in preference to ``find_module()``.
905905

906906
.. versionchanged:: 3.10
907-
Calls to :meth:`~importlib.abc.PathEntryFinder.find_module` by the import
907+
Calls to :meth:`~importlib.abc.PathEntryFinder.find_module` and
908+
:meth:`~importlib.abc.PathEntryFinder.find_loader` by the import
908909
system will raise :exc:`ImportWarning`.
909910

910911

Doc/whatsnew/3.10.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1050,7 +1050,13 @@ Deprecated
10501050
:meth:`importlib.abc.PathEntryFinder.find_spec`
10511051
are preferred, respectively. You can use
10521052
:func:`importlib.util.spec_from_loader` to help in porting.
1053-
(Contributed by Brett Cannon in :issue:`42134`.)
1053+
(Contributed by Brett Cannon in :issue:`42134`.)
1054+
1055+
* The use of :meth:`importlib.abc.PathEntryFinder.find_loader` by the import
1056+
system now triggers an :exc:`ImportWarning` as
1057+
:meth:`importlib.abc.PathEntryFinder.find_spec` is preferred. You can use
1058+
:func:`importlib.util.spec_from_loader` to help in porting.
1059+
(Contributed by Brett Cannon in :issue:`43672`.)
10541060
10551061
* The import system now uses the ``__spec__`` attribute on modules before
10561062
falling back on :meth:`~importlib.abc.Loader.module_repr` for a module's

Lib/importlib/_bootstrap_external.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1323,10 +1323,13 @@ def _legacy_get_spec(cls, fullname, finder):
13231323
# This would be a good place for a DeprecationWarning if
13241324
# we ended up going that route.
13251325
if hasattr(finder, 'find_loader'):
1326+
msg = (f"{_bootstrap._object_name(finder)}.find_spec() not found; "
1327+
"falling back to find_loader()")
1328+
_warnings.warn(msg, ImportWarning)
13261329
loader, portions = finder.find_loader(fullname)
13271330
else:
13281331
msg = (f"{_bootstrap._object_name(finder)}.find_spec() not found; "
1329-
"falling back to find_module()")
1332+
"falling back to find_module()")
13301333
_warnings.warn(msg, ImportWarning)
13311334
loader = finder.find_module(fullname)
13321335
portions = []

Lib/test/test_importlib/extension/test_loader.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ def test_is_package(self):
8989
) = util.test_both(LoaderTests, machinery=machinery)
9090

9191
class MultiPhaseExtensionModuleTests(abc.LoaderTests):
92-
"""Test loading extension modules with multi-phase initialization (PEP 489)
93-
"""
92+
# Test loading extension modules with multi-phase initialization (PEP 489).
9493

9594
def setUp(self):
9695
self.name = '_testmultiphase'
@@ -101,13 +100,13 @@ def setUp(self):
101100
self.name, self.spec.origin)
102101

103102
def load_module(self):
104-
'''Load the module from the test extension'''
103+
# Load the module from the test extension.
105104
with warnings.catch_warnings():
106105
warnings.simplefilter("ignore", DeprecationWarning)
107106
return self.loader.load_module(self.name)
108107

109108
def load_module_by_name(self, fullname):
110-
'''Load a module from the test extension by name'''
109+
# Load a module from the test extension by name.
111110
origin = self.spec.origin
112111
loader = self.machinery.ExtensionFileLoader(fullname, origin)
113112
spec = importlib.util.spec_from_loader(fullname, loader)
@@ -125,7 +124,7 @@ def load_module_by_name(self, fullname):
125124
test_state_after_failure = None
126125

127126
def test_module(self):
128-
'''Test loading an extension module'''
127+
# Test loading an extension module.
129128
with util.uncache(self.name):
130129
module = self.load_module()
131130
for attr, value in [('__name__', self.name),
@@ -139,7 +138,7 @@ def test_module(self):
139138
self.machinery.ExtensionFileLoader)
140139

141140
def test_functionality(self):
142-
'''Test basic functionality of stuff defined in an extension module'''
141+
# Test basic functionality of stuff defined in an extension module.
143142
with util.uncache(self.name):
144143
module = self.load_module()
145144
self.assertIsInstance(module, types.ModuleType)
@@ -159,15 +158,15 @@ def test_functionality(self):
159158
self.assertEqual(module.str_const, 'something different')
160159

161160
def test_reload(self):
162-
'''Test that reload didn't re-set the module's attributes'''
161+
# Test that reload didn't re-set the module's attributes.
163162
with util.uncache(self.name):
164163
module = self.load_module()
165164
ex_class = module.Example
166165
importlib.reload(module)
167166
self.assertIs(ex_class, module.Example)
168167

169168
def test_try_registration(self):
170-
'''Assert that the PyState_{Find,Add,Remove}Module C API doesn't work'''
169+
# Assert that the PyState_{Find,Add,Remove}Module C API doesn't work.
171170
module = self.load_module()
172171
with self.subTest('PyState_FindModule'):
173172
self.assertEqual(module.call_state_registration_func(0), None)
@@ -179,65 +178,65 @@ def test_try_registration(self):
179178
module.call_state_registration_func(2)
180179

181180
def test_load_submodule(self):
182-
'''Test loading a simulated submodule'''
181+
# Test loading a simulated submodule.
183182
module = self.load_module_by_name('pkg.' + self.name)
184183
self.assertIsInstance(module, types.ModuleType)
185184
self.assertEqual(module.__name__, 'pkg.' + self.name)
186185
self.assertEqual(module.str_const, 'something different')
187186

188187
def test_load_short_name(self):
189-
'''Test loading module with a one-character name'''
188+
# Test loading module with a one-character name.
190189
module = self.load_module_by_name('x')
191190
self.assertIsInstance(module, types.ModuleType)
192191
self.assertEqual(module.__name__, 'x')
193192
self.assertEqual(module.str_const, 'something different')
194193
self.assertNotIn('x', sys.modules)
195194

196195
def test_load_twice(self):
197-
'''Test that 2 loads result in 2 module objects'''
196+
# Test that 2 loads result in 2 module objects.
198197
module1 = self.load_module_by_name(self.name)
199198
module2 = self.load_module_by_name(self.name)
200199
self.assertIsNot(module1, module2)
201200

202201
def test_unloadable(self):
203-
'''Test nonexistent module'''
202+
# Test nonexistent module.
204203
name = 'asdfjkl;'
205204
with self.assertRaises(ImportError) as cm:
206205
self.load_module_by_name(name)
207206
self.assertEqual(cm.exception.name, name)
208207

209208
def test_unloadable_nonascii(self):
210-
'''Test behavior with nonexistent module with non-ASCII name'''
209+
# Test behavior with nonexistent module with non-ASCII name.
211210
name = 'fo\xf3'
212211
with self.assertRaises(ImportError) as cm:
213212
self.load_module_by_name(name)
214213
self.assertEqual(cm.exception.name, name)
215214

216215
def test_nonmodule(self):
217-
'''Test returning a non-module object from create works'''
216+
# Test returning a non-module object from create works.
218217
name = self.name + '_nonmodule'
219218
mod = self.load_module_by_name(name)
220219
self.assertNotEqual(type(mod), type(unittest))
221220
self.assertEqual(mod.three, 3)
222221

223222
# issue 27782
224223
def test_nonmodule_with_methods(self):
225-
'''Test creating a non-module object with methods defined'''
224+
# Test creating a non-module object with methods defined.
226225
name = self.name + '_nonmodule_with_methods'
227226
mod = self.load_module_by_name(name)
228227
self.assertNotEqual(type(mod), type(unittest))
229228
self.assertEqual(mod.three, 3)
230229
self.assertEqual(mod.bar(10, 1), 9)
231230

232231
def test_null_slots(self):
233-
'''Test that NULL slots aren't a problem'''
232+
# Test that NULL slots aren't a problem.
234233
name = self.name + '_null_slots'
235234
module = self.load_module_by_name(name)
236235
self.assertIsInstance(module, types.ModuleType)
237236
self.assertEqual(module.__name__, name)
238237

239238
def test_bad_modules(self):
240-
'''Test SystemError is raised for misbehaving extensions'''
239+
# Test SystemError is raised for misbehaving extensions.
241240
for name_base in [
242241
'bad_slot_large',
243242
'bad_slot_negative',
@@ -261,9 +260,9 @@ def test_bad_modules(self):
261260
self.load_module_by_name(name)
262261

263262
def test_nonascii(self):
264-
'''Test that modules with non-ASCII names can be loaded'''
263+
# Test that modules with non-ASCII names can be loaded.
265264
# punycode behaves slightly differently in some-ASCII and no-ASCII
266-
# cases, so test both
265+
# cases, so test both.
267266
cases = [
268267
(self.name + '_zkou\u0161ka_na\u010dten\xed', 'Czech'),
269268
('\uff3f\u30a4\u30f3\u30dd\u30fc\u30c8\u30c6\u30b9\u30c8',

Lib/test/test_importlib/import_/test_path.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,16 @@ def find_loader(self, fullname):
143143
return self.loader, self.portions
144144
path = 'testing path'
145145
with util.import_state(path_importer_cache={path: TestFinder()}):
146-
self.assertIsNone(
146+
with warnings.catch_warnings():
147+
warnings.simplefilter("ignore", ImportWarning)
148+
self.assertIsNone(
147149
self.machinery.PathFinder.find_spec('whatever', [path]))
148150
success_finder = TestFinder()
149151
success_finder.loader = __loader__
150152
with util.import_state(path_importer_cache={path: success_finder}):
151-
spec = self.machinery.PathFinder.find_spec('whatever', [path])
153+
with warnings.catch_warnings():
154+
warnings.simplefilter("ignore", ImportWarning)
155+
spec = self.machinery.PathFinder.find_spec('whatever', [path])
152156
self.assertEqual(spec.loader, __loader__)
153157

154158
def test_finder_with_find_spec(self):

Lib/test/test_importlib/test_abc.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,13 @@ def test_load_module(self):
221221
def test_module_repr(self):
222222
mod = types.ModuleType('blah')
223223
with warnings.catch_warnings():
224-
warnings.simplefilter("ignore")
224+
warnings.simplefilter("ignore", DeprecationWarning)
225225
with self.assertRaises(NotImplementedError):
226226
self.ins.module_repr(mod)
227-
original_repr = repr(mod)
228-
mod.__loader__ = self.ins
229-
# Should still return a proper repr.
230-
self.assertTrue(repr(mod))
227+
original_repr = repr(mod)
228+
mod.__loader__ = self.ins
229+
# Should still return a proper repr.
230+
self.assertTrue(repr(mod))
231231

232232

233233
(Frozen_LDefaultTests,

Lib/test/test_importlib/test_main.py

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,10 @@ def test_for_name_does_not_exist(self):
3636
Distribution.from_name('does-not-exist')
3737

3838
def test_package_not_found_mentions_metadata(self):
39-
"""
40-
When a package is not found, that could indicate that the
41-
packgae is not installed or that it is installed without
42-
metadata. Ensure the exception mentions metadata to help
43-
guide users toward the cause. See #124.
44-
"""
39+
# When a package is not found, that could indicate that the
40+
# packgae is not installed or that it is installed without
41+
# metadata. Ensure the exception mentions metadata to help
42+
# guide users toward the cause. See #124.
4543
with self.assertRaises(PackageNotFoundError) as ctx:
4644
Distribution.from_name('does-not-exist')
4745

@@ -90,10 +88,8 @@ def pkg_with_dashes(site_dir):
9088
return 'my-pkg'
9189

9290
def test_dashes_in_dist_name_found_as_underscores(self):
93-
"""
94-
For a package with a dash in the name, the dist-info metadata
95-
uses underscores in the name. Ensure the metadata loads.
96-
"""
91+
# For a package with a dash in the name, the dist-info metadata
92+
# uses underscores in the name. Ensure the metadata loads.
9793
pkg_name = self.pkg_with_dashes(self.site_dir)
9894
assert version(pkg_name) == '1.0'
9995

@@ -111,9 +107,7 @@ def pkg_with_mixed_case(site_dir):
111107
return 'CherryPy'
112108

113109
def test_dist_name_found_as_any_case(self):
114-
"""
115-
Ensure the metadata loads when queried with any case.
116-
"""
110+
# Ensure the metadata loads when queried with any case.
117111
pkg_name = self.pkg_with_mixed_case(self.site_dir)
118112
assert version(pkg_name) == '1.0'
119113
assert version(pkg_name.lower()) == '1.0'
@@ -241,13 +235,11 @@ def test_repr(self):
241235
assert "'name'" in repr(self.ep)
242236

243237
def test_hashable(self):
244-
"""EntryPoints should be hashable"""
238+
# EntryPoints should be hashable.
245239
hash(self.ep)
246240

247241
def test_json_dump(self):
248-
"""
249-
json should not expect to be able to dump an EntryPoint
250-
"""
242+
# json should not expect to be able to dump an EntryPoint.
251243
with self.assertRaises(Exception):
252244
with warnings.catch_warnings(record=True):
253245
json.dumps(self.ep)
@@ -259,9 +251,7 @@ def test_attr(self):
259251
assert self.ep.attr is None
260252

261253
def test_sortable(self):
262-
"""
263-
EntryPoint objects are sortable, but result is undefined.
264-
"""
254+
# EntryPoint objects are sortable, but result is undefined.
265255
sorted(
266256
[
267257
EntryPoint('b', 'val', 'group'),
@@ -274,10 +264,8 @@ class FileSystem(
274264
fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, unittest.TestCase
275265
):
276266
def test_unicode_dir_on_sys_path(self):
277-
"""
278-
Ensure a Unicode subdirectory of a directory on sys.path
279-
does not crash.
280-
"""
267+
# Ensure a Unicode subdirectory of a directory on sys.path
268+
# does not crash.
281269
fixtures.build_files(
282270
{self.unicode_filename(): {}},
283271
prefix=self.site_dir,

Lib/test/test_importlib/test_metadata_api.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,8 @@ def test_entry_points_distribution(self):
8181
self.assertEqual(ep.dist.version, "1.0.0")
8282

8383
def test_entry_points_unique_packages(self):
84-
"""
85-
Entry points should only be exposed for the first package
86-
on sys.path with a given name.
87-
"""
84+
# Entry points should only be exposed for the first package
85+
# on sys.path with a given name.
8886
alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
8987
self.fixtures.enter_context(self.add_sys_path(alt_site_dir))
9088
alt_pkg = {
@@ -116,11 +114,9 @@ def test_entry_points_missing_group(self):
116114
assert entry_points(group='missing') == ()
117115

118116
def test_entry_points_dict_construction(self):
119-
"""
120-
Prior versions of entry_points() returned simple lists and
121-
allowed casting those lists into maps by name using ``dict()``.
122-
Capture this now deprecated use-case.
123-
"""
117+
# Prior versions of entry_points() returned simple lists and
118+
# allowed casting those lists into maps by name using ``dict()``.
119+
# Capture this now deprecated use-case.
124120
with warnings.catch_warnings(record=True) as caught:
125121
warnings.filterwarnings("default", category=DeprecationWarning)
126122
eps = dict(entry_points(group='entries'))
@@ -134,23 +130,19 @@ def test_entry_points_dict_construction(self):
134130
assert "Construction of dict of EntryPoints is deprecated" in str(expected)
135131

136132
def test_entry_points_groups_getitem(self):
137-
"""
138-
Prior versions of entry_points() returned a dict. Ensure
139-
that callers using '.__getitem__()' are supported but warned to
140-
migrate.
141-
"""
133+
# Prior versions of entry_points() returned a dict. Ensure
134+
# that callers using '.__getitem__()' are supported but warned to
135+
# migrate.
142136
with warnings.catch_warnings(record=True):
143137
entry_points()['entries'] == entry_points(group='entries')
144138

145139
with self.assertRaises(KeyError):
146140
entry_points()['missing']
147141

148142
def test_entry_points_groups_get(self):
149-
"""
150-
Prior versions of entry_points() returned a dict. Ensure
151-
that callers using '.get()' are supported but warned to
152-
migrate.
153-
"""
143+
# Prior versions of entry_points() returned a dict. Ensure
144+
# that callers using '.get()' are supported but warned to
145+
# migrate.
154146
with warnings.catch_warnings(record=True):
155147
entry_points().get('missing', 'default') == 'default'
156148
entry_points().get('entries', 'default') == entry_points()['entries']
@@ -259,7 +251,7 @@ def test_find_distributions_specified_path(self):
259251
assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists)
260252

261253
def test_distribution_at_pathlib(self):
262-
"""Demonstrate how to load metadata direct from a directory."""
254+
# Demonstrate how to load metadata direct from a directory.
263255
dist_info_path = self.site_dir / 'distinfo_pkg-1.0.0.dist-info'
264256
dist = Distribution.at(dist_info_path)
265257
assert dist.version == '1.0.0'

Lib/test/test_importlib/test_path.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,9 @@ class PathDiskTests(PathTests, unittest.TestCase):
2929
data = data01
3030

3131
def test_natural_path(self):
32-
"""
33-
Guarantee the internal implementation detail that
34-
file-system-backed resources do not get the tempdir
35-
treatment.
36-
"""
32+
# Guarantee the internal implementation detail that
33+
# file-system-backed resources do not get the tempdir
34+
# treatment.
3735
with resources.path(self.data, 'utf-8.file') as path:
3836
assert 'data' in str(path)
3937

Lib/test/test_importlib/test_reader.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ def test_open_file(self):
6060
path.open()
6161

6262
def test_join_path(self):
63-
print('test_join_path')
6463
prefix = os.path.abspath(os.path.join(__file__, '..'))
6564
data01 = os.path.join(prefix, 'data01')
6665
path = MultiplexedPath(self.folder, data01)

0 commit comments

Comments
 (0)