diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 1b76328429f63a..4a441a2ad43dfa 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1105,7 +1105,15 @@ def _find_parent_path_names(self): def _get_parent_path(self): parent_module_name, path_attr_name = self._find_parent_path_names() - return getattr(sys.modules[parent_module_name], path_attr_name) + try: + module = sys.modules[parent_module_name] + except KeyError as e: + raise ModuleNotFoundError( + f"{parent_module_name!r} must be imported before finding {self._name!r}.", + name=parent_module_name, + ) from e + else: + return getattr(module, path_attr_name) def _recalculate(self): # If the parent's path has changed, recalculate _path diff --git a/Lib/test/test_importlib/namespace_pkgs/foo/README.md b/Lib/test/test_importlib/namespace_pkgs/foo/README.md new file mode 100644 index 00000000000000..9f6bc74941d0ce --- /dev/null +++ b/Lib/test/test_importlib/namespace_pkgs/foo/README.md @@ -0,0 +1,2 @@ +This directory should not be a package, but should share a name with an +unrelated subpackage. diff --git a/Lib/test/test_importlib/test_namespace_pkgs.py b/Lib/test/test_importlib/test_namespace_pkgs.py index cbbdada3b010a7..a4bb27fa1a383e 100644 --- a/Lib/test/test_importlib/test_namespace_pkgs.py +++ b/Lib/test/test_importlib/test_namespace_pkgs.py @@ -318,6 +318,17 @@ def test_module_before_namespace_package(self): self.assertEqual(a_test.attr, 'in module') +class NamespaceSubpackageSameName(NamespacePackageTest): + paths = [''] + + def test_namespace_subpackage_shares_name_with_directory(self): + submodule_path = 'project4.foo' + with self.assertRaises(ModuleNotFoundError) as cm: + importlib.machinery.PathFinder.find_spec(submodule_path) + + self.assertEqual(cm.exception.name, 'project4') + + class ReloadTests(NamespacePackageTest): paths = ['portion1'] diff --git a/Makefile.pre.in b/Makefile.pre.in index 579b3fddabc7c3..f53fe69679ec38 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2470,6 +2470,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_importlib/namespace_pkgs \ test/test_importlib/namespace_pkgs/both_portions \ test/test_importlib/namespace_pkgs/both_portions/foo \ + test/test_importlib/namespace_pkgs/foo \ test/test_importlib/namespace_pkgs/module_and_namespace_package \ test/test_importlib/namespace_pkgs/module_and_namespace_package/a_test \ test/test_importlib/namespace_pkgs/not_a_namespace_pkg \ diff --git a/Misc/NEWS.d/next/Library/2022-10-08-14-56-07.gh-issue-93334.0KUm8d.rst b/Misc/NEWS.d/next/Library/2022-10-08-14-56-07.gh-issue-93334.0KUm8d.rst new file mode 100644 index 00000000000000..c3a3b7a6812252 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-08-14-56-07.gh-issue-93334.0KUm8d.rst @@ -0,0 +1,3 @@ +Reraise :exc:`KeyError` as :exc:`ModuleNotFoundError` when +:meth:`importlib.machinery.PathFinder.find_spec` is called on a submodule +without importing the parent (and without a ``path`` argument).