Skip to content

Commit

Permalink
Merge pull request #315 from python/bugfix/311-non-path-namespace-paths
Browse files Browse the repository at this point in the history
Omit non-dir items when constructing a NamespaceReader
  • Loading branch information
jaraco authored Sep 9, 2024
2 parents 4875bc5 + 2c145c5 commit 63a7bcb
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 4 deletions.
12 changes: 8 additions & 4 deletions importlib_resources/readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,19 +138,23 @@ class NamespaceReader(abc.TraversableResources):
def __init__(self, namespace_path):
if 'NamespacePath' not in str(namespace_path):
raise ValueError('Invalid path')
self.path = MultiplexedPath(*map(self._resolve, namespace_path))
self.path = MultiplexedPath(*filter(bool, map(self._resolve, namespace_path)))

@classmethod
def _resolve(cls, path_str) -> abc.Traversable:
def _resolve(cls, path_str) -> abc.Traversable | None:
r"""
Given an item from a namespace path, resolve it to a Traversable.
path_str might be a directory on the filesystem or a path to a
zipfile plus the path within the zipfile, e.g. ``/foo/bar`` or
``/foo/baz.zip/inner_dir`` or ``foo\baz.zip\inner_dir\sub``.
path_str might also be a sentinel used by editable packages to
trigger other behaviors (see python/importlib_resources#311).
In that case, return None.
"""
(dir,) = (cand for cand in cls._candidate_paths(path_str) if cand.is_dir())
return dir
dirs = (cand for cand in cls._candidate_paths(path_str) if cand.is_dir())
return next(dirs, None)

@classmethod
def _candidate_paths(cls, path_str: str) -> Iterator[abc.Traversable]:
Expand Down
20 changes: 20 additions & 0 deletions importlib_resources/tests/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,26 @@ class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
class OpenNamespaceTests(FilesTests, util.DiskSetup, unittest.TestCase):
MODULE = 'namespacedata01'

def test_non_paths_in_dunder_path(self):
"""
Non-path items in a namespace package's ``__path__`` are ignored.
As reported in python/importlib_resources#311, some tools
like Setuptools, when creating editable packages, will inject
non-paths into a namespace package's ``__path__``, a
sentinel like
``__editable__.sample_namespace-1.0.finder.__path_hook__``
to cause the ``PathEntryFinder`` to be called when searching
for packages. In that case, resources should still be loadable.
"""
import namespacedata01

namespacedata01.__path__.append(
'__editable__.sample_namespace-1.0.finder.__path_hook__'
)

resources.files(namespacedata01)


class OpenNamespaceZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
ZIP_MODULE = 'namespacedata01'
Expand Down
1 change: 1 addition & 0 deletions newsfragments/311.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Omit sentinel values from a namespace path.

0 comments on commit 63a7bcb

Please sign in to comment.