bpo-30436: Raise ModuleNotFoundError for importlib.util.find_spec() w… · python/cpython@8c3f05e (original) (raw)

5 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -1215,6 +1215,11 @@ an :term:`importer`.
1215 1215
1216 1216 .. versionadded:: 3.4
1217 1217
1218 + .. versionchanged:: 3.7
1219 + Raises :exc:`ModuleNotFoundError` instead of :exc:`AttributeError` if
1220 + **package** is in fact not a package (i.e. lacks a :attr:`__path__`
1221 + attribute).
1222 +
1218 1223 .. function:: module_from_spec(spec)
1219 1224
1220 1225 Create a new module based on **spec** and
Original file line number Diff line number Diff line change
@@ -84,11 +84,16 @@ def find_spec(name, package=None):
84 84 if fullname not in sys.modules:
85 85 parent_name = fullname.rpartition('.')[0]
86 86 if parent_name:
87 -# Use builtins.__import__() in case someone replaced it.
88 87 parent = __import__(parent_name, fromlist=['__path__'])
89 -return _find_spec(fullname, parent.__path__)
88 +try:
89 +parent_path = parent.__path__
90 +except AttributeError as e:
91 +raise ModuleNotFoundError(
92 +f"__path__ attribute not found on {parent_name!r}"
93 +f"while trying to find {fullname!r}", name=fullname) from e
90 94 else:
91 -return _find_spec(fullname, None)
95 +parent_path = None
96 +return _find_spec(fullname, parent_path)
92 97 else:
93 98 module = sys.modules[fullname]
94 99 if module is None:
Original file line number Diff line number Diff line change
@@ -427,7 +427,7 @@ def test_dash_m_errors(self):
427 427 tests = (
428 428 ('builtins', br'No code object available'),
429 429 ('builtins.x', br'Error while finding module specification.*'
430 -br'AttributeError'),
430 +br'ModuleNotFoundError'),
431 431 ('builtins.x.y', br'Error while finding module specification.*'
432 432 br'ModuleNotFoundError.*No module named.*not a package'),
433 433 ('os.path', br'loader.*cannot handle'),
Original file line number Diff line number Diff line change
@@ -522,6 +522,12 @@ def test_find_relative_module_missing_package(self):
522 522 self.assertNotIn(name, sorted(sys.modules))
523 523 self.assertNotIn(fullname, sorted(sys.modules))
524 524
525 +def test_find_submodule_in_module(self):
526 +# ModuleNotFoundError raised when a module is specified as
527 +# a parent instead of a package.
528 +with self.assertRaises(ModuleNotFoundError):
529 +self.util.find_spec('module.name')
530 +
525 531
526 532 (Frozen_FindSpecTests,
527 533 Source_FindSpecTests
Original file line number Diff line number Diff line change
@@ -441,6 +441,10 @@ Library
441 441 - bpo-30149: inspect.signature() now supports callables with
442 442 variable-argument parameters wrapped with partialmethod.
443 443 Patch by Dong-hee Na.
444 +
445 +- bpo-30436: importlib.find_spec() raises ModuleNotFoundError instead of
446 + AttributeError if the specified parent module is not a package
447 + (i.e. lacks a __path__ attribute).
444 448
445 449 - bpo-30301: Fix AttributeError when using SimpleQueue.empty() under
446 450 *spawn* and *forkserver* start methods.