summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/importlib.rst5
-rw-r--r--Lib/importlib/util.py11
-rw-r--r--Lib/test/test_cmd_line_script.py2
-rw-r--r--Lib/test/test_importlib/test_util.py6
-rw-r--r--Misc/NEWS4
5 files changed, 24 insertions, 4 deletions
diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst
index 1df6130..45a02e5 100644
--- a/Doc/library/importlib.rst
+++ b/Doc/library/importlib.rst
@@ -1215,6 +1215,11 @@ an :term:`importer`.
.. versionadded:: 3.4
+ .. versionchanged:: 3.7
+ Raises :exc:`ModuleNotFoundError` instead of :exc:`AttributeError` if
+ **package** is in fact not a package (i.e. lacks a :attr:`__path__`
+ attribute).
+
.. function:: module_from_spec(spec)
Create a new module based on **spec** and
diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py
index 6bdf0d4..41c74d4 100644
--- a/Lib/importlib/util.py
+++ b/Lib/importlib/util.py
@@ -84,11 +84,16 @@ def find_spec(name, package=None):
if fullname not in sys.modules:
parent_name = fullname.rpartition('.')[0]
if parent_name:
- # Use builtins.__import__() in case someone replaced it.
parent = __import__(parent_name, fromlist=['__path__'])
- return _find_spec(fullname, parent.__path__)
+ try:
+ parent_path = parent.__path__
+ except AttributeError as e:
+ raise ModuleNotFoundError(
+ f"__path__ attribute not found on {parent_name!r}"
+ f"while trying to find {fullname!r}", name=fullname) from e
else:
- return _find_spec(fullname, None)
+ parent_path = None
+ return _find_spec(fullname, parent_path)
else:
module = sys.modules[fullname]
if module is None:
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index 1587daf..0d0bcd7 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -427,7 +427,7 @@ class CmdLineTest(unittest.TestCase):
tests = (
('builtins', br'No code object available'),
('builtins.x', br'Error while finding module specification.*'
- br'AttributeError'),
+ br'ModuleNotFoundError'),
('builtins.x.y', br'Error while finding module specification.*'
br'ModuleNotFoundError.*No module named.*not a package'),
('os.path', br'loader.*cannot handle'),
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
index ac18e5c..56a0b0e 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -522,6 +522,12 @@ class FindSpecTests:
self.assertNotIn(name, sorted(sys.modules))
self.assertNotIn(fullname, sorted(sys.modules))
+ def test_find_submodule_in_module(self):
+ # ModuleNotFoundError raised when a module is specified as
+ # a parent instead of a package.
+ with self.assertRaises(ModuleNotFoundError):
+ self.util.find_spec('module.name')
+
(Frozen_FindSpecTests,
Source_FindSpecTests
diff --git a/Misc/NEWS b/Misc/NEWS
index 31e8ec0..7452b25 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -441,6 +441,10 @@ Library
- bpo-30149: inspect.signature() now supports callables with
variable-argument parameters wrapped with partialmethod.
Patch by Dong-hee Na.
+
+- bpo-30436: importlib.find_spec() raises ModuleNotFoundError instead of
+ AttributeError if the specified parent module is not a package
+ (i.e. lacks a __path__ attribute).
- bpo-30301: Fix AttributeError when using SimpleQueue.empty() under
*spawn* and *forkserver* start methods.