Skip to content

Commit

Permalink
Merge pull request #293 from sfc-gh-wzhao/wzhao-fix-files-not-a-direc…
Browse files Browse the repository at this point in the history
…tory-when-reading-submodule-of-a-zipped-namespace-package

Fix NotADirectory error when calling files on a subdirectory of a zipped namespace package
  • Loading branch information
jaraco authored Feb 25, 2024
2 parents eec758b + 76145e2 commit 3f49de3
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 13 deletions.
5 changes: 4 additions & 1 deletion importlib_resources/readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@ def _candidate_paths(cls, path_str):
def _resolve_zip_path(path_str):
for match in reversed(list(re.finditer(r'[\\/]', path_str))):
with contextlib.suppress(
FileNotFoundError, IsADirectoryError, PermissionError
FileNotFoundError,
IsADirectoryError,
NotADirectoryError,
PermissionError,
):
inner = path_str[match.end() :].replace('\\', '/') + '/'
yield ZipPath(path_str[: match.start()], inner.lstrip('/'))
Expand Down
Binary file modified importlib_resources/tests/data01/subdirectory/binary.file
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Expand Down
2 changes: 1 addition & 1 deletion importlib_resources/tests/test_contents.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class ContentsZipTests(ContentsTests, util.ZipSetup, unittest.TestCase):
class ContentsNamespaceTests(ContentsTests, unittest.TestCase):
expected = {
# no __init__ because of namespace design
# no subdirectory as incidental difference in fixture
'binary.file',
'subdirectory',
'utf-16.file',
'utf-8.file',
}
Expand Down
4 changes: 4 additions & 0 deletions importlib_resources/tests/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ def setUp(self):
self.data = namespacedata01


class OpenNamespaceZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
ZIP_MODULE = 'namespacedata01'


class SiteDir:
def setUp(self):
self.fixtures = contextlib.ExitStack()
Expand Down
6 changes: 5 additions & 1 deletion importlib_resources/tests/test_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_open_binary(self):
target = resources.files(self.data) / 'binary.file'
with target.open('rb') as fp:
result = fp.read()
self.assertEqual(result, b'\x00\x01\x02\x03')
self.assertEqual(result, bytes(range(4)))

def test_open_text_default_encoding(self):
target = resources.files(self.data) / 'utf-8.file'
Expand Down Expand Up @@ -81,5 +81,9 @@ class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
pass


class OpenNamespaceZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
ZIP_MODULE = 'namespacedata01'


if __name__ == '__main__':
unittest.main()
23 changes: 20 additions & 3 deletions importlib_resources/tests/test_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def execute(self, package, path):
class ReadTests:
def test_read_bytes(self):
result = resources.files(self.data).joinpath('binary.file').read_bytes()
self.assertEqual(result, b'\0\1\2\3')
self.assertEqual(result, bytes(range(4)))

def test_read_text_default_encoding(self):
result = (
Expand Down Expand Up @@ -60,13 +60,13 @@ class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
def test_read_submodule_resource(self):
submodule = import_module('data01.subdirectory')
result = resources.files(submodule).joinpath('binary.file').read_bytes()
self.assertEqual(result, b'\0\1\2\3')
self.assertEqual(result, bytes(range(4, 8)))

def test_read_submodule_resource_by_name(self):
result = (
resources.files('data01.subdirectory').joinpath('binary.file').read_bytes()
)
self.assertEqual(result, b'\0\1\2\3')
self.assertEqual(result, bytes(range(4, 8)))


class ReadNamespaceTests(ReadTests, unittest.TestCase):
Expand All @@ -76,5 +76,22 @@ def setUp(self):
self.data = namespacedata01


class ReadNamespaceZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
ZIP_MODULE = 'namespacedata01'

def test_read_submodule_resource(self):
submodule = import_module('namespacedata01.subdirectory')
result = resources.files(submodule).joinpath('binary.file').read_bytes()
self.assertEqual(result, bytes(range(12, 16)))

def test_read_submodule_resource_by_name(self):
result = (
resources.files('namespacedata01.subdirectory')
.joinpath('binary.file')
.read_bytes()
)
self.assertEqual(result, bytes(range(12, 16)))


if __name__ == '__main__':
unittest.main()
12 changes: 7 additions & 5 deletions importlib_resources/tests/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ def test_iterdir(self):
contents.remove('__pycache__')
except (KeyError, ValueError):
pass
self.assertEqual(contents, {'binary.file', 'utf-16.file', 'utf-8.file'})
self.assertEqual(
contents, {'subdirectory', 'binary.file', 'utf-16.file', 'utf-8.file'}
)

def test_iterdir_duplicate(self):
data01 = pathlib.Path(__file__).parent.joinpath('data01')
Expand Down Expand Up @@ -66,10 +68,10 @@ def test_join_path(self):
str(path.joinpath('binary.file'))[len(prefix) + 1 :],
os.path.join('namespacedata01', 'binary.file'),
)
self.assertEqual(
str(path.joinpath('subdirectory'))[len(prefix) + 1 :],
os.path.join('data01', 'subdirectory'),
)
sub = path.joinpath('subdirectory')
assert isinstance(sub, MultiplexedPath)
assert 'namespacedata01' in str(sub)
assert 'data01' in str(sub)
self.assertEqual(
str(path.joinpath('imaginary'))[len(prefix) + 1 :],
os.path.join('namespacedata01', 'imaginary'),
Expand Down
24 changes: 22 additions & 2 deletions importlib_resources/tests/test_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,35 @@ def test_submodule_contents(self):
contents.remove('__pycache__')
except KeyError:
pass
self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
self.assertEqual(
contents, {'subdirectory', 'binary.file', 'utf-8.file', 'utf-16.file'}
)

def test_submodule_contents_by_name(self):
contents = names(resources.files('namespacedata01'))
try:
contents.remove('__pycache__')
except KeyError:
pass
self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
self.assertEqual(
contents, {'subdirectory', 'binary.file', 'utf-8.file', 'utf-16.file'}
)

def test_submodule_sub_contents(self):
contents = names(resources.files(import_module('namespacedata01.subdirectory')))
try:
contents.remove('__pycache__')
except KeyError:
pass
self.assertEqual(contents, {'binary.file'})

def test_submodule_sub_contents_by_name(self):
contents = names(resources.files('namespacedata01.subdirectory'))
try:
contents.remove('__pycache__')
except KeyError:
pass
self.assertEqual(contents, {'binary.file'})


class ResourceFromNamespaceDiskTests(ResourceFromNamespaceTests, unittest.TestCase):
Expand Down
1 change: 1 addition & 0 deletions newsfragments/293.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed NotADirectoryError when calling files on a subdirectory of a namespace package.

0 comments on commit 3f49de3

Please sign in to comment.