Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions Lib/importlib/_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,17 +752,18 @@ def _init_module_attrs(spec, module, *, override=False):
loader = NamespaceLoader.__new__(NamespaceLoader)
loader._path = spec.submodule_search_locations
spec.loader = loader
# While the docs say that module.__file__ is not set for
# built-in modules, and the code below will avoid setting it if
# spec.has_location is false, this is incorrect for namespace
# packages. Namespace packages have no location, but their
# __spec__.origin is None, and thus their module.__file__
# should also be None for consistency. While a bit of a hack,
# this is the best place to ensure this consistency.
#
# See # https://docs.python.org/3/library/importlib.html#importlib.abc.Loader.load_module
# and bpo-32305
module.__file__ = None
if _bootstrap_external and isinstance(loader, _bootstrap_external.NamespaceLoader):
# While the docs say that module.__file__ is not set for
# built-in modules, and the code below will avoid setting it if
# spec.has_location is false, this is incorrect for namespace
# packages. Namespace packages have no location, but their
# __spec__.origin is None, and thus their module.__file__
# should also be None for consistency. While a bit of a hack,
# this is the best place to ensure this consistency.
#
# See # https://docs.python.org/3/library/importlib.html#importlib.abc.Loader.load_module
# and bpo-32305
module.__file__ = None
try:
module.__loader__ = loader
except AttributeError:
Expand Down
1 change: 1 addition & 0 deletions Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,7 @@ def find_spec(cls, fullname, path=None, target=None):
# can create the namespace package.
spec.origin = None
spec.submodule_search_locations = _NamespacePath(fullname, namespace_path, cls._get_spec)
spec.loader = NamespaceLoader(fullname, namespace_path, cls._get_spec)
return spec
else:
return None
Expand Down
6 changes: 5 additions & 1 deletion Lib/modulefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
_PKG_DIRECTORY = 5
_C_BUILTIN = 6
_PY_FROZEN = 7
_NAMESPACE = 8

# Modulefinder does a good job at simulating Python's, but it can not
# handle __path__ modifications packages make at runtime. Therefore there
Expand Down Expand Up @@ -66,7 +67,10 @@ def _find_module(name, path=None):

file_path = spec.origin

if spec.loader.is_package(name):
if isinstance(spec.loader, importlib.machinery.NamespaceLoader):
return None, spec.submodule_search_locations, ("", "", _NAMESPACE)

if spec.loader.is_package(name): # non-namespace package
return None, os.path.dirname(file_path), ("", "", _PKG_DIRECTORY)

if isinstance(spec.loader, importlib.machinery.SourceFileLoader):
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_modulefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@
from sys import version_info
"""]

namespace_package_test = [
"module",
["a", "module"],
["a.c", "blahblah"], [],
"""\
module.py
import a
import a.c
import blahblah
a/b.py
"""]

absolute_import_test = [
"a.module",
["a", "a.module",
Expand Down Expand Up @@ -353,6 +365,9 @@ def _do_test(self, info, report=False, debug=0, replace_paths=[], modulefinder_c
def test_package(self):
self._do_test(package_test)

def test_namespace_package(self):
self._do_test(namespace_package_test)

def test_maybe(self):
self._do_test(maybe_test)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix support for namespace packages in :mod:`modulefinder`.
Loading