diff --git a/importlib_resources/readers.py b/importlib_resources/readers.py index cad10e53..4a80a774 100644 --- a/importlib_resources/readers.py +++ b/importlib_resources/readers.py @@ -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('/')) diff --git a/importlib_resources/tests/data01/subdirectory/binary.file b/importlib_resources/tests/data01/subdirectory/binary.file index eaf36c1d..5bd8bb89 100644 Binary files a/importlib_resources/tests/data01/subdirectory/binary.file and b/importlib_resources/tests/data01/subdirectory/binary.file differ diff --git a/importlib_resources/tests/namespacedata01/subdirectory/binary.file b/importlib_resources/tests/namespacedata01/subdirectory/binary.file new file mode 100644 index 00000000..100f5064 --- /dev/null +++ b/importlib_resources/tests/namespacedata01/subdirectory/binary.file @@ -0,0 +1 @@ +  \ No newline at end of file diff --git a/importlib_resources/tests/test_contents.py b/importlib_resources/tests/test_contents.py index 525568e8..7dc3b0a6 100644 --- a/importlib_resources/tests/test_contents.py +++ b/importlib_resources/tests/test_contents.py @@ -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', } diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index 36267e9d..51ecc896 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -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() diff --git a/importlib_resources/tests/test_open.py b/importlib_resources/tests/test_open.py index 83b737dc..44f1018a 100644 --- a/importlib_resources/tests/test_open.py +++ b/importlib_resources/tests/test_open.py @@ -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' @@ -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() diff --git a/importlib_resources/tests/test_read.py b/importlib_resources/tests/test_read.py index 5b83221e..97d90128 100644 --- a/importlib_resources/tests/test_read.py +++ b/importlib_resources/tests/test_read.py @@ -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 = ( @@ -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): @@ -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() diff --git a/importlib_resources/tests/test_reader.py b/importlib_resources/tests/test_reader.py index a1eadb2c..95c2fc85 100644 --- a/importlib_resources/tests/test_reader.py +++ b/importlib_resources/tests/test_reader.py @@ -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') @@ -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'), diff --git a/importlib_resources/tests/test_resource.py b/importlib_resources/tests/test_resource.py index de7d734b..dc2a108c 100644 --- a/importlib_resources/tests/test_resource.py +++ b/importlib_resources/tests/test_resource.py @@ -186,7 +186,9 @@ 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')) @@ -194,7 +196,25 @@ def test_submodule_contents_by_name(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_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): diff --git a/newsfragments/293.bugfix.rst b/newsfragments/293.bugfix.rst new file mode 100644 index 00000000..e0ca5ecf --- /dev/null +++ b/newsfragments/293.bugfix.rst @@ -0,0 +1 @@ +Fixed NotADirectoryError when calling files on a subdirectory of a namespace package. \ No newline at end of file