Skip to content

gh-93334: Fix homonym edge case in PathFinder.find_spec() #98100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
10 changes: 9 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_importlib/namespace_pkgs/foo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This directory should not be a package, but should share a name with an
unrelated subpackage.
11 changes: 11 additions & 0 deletions Lib/test/test_importlib/test_namespace_pkgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']

Expand Down
1 change: 1 addition & 0 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
Original file line number Diff line number Diff line change
@@ -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).
Loading