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. |