summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes2
-rw-r--r--Lib/importlib/resources/readers.py6
-rw-r--r--Lib/test/test_importlib/resources/data01/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/data01/binary.filebin4 -> 0 bytes
-rw-r--r--Lib/test/test_importlib/resources/data01/subdirectory/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/data01/subdirectory/binary.file1
-rw-r--r--Lib/test/test_importlib/resources/data01/utf-16.filebin44 -> 0 bytes
-rw-r--r--Lib/test/test_importlib/resources/data01/utf-8.file1
-rw-r--r--Lib/test/test_importlib/resources/data02/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/data02/one/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/data02/one/resource1.txt1
-rw-r--r--Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt1
-rw-r--r--Lib/test/test_importlib/resources/data02/two/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/data02/two/resource2.txt1
-rw-r--r--Lib/test/test_importlib/resources/data03/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/data03/namespace/portion1/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/data03/namespace/portion2/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/data03/namespace/resource1.txt0
-rw-r--r--Lib/test/test_importlib/resources/namespacedata01/binary.filebin4 -> 0 bytes
-rw-r--r--Lib/test/test_importlib/resources/namespacedata01/subdirectory/binary.file1
-rw-r--r--Lib/test/test_importlib/resources/namespacedata01/utf-16.filebin44 -> 0 bytes
-rw-r--r--Lib/test/test_importlib/resources/namespacedata01/utf-8.file1
-rw-r--r--Lib/test/test_importlib/resources/test_contents.py15
-rw-r--r--Lib/test/test_importlib/resources/test_files.py102
-rw-r--r--Lib/test/test_importlib/resources/test_functional.py30
-rw-r--r--Lib/test/test_importlib/resources/test_open.py15
-rw-r--r--Lib/test/test_importlib/resources/test_path.py5
-rw-r--r--Lib/test/test_importlib/resources/test_read.py15
-rw-r--r--Lib/test/test_importlib/resources/test_reader.py48
-rw-r--r--Lib/test/test_importlib/resources/test_resource.py58
-rwxr-xr-xLib/test/test_importlib/resources/update-zips.py53
-rw-r--r--Lib/test/test_importlib/resources/util.py72
-rwxr-xr-xLib/test/test_importlib/resources/zip.py26
-rw-r--r--Lib/test/test_importlib/resources/zipdata01/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/zipdata01/ziptestdata.zipbin876 -> 0 bytes
-rw-r--r--Lib/test/test_importlib/resources/zipdata02/__init__.py0
-rw-r--r--Lib/test/test_importlib/resources/zipdata02/ziptestdata.zipbin698 -> 0 bytes
-rw-r--r--Lib/zipimport.py12
-rw-r--r--Makefile.pre.in15
-rw-r--r--Misc/NEWS.d/next/Library/2024-08-15-09-45-34.gh-issue-121735._1q0qf.rst3
40 files changed, 223 insertions, 261 deletions
diff --git a/.gitattributes b/.gitattributes
index 5b81d2c..2f5a030 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -27,8 +27,6 @@ Lib/test/cjkencodings/* noeol
Lib/test/tokenizedata/coding20731.py noeol
Lib/test/decimaltestdata/*.decTest noeol
Lib/test/test_email/data/*.txt noeol
-Lib/test/test_importlib/resources/data01/* noeol
-Lib/test/test_importlib/resources/namespacedata01/* noeol
Lib/test/xmltestdata/* noeol
# Shell scripts should have LF even on Windows because of Cygwin
diff --git a/Lib/importlib/resources/readers.py b/Lib/importlib/resources/readers.py
index b86cdef..ccc5abb 100644
--- a/Lib/importlib/resources/readers.py
+++ b/Lib/importlib/resources/readers.py
@@ -34,8 +34,10 @@ class FileReader(abc.TraversableResources):
class ZipReader(abc.TraversableResources):
def __init__(self, loader, module):
- _, _, name = module.rpartition('.')
- self.prefix = loader.prefix.replace('\\', '/') + name + '/'
+ self.prefix = loader.prefix.replace('\\', '/')
+ if loader.is_package(module):
+ _, _, name = module.rpartition('.')
+ self.prefix += name + '/'
self.archive = loader.archive
def open_resource(self, resource):
diff --git a/Lib/test/test_importlib/resources/data01/__init__.py b/Lib/test/test_importlib/resources/data01/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data01/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/data01/binary.file b/Lib/test/test_importlib/resources/data01/binary.file
deleted file mode 100644
index eaf36c1..0000000
--- a/Lib/test/test_importlib/resources/data01/binary.file
+++ /dev/null
Binary files differ
diff --git a/Lib/test/test_importlib/resources/data01/subdirectory/__init__.py b/Lib/test/test_importlib/resources/data01/subdirectory/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data01/subdirectory/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/data01/subdirectory/binary.file b/Lib/test/test_importlib/resources/data01/subdirectory/binary.file
deleted file mode 100644
index 5bd8bb8..0000000
--- a/Lib/test/test_importlib/resources/data01/subdirectory/binary.file
+++ /dev/null
@@ -1 +0,0 @@
- \ No newline at end of file
diff --git a/Lib/test/test_importlib/resources/data01/utf-16.file b/Lib/test/test_importlib/resources/data01/utf-16.file
deleted file mode 100644
index 2cb7722..0000000
--- a/Lib/test/test_importlib/resources/data01/utf-16.file
+++ /dev/null
Binary files differ
diff --git a/Lib/test/test_importlib/resources/data01/utf-8.file b/Lib/test/test_importlib/resources/data01/utf-8.file
deleted file mode 100644
index 1c0132a..0000000
--- a/Lib/test/test_importlib/resources/data01/utf-8.file
+++ /dev/null
@@ -1 +0,0 @@
-Hello, UTF-8 world!
diff --git a/Lib/test/test_importlib/resources/data02/__init__.py b/Lib/test/test_importlib/resources/data02/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data02/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/data02/one/__init__.py b/Lib/test/test_importlib/resources/data02/one/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data02/one/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/data02/one/resource1.txt b/Lib/test/test_importlib/resources/data02/one/resource1.txt
deleted file mode 100644
index 61a813e..0000000
--- a/Lib/test/test_importlib/resources/data02/one/resource1.txt
+++ /dev/null
@@ -1 +0,0 @@
-one resource
diff --git a/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt b/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt
deleted file mode 100644
index 48f587a..0000000
--- a/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt
+++ /dev/null
@@ -1 +0,0 @@
-a resource \ No newline at end of file
diff --git a/Lib/test/test_importlib/resources/data02/two/__init__.py b/Lib/test/test_importlib/resources/data02/two/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data02/two/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/data02/two/resource2.txt b/Lib/test/test_importlib/resources/data02/two/resource2.txt
deleted file mode 100644
index a80ce46..0000000
--- a/Lib/test/test_importlib/resources/data02/two/resource2.txt
+++ /dev/null
@@ -1 +0,0 @@
-two resource
diff --git a/Lib/test/test_importlib/resources/data03/__init__.py b/Lib/test/test_importlib/resources/data03/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data03/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/data03/namespace/portion1/__init__.py b/Lib/test/test_importlib/resources/data03/namespace/portion1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data03/namespace/portion1/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/data03/namespace/portion2/__init__.py b/Lib/test/test_importlib/resources/data03/namespace/portion2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data03/namespace/portion2/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/data03/namespace/resource1.txt b/Lib/test/test_importlib/resources/data03/namespace/resource1.txt
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/data03/namespace/resource1.txt
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/namespacedata01/binary.file b/Lib/test/test_importlib/resources/namespacedata01/binary.file
deleted file mode 100644
index eaf36c1..0000000
--- a/Lib/test/test_importlib/resources/namespacedata01/binary.file
+++ /dev/null
Binary files differ
diff --git a/Lib/test/test_importlib/resources/namespacedata01/subdirectory/binary.file b/Lib/test/test_importlib/resources/namespacedata01/subdirectory/binary.file
deleted file mode 100644
index 100f506..0000000
--- a/Lib/test/test_importlib/resources/namespacedata01/subdirectory/binary.file
+++ /dev/null
@@ -1 +0,0 @@
-  \ No newline at end of file
diff --git a/Lib/test/test_importlib/resources/namespacedata01/utf-16.file b/Lib/test/test_importlib/resources/namespacedata01/utf-16.file
deleted file mode 100644
index 2cb7722..0000000
--- a/Lib/test/test_importlib/resources/namespacedata01/utf-16.file
+++ /dev/null
Binary files differ
diff --git a/Lib/test/test_importlib/resources/namespacedata01/utf-8.file b/Lib/test/test_importlib/resources/namespacedata01/utf-8.file
deleted file mode 100644
index 1c0132a..0000000
--- a/Lib/test/test_importlib/resources/namespacedata01/utf-8.file
+++ /dev/null
@@ -1 +0,0 @@
-Hello, UTF-8 world!
diff --git a/Lib/test/test_importlib/resources/test_contents.py b/Lib/test/test_importlib/resources/test_contents.py
index beab67c..4e4e0e9 100644
--- a/Lib/test/test_importlib/resources/test_contents.py
+++ b/Lib/test/test_importlib/resources/test_contents.py
@@ -1,7 +1,6 @@
import unittest
from importlib import resources
-from . import data01
from . import util
@@ -19,16 +18,17 @@ class ContentsTests:
assert self.expected <= contents
-class ContentsDiskTests(ContentsTests, unittest.TestCase):
- def setUp(self):
- self.data = data01
+class ContentsDiskTests(ContentsTests, util.DiskSetup, unittest.TestCase):
+ pass
class ContentsZipTests(ContentsTests, util.ZipSetup, unittest.TestCase):
pass
-class ContentsNamespaceTests(ContentsTests, unittest.TestCase):
+class ContentsNamespaceTests(ContentsTests, util.DiskSetup, unittest.TestCase):
+ MODULE = 'namespacedata01'
+
expected = {
# no __init__ because of namespace design
'binary.file',
@@ -36,8 +36,3 @@ class ContentsNamespaceTests(ContentsTests, unittest.TestCase):
'utf-16.file',
'utf-8.file',
}
-
- def setUp(self):
- from . import namespacedata01
-
- self.data = namespacedata01
diff --git a/Lib/test/test_importlib/resources/test_files.py b/Lib/test/test_importlib/resources/test_files.py
index 7df6d03..08b8408 100644
--- a/Lib/test/test_importlib/resources/test_files.py
+++ b/Lib/test/test_importlib/resources/test_files.py
@@ -6,11 +6,7 @@ import contextlib
from importlib import resources
from importlib.resources.abc import Traversable
-from . import data01
from . import util
-from . import _path
-from test.support import os_helper
-from test.support import import_helper
@contextlib.contextmanager
@@ -48,70 +44,96 @@ class FilesTests:
resources.files(package=self.data)
-class OpenDiskTests(FilesTests, unittest.TestCase):
- def setUp(self):
- self.data = data01
+class OpenDiskTests(FilesTests, util.DiskSetup, unittest.TestCase):
+ pass
class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
pass
-class OpenNamespaceTests(FilesTests, unittest.TestCase):
- def setUp(self):
- from . import namespacedata01
-
- self.data = namespacedata01
+class OpenNamespaceTests(FilesTests, util.DiskSetup, unittest.TestCase):
+ MODULE = 'namespacedata01'
class OpenNamespaceZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
ZIP_MODULE = 'namespacedata01'
-class SiteDir:
- def setUp(self):
- self.fixtures = contextlib.ExitStack()
- self.addCleanup(self.fixtures.close)
- self.site_dir = self.fixtures.enter_context(os_helper.temp_dir())
- self.fixtures.enter_context(import_helper.DirsOnSysPath(self.site_dir))
- self.fixtures.enter_context(import_helper.isolated_modules())
+class DirectSpec:
+ """
+ Override behavior of ModuleSetup to write a full spec directly.
+ """
+
+ MODULE = 'unused'
+
+ def load_fixture(self, name):
+ self.tree_on_path(self.spec)
-class ModulesFilesTests(SiteDir, unittest.TestCase):
+class ModulesFiles:
+ spec = {
+ 'mod.py': '',
+ 'res.txt': 'resources are the best',
+ }
+
def test_module_resources(self):
"""
A module can have resources found adjacent to the module.
"""
- spec = {
- 'mod.py': '',
- 'res.txt': 'resources are the best',
- }
- _path.build(spec, self.site_dir)
import mod
actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8')
- assert actual == spec['res.txt']
+ assert actual == self.spec['res.txt']
+
+
+class ModuleFilesDiskTests(DirectSpec, util.DiskSetup, ModulesFiles, unittest.TestCase):
+ pass
+
+
+class ModuleFilesZipTests(DirectSpec, util.ZipSetup, ModulesFiles, unittest.TestCase):
+ pass
+
+class ImplicitContextFiles:
+ set_val = textwrap.dedent(
+ """
+ import importlib.resources as res
+ val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
+ """
+ )
+ spec = {
+ 'somepkg': {
+ '__init__.py': set_val,
+ 'submod.py': set_val,
+ 'res.txt': 'resources are the best',
+ },
+ }
-class ImplicitContextFilesTests(SiteDir, unittest.TestCase):
- def test_implicit_files(self):
+ def test_implicit_files_package(self):
"""
Without any parameter, files() will infer the location as the caller.
"""
- spec = {
- 'somepkg': {
- '__init__.py': textwrap.dedent(
- """
- import importlib.resources as res
- val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
- """
- ),
- 'res.txt': 'resources are the best',
- },
- }
- _path.build(spec, self.site_dir)
assert importlib.import_module('somepkg').val == 'resources are the best'
+ def test_implicit_files_submodule(self):
+ """
+ Without any parameter, files() will infer the location as the caller.
+ """
+ assert importlib.import_module('somepkg.submod').val == 'resources are the best'
+
+
+class ImplicitContextFilesDiskTests(
+ DirectSpec, util.DiskSetup, ImplicitContextFiles, unittest.TestCase
+):
+ pass
+
+
+class ImplicitContextFilesZipTests(
+ DirectSpec, util.ZipSetup, ImplicitContextFiles, unittest.TestCase
+):
+ pass
+
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/test/test_importlib/resources/test_functional.py b/Lib/test/test_importlib/resources/test_functional.py
index f65ffe6..4317abf 100644
--- a/Lib/test/test_importlib/resources/test_functional.py
+++ b/Lib/test/test_importlib/resources/test_functional.py
@@ -1,26 +1,38 @@
import unittest
import os
+import importlib
from test.support import warnings_helper
from importlib import resources
+from . import util
+
# Since the functional API forwards to Traversable, we only test
# filesystem resources here -- not zip files, namespace packages etc.
# We do test for two kinds of Anchor, though.
class StringAnchorMixin:
- anchor01 = 'test.test_importlib.resources.data01'
- anchor02 = 'test.test_importlib.resources.data02'
+ anchor01 = 'data01'
+ anchor02 = 'data02'
class ModuleAnchorMixin:
- from . import data01 as anchor01
- from . import data02 as anchor02
+ @property
+ def anchor01(self):
+ return importlib.import_module('data01')
+
+ @property
+ def anchor02(self):
+ return importlib.import_module('data02')
+
+class FunctionalAPIBase(util.DiskSetup):
+ def setUp(self):
+ super().setUp()
+ self.load_fixture('data02')
-class FunctionalAPIBase:
def _gen_resourcetxt_path_parts(self):
"""Yield various names of a text file in anchor02, each in a subTest"""
for path_parts in (
@@ -228,16 +240,16 @@ class FunctionalAPIBase:
class FunctionalAPITest_StringAnchor(
- unittest.TestCase,
- FunctionalAPIBase,
StringAnchorMixin,
+ FunctionalAPIBase,
+ unittest.TestCase,
):
pass
class FunctionalAPITest_ModuleAnchor(
- unittest.TestCase,
- FunctionalAPIBase,
ModuleAnchorMixin,
+ FunctionalAPIBase,
+ unittest.TestCase,
):
pass
diff --git a/Lib/test/test_importlib/resources/test_open.py b/Lib/test/test_importlib/resources/test_open.py
index 3b6b214..8c00378 100644
--- a/Lib/test/test_importlib/resources/test_open.py
+++ b/Lib/test/test_importlib/resources/test_open.py
@@ -1,7 +1,6 @@
import unittest
from importlib import resources
-from . import data01
from . import util
@@ -65,16 +64,12 @@ class OpenTests:
target.open(encoding='utf-8')
-class OpenDiskTests(OpenTests, unittest.TestCase):
- def setUp(self):
- self.data = data01
-
+class OpenDiskTests(OpenTests, util.DiskSetup, unittest.TestCase):
+ pass
-class OpenDiskNamespaceTests(OpenTests, unittest.TestCase):
- def setUp(self):
- from . import namespacedata01
- self.data = namespacedata01
+class OpenDiskNamespaceTests(OpenTests, util.DiskSetup, unittest.TestCase):
+ MODULE = 'namespacedata01'
class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
@@ -82,7 +77,7 @@ class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
class OpenNamespaceZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
- ZIP_MODULE = 'namespacedata01'
+ MODULE = 'namespacedata01'
if __name__ == '__main__':
diff --git a/Lib/test/test_importlib/resources/test_path.py b/Lib/test/test_importlib/resources/test_path.py
index 90b2290..378dc7a 100644
--- a/Lib/test/test_importlib/resources/test_path.py
+++ b/Lib/test/test_importlib/resources/test_path.py
@@ -3,7 +3,6 @@ import pathlib
import unittest
from importlib import resources
-from . import data01
from . import util
@@ -25,9 +24,7 @@ class PathTests:
self.assertEqual('Hello, UTF-8 world!\n', path.read_text(encoding='utf-8'))
-class PathDiskTests(PathTests, unittest.TestCase):
- data = data01
-
+class PathDiskTests(PathTests, util.DiskSetup, unittest.TestCase):
def test_natural_path(self):
# Guarantee the internal implementation detail that
# file-system-backed resources do not get the tempdir
diff --git a/Lib/test/test_importlib/resources/test_read.py b/Lib/test/test_importlib/resources/test_read.py
index 984feec..59c237d 100644
--- a/Lib/test/test_importlib/resources/test_read.py
+++ b/Lib/test/test_importlib/resources/test_read.py
@@ -1,7 +1,7 @@
import unittest
from importlib import import_module, resources
-from . import data01
+
from . import util
@@ -51,8 +51,8 @@ class ReadTests:
)
-class ReadDiskTests(ReadTests, unittest.TestCase):
- data = data01
+class ReadDiskTests(ReadTests, util.DiskSetup, unittest.TestCase):
+ pass
class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
@@ -68,15 +68,12 @@ class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
self.assertEqual(result, bytes(range(4, 8)))
-class ReadNamespaceTests(ReadTests, unittest.TestCase):
- def setUp(self):
- from . import namespacedata01
-
- self.data = namespacedata01
+class ReadNamespaceTests(ReadTests, util.DiskSetup, unittest.TestCase):
+ MODULE = 'namespacedata01'
class ReadNamespaceZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
- ZIP_MODULE = 'namespacedata01'
+ MODULE = 'namespacedata01'
def test_read_submodule_resource(self):
submodule = import_module('namespacedata01.subdirectory')
diff --git a/Lib/test/test_importlib/resources/test_reader.py b/Lib/test/test_importlib/resources/test_reader.py
index dac9c2a..ed5693a 100644
--- a/Lib/test/test_importlib/resources/test_reader.py
+++ b/Lib/test/test_importlib/resources/test_reader.py
@@ -1,16 +1,21 @@
import os.path
-import sys
import pathlib
import unittest
from importlib import import_module
from importlib.readers import MultiplexedPath, NamespaceReader
+from . import util
-class MultiplexedPathTest(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- cls.folder = pathlib.Path(__file__).parent / 'namespacedata01'
+
+class MultiplexedPathTest(util.DiskSetup, unittest.TestCase):
+ MODULE = 'namespacedata01'
+
+ def setUp(self):
+ super().setUp()
+ self.folder = pathlib.Path(self.data.__path__[0])
+ self.data01 = pathlib.Path(self.load_fixture('data01').__file__).parent
+ self.data02 = pathlib.Path(self.load_fixture('data02').__file__).parent
def test_init_no_paths(self):
with self.assertRaises(FileNotFoundError):
@@ -31,9 +36,8 @@ class MultiplexedPathTest(unittest.TestCase):
)
def test_iterdir_duplicate(self):
- data01 = pathlib.Path(__file__).parent.joinpath('data01')
contents = {
- path.name for path in MultiplexedPath(self.folder, data01).iterdir()
+ path.name for path in MultiplexedPath(self.folder, self.data01).iterdir()
}
for remove in ('__pycache__', '__init__.pyc'):
try:
@@ -61,9 +65,8 @@ class MultiplexedPathTest(unittest.TestCase):
path.open()
def test_join_path(self):
- data01 = pathlib.Path(__file__).parent.joinpath('data01')
- prefix = str(data01.parent)
- path = MultiplexedPath(self.folder, data01)
+ prefix = str(self.folder.parent)
+ path = MultiplexedPath(self.folder, self.data01)
self.assertEqual(
str(path.joinpath('binary.file'))[len(prefix) + 1 :],
os.path.join('namespacedata01', 'binary.file'),
@@ -83,10 +86,8 @@ class MultiplexedPathTest(unittest.TestCase):
assert not path.joinpath('imaginary/foo.py').exists()
def test_join_path_common_subdir(self):
- data01 = pathlib.Path(__file__).parent.joinpath('data01')
- data02 = pathlib.Path(__file__).parent.joinpath('data02')
- prefix = str(data01.parent)
- path = MultiplexedPath(data01, data02)
+ prefix = str(self.data02.parent)
+ path = MultiplexedPath(self.data01, self.data02)
self.assertIsInstance(path.joinpath('subdirectory'), MultiplexedPath)
self.assertEqual(
str(path.joinpath('subdirectory', 'subsubdir'))[len(prefix) + 1 :],
@@ -106,16 +107,8 @@ class MultiplexedPathTest(unittest.TestCase):
)
-class NamespaceReaderTest(unittest.TestCase):
- site_dir = str(pathlib.Path(__file__).parent)
-
- @classmethod
- def setUpClass(cls):
- sys.path.append(cls.site_dir)
-
- @classmethod
- def tearDownClass(cls):
- sys.path.remove(cls.site_dir)
+class NamespaceReaderTest(util.DiskSetup, unittest.TestCase):
+ MODULE = 'namespacedata01'
def test_init_error(self):
with self.assertRaises(ValueError):
@@ -125,7 +118,7 @@ class NamespaceReaderTest(unittest.TestCase):
namespacedata01 = import_module('namespacedata01')
reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
- root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
+ root = self.data.__path__[0]
self.assertEqual(
reader.resource_path('binary.file'), os.path.join(root, 'binary.file')
)
@@ -134,9 +127,8 @@ class NamespaceReaderTest(unittest.TestCase):
)
def test_files(self):
- namespacedata01 = import_module('namespacedata01')
- reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
- root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
+ reader = NamespaceReader(self.data.__spec__.submodule_search_locations)
+ root = self.data.__path__[0]
self.assertIsInstance(reader.files(), MultiplexedPath)
self.assertEqual(repr(reader.files()), f"MultiplexedPath('{root}')")
diff --git a/Lib/test/test_importlib/resources/test_resource.py b/Lib/test/test_importlib/resources/test_resource.py
index d1d45d9..fcede14 100644
--- a/Lib/test/test_importlib/resources/test_resource.py
+++ b/Lib/test/test_importlib/resources/test_resource.py
@@ -1,8 +1,5 @@
-import sys
import unittest
-import pathlib
-from . import data01
from . import util
from importlib import resources, import_module
@@ -24,9 +21,8 @@ class ResourceTests:
self.assertTrue(target.is_dir())
-class ResourceDiskTests(ResourceTests, unittest.TestCase):
- def setUp(self):
- self.data = data01
+class ResourceDiskTests(ResourceTests, util.DiskSetup, unittest.TestCase):
+ pass
class ResourceZipTests(ResourceTests, util.ZipSetup, unittest.TestCase):
@@ -37,33 +33,39 @@ def names(traversable):
return {item.name for item in traversable.iterdir()}
-class ResourceLoaderTests(unittest.TestCase):
+class ResourceLoaderTests(util.DiskSetup, unittest.TestCase):
def test_resource_contents(self):
package = util.create_package(
- file=data01, path=data01.__file__, contents=['A', 'B', 'C']
+ file=self.data, path=self.data.__file__, contents=['A', 'B', 'C']
)
self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'})
def test_is_file(self):
package = util.create_package(
- file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ file=self.data,
+ path=self.data.__file__,
+ contents=['A', 'B', 'C', 'D/E', 'D/F'],
)
self.assertTrue(resources.files(package).joinpath('B').is_file())
def test_is_dir(self):
package = util.create_package(
- file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ file=self.data,
+ path=self.data.__file__,
+ contents=['A', 'B', 'C', 'D/E', 'D/F'],
)
self.assertTrue(resources.files(package).joinpath('D').is_dir())
def test_resource_missing(self):
package = util.create_package(
- file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ file=self.data,
+ path=self.data.__file__,
+ contents=['A', 'B', 'C', 'D/E', 'D/F'],
)
self.assertFalse(resources.files(package).joinpath('Z').is_file())
-class ResourceCornerCaseTests(unittest.TestCase):
+class ResourceCornerCaseTests(util.DiskSetup, unittest.TestCase):
def test_package_has_no_reader_fallback(self):
"""
Test odd ball packages which:
@@ -72,7 +74,7 @@ class ResourceCornerCaseTests(unittest.TestCase):
# 3. Are not in a zip file
"""
module = util.create_package(
- file=data01, path=data01.__file__, contents=['A', 'B', 'C']
+ file=self.data, path=self.data.__file__, contents=['A', 'B', 'C']
)
# Give the module a dummy loader.
module.__loader__ = object()
@@ -83,9 +85,7 @@ class ResourceCornerCaseTests(unittest.TestCase):
self.assertFalse(resources.files(module).joinpath('A').is_file())
-class ResourceFromZipsTest01(util.ZipSetupBase, unittest.TestCase):
- ZIP_MODULE = 'data01'
-
+class ResourceFromZipsTest01(util.ZipSetup, unittest.TestCase):
def test_is_submodule_resource(self):
submodule = import_module('data01.subdirectory')
self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file())
@@ -116,8 +116,8 @@ class ResourceFromZipsTest01(util.ZipSetupBase, unittest.TestCase):
assert not data.parent.exists()
-class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
- ZIP_MODULE = 'data02'
+class ResourceFromZipsTest02(util.ZipSetup, unittest.TestCase):
+ MODULE = 'data02'
def test_unrelated_contents(self):
"""
@@ -134,7 +134,7 @@ class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
)
-class DeletingZipsTest(util.ZipSetupBase, unittest.TestCase):
+class DeletingZipsTest(util.ZipSetup, unittest.TestCase):
"""Having accessed resources in a zip file should not keep an open
reference to the zip.
"""
@@ -216,24 +216,20 @@ class ResourceFromNamespaceTests:
self.assertEqual(contents, {'binary.file'})
-class ResourceFromNamespaceDiskTests(ResourceFromNamespaceTests, unittest.TestCase):
- site_dir = str(pathlib.Path(__file__).parent)
-
- @classmethod
- def setUpClass(cls):
- sys.path.append(cls.site_dir)
-
- @classmethod
- def tearDownClass(cls):
- sys.path.remove(cls.site_dir)
+class ResourceFromNamespaceDiskTests(
+ util.DiskSetup,
+ ResourceFromNamespaceTests,
+ unittest.TestCase,
+):
+ MODULE = 'namespacedata01'
class ResourceFromNamespaceZipTests(
- util.ZipSetupBase,
+ util.ZipSetup,
ResourceFromNamespaceTests,
unittest.TestCase,
):
- ZIP_MODULE = 'namespacedata01'
+ MODULE = 'namespacedata01'
if __name__ == '__main__':
diff --git a/Lib/test/test_importlib/resources/update-zips.py b/Lib/test/test_importlib/resources/update-zips.py
deleted file mode 100755
index 231334a..0000000
--- a/Lib/test/test_importlib/resources/update-zips.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""
-Generate the zip test data files.
-
-Run to build the tests/zipdataNN/ziptestdata.zip files from
-files in tests/dataNN.
-
-Replaces the file with the working copy, but does commit anything
-to the source repo.
-"""
-
-import contextlib
-import os
-import pathlib
-import zipfile
-
-
-def main():
- """
- >>> from unittest import mock
- >>> monkeypatch = getfixture('monkeypatch')
- >>> monkeypatch.setattr(zipfile, 'ZipFile', mock.MagicMock())
- >>> print(); main() # print workaround for bpo-32509
- <BLANKLINE>
- ...data01... -> ziptestdata/...
- ...
- ...data02... -> ziptestdata/...
- ...
- """
- suffixes = '01', '02'
- tuple(map(generate, suffixes))
-
-
-def generate(suffix):
- root = pathlib.Path(__file__).parent.relative_to(os.getcwd())
- zfpath = root / f'zipdata{suffix}/ziptestdata.zip'
- with zipfile.ZipFile(zfpath, 'w') as zf:
- for src, rel in walk(root / f'data{suffix}'):
- dst = 'ziptestdata' / pathlib.PurePosixPath(rel.as_posix())
- print(src, '->', dst)
- zf.write(src, dst)
-
-
-def walk(datapath):
- for dirpath, dirnames, filenames in os.walk(datapath):
- with contextlib.suppress(ValueError):
- dirnames.remove('__pycache__')
- for filename in filenames:
- res = pathlib.Path(dirpath) / filename
- rel = res.relative_to(datapath)
- yield res, rel
-
-
-__name__ == '__main__' and main()
diff --git a/Lib/test/test_importlib/resources/util.py b/Lib/test/test_importlib/resources/util.py
index d4bf3e6..893dda6 100644
--- a/Lib/test/test_importlib/resources/util.py
+++ b/Lib/test/test_importlib/resources/util.py
@@ -6,10 +6,10 @@ import types
import pathlib
import contextlib
-from . import data01
from importlib.resources.abc import ResourceReader
from test.support import import_helper, os_helper
from . import zip as zip_
+from . import _path
from importlib.machinery import ModuleSpec
@@ -68,7 +68,7 @@ def create_package(file=None, path=None, is_package=True, contents=()):
)
-class CommonTests(metaclass=abc.ABCMeta):
+class CommonTestsBase(metaclass=abc.ABCMeta):
"""
Tests shared by test_open, test_path, and test_read.
"""
@@ -84,34 +84,34 @@ class CommonTests(metaclass=abc.ABCMeta):
"""
Passing in the package name should succeed.
"""
- self.execute(data01.__name__, 'utf-8.file')
+ self.execute(self.data.__name__, 'utf-8.file')
def test_package_object(self):
"""
Passing in the package itself should succeed.
"""
- self.execute(data01, 'utf-8.file')
+ self.execute(self.data, 'utf-8.file')
def test_string_path(self):
"""
Passing in a string for the path should succeed.
"""
path = 'utf-8.file'
- self.execute(data01, path)
+ self.execute(self.data, path)
def test_pathlib_path(self):
"""
Passing in a pathlib.PurePath object for the path should succeed.
"""
path = pathlib.PurePath('utf-8.file')
- self.execute(data01, path)
+ self.execute(self.data, path)
def test_importing_module_as_side_effect(self):
"""
The anchor package can already be imported.
"""
- del sys.modules[data01.__name__]
- self.execute(data01.__name__, 'utf-8.file')
+ del sys.modules[self.data.__name__]
+ self.execute(self.data.__name__, 'utf-8.file')
def test_missing_path(self):
"""
@@ -141,24 +141,66 @@ class CommonTests(metaclass=abc.ABCMeta):
self.execute(package, 'utf-8.file')
-class ZipSetupBase:
- ZIP_MODULE = 'data01'
-
+fixtures = dict(
+ data01={
+ '__init__.py': '',
+ 'binary.file': bytes(range(4)),
+ 'utf-16.file': 'Hello, UTF-16 world!\n'.encode('utf-16'),
+ 'utf-8.file': 'Hello, UTF-8 world!\n'.encode('utf-8'),
+ 'subdirectory': {
+ '__init__.py': '',
+ 'binary.file': bytes(range(4, 8)),
+ },
+ },
+ data02={
+ '__init__.py': '',
+ 'one': {'__init__.py': '', 'resource1.txt': 'one resource'},
+ 'two': {'__init__.py': '', 'resource2.txt': 'two resource'},
+ 'subdirectory': {'subsubdir': {'resource.txt': 'a resource'}},
+ },
+ namespacedata01={
+ 'binary.file': bytes(range(4)),
+ 'utf-16.file': 'Hello, UTF-16 world!\n'.encode('utf-16'),
+ 'utf-8.file': 'Hello, UTF-8 world!\n'.encode('utf-8'),
+ 'subdirectory': {
+ 'binary.file': bytes(range(12, 16)),
+ },
+ },
+)
+
+
+class ModuleSetup:
def setUp(self):
self.fixtures = contextlib.ExitStack()
self.addCleanup(self.fixtures.close)
self.fixtures.enter_context(import_helper.isolated_modules())
+ self.data = self.load_fixture(self.MODULE)
+
+ def load_fixture(self, module):
+ self.tree_on_path({module: fixtures[module]})
+ return importlib.import_module(module)
+
+
+class ZipSetup(ModuleSetup):
+ MODULE = 'data01'
+ def tree_on_path(self, spec):
temp_dir = self.fixtures.enter_context(os_helper.temp_dir())
modules = pathlib.Path(temp_dir) / 'zipped modules.zip'
- src_path = pathlib.Path(__file__).parent.joinpath(self.ZIP_MODULE)
self.fixtures.enter_context(
- import_helper.DirsOnSysPath(str(zip_.make_zip_file(src_path, modules)))
+ import_helper.DirsOnSysPath(str(zip_.make_zip_file(spec, modules)))
)
- self.data = importlib.import_module(self.ZIP_MODULE)
+
+class DiskSetup(ModuleSetup):
+ MODULE = 'data01'
+
+ def tree_on_path(self, spec):
+ temp_dir = self.fixtures.enter_context(os_helper.temp_dir())
+ _path.build(spec, pathlib.Path(temp_dir))
+ self.fixtures.enter_context(import_helper.DirsOnSysPath(temp_dir))
-class ZipSetup(ZipSetupBase):
+class CommonTests(DiskSetup, CommonTestsBase):
pass
diff --git a/Lib/test/test_importlib/resources/zip.py b/Lib/test/test_importlib/resources/zip.py
index 4dcf6fa..fc453f0 100755
--- a/Lib/test/test_importlib/resources/zip.py
+++ b/Lib/test/test_importlib/resources/zip.py
@@ -2,29 +2,23 @@
Generate zip test data files.
"""
-import contextlib
-import os
-import pathlib
import zipfile
-def make_zip_file(src, dst):
+def make_zip_file(tree, dst):
"""
- Zip the files in src into a new zipfile at dst.
+ Zip the files in tree into a new zipfile at dst.
"""
with zipfile.ZipFile(dst, 'w') as zf:
- for src_path, rel in walk(src):
- dst_name = src.name / pathlib.PurePosixPath(rel.as_posix())
- zf.write(src_path, dst_name)
+ for name, contents in walk(tree):
+ zf.writestr(name, contents)
zipfile._path.CompleteDirs.inject(zf)
return dst
-def walk(datapath):
- for dirpath, dirnames, filenames in os.walk(datapath):
- with contextlib.suppress(ValueError):
- dirnames.remove('__pycache__')
- for filename in filenames:
- res = pathlib.Path(dirpath) / filename
- rel = res.relative_to(datapath)
- yield res, rel
+def walk(tree, prefix=''):
+ for name, contents in tree.items():
+ if isinstance(contents, dict):
+ yield from walk(contents, prefix=f'{prefix}{name}/')
+ else:
+ yield f'{prefix}{name}', contents
diff --git a/Lib/test/test_importlib/resources/zipdata01/__init__.py b/Lib/test/test_importlib/resources/zipdata01/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/zipdata01/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/zipdata01/ziptestdata.zip b/Lib/test/test_importlib/resources/zipdata01/ziptestdata.zip
deleted file mode 100644
index 9a3bb07..0000000
--- a/Lib/test/test_importlib/resources/zipdata01/ziptestdata.zip
+++ /dev/null
Binary files differ
diff --git a/Lib/test/test_importlib/resources/zipdata02/__init__.py b/Lib/test/test_importlib/resources/zipdata02/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Lib/test/test_importlib/resources/zipdata02/__init__.py
+++ /dev/null
diff --git a/Lib/test/test_importlib/resources/zipdata02/ziptestdata.zip b/Lib/test/test_importlib/resources/zipdata02/ziptestdata.zip
deleted file mode 100644
index d63ff51..0000000
--- a/Lib/test/test_importlib/resources/zipdata02/ziptestdata.zip
+++ /dev/null
Binary files differ
diff --git a/Lib/zipimport.py b/Lib/zipimport.py
index 7ceae2b..e5192c4 100644
--- a/Lib/zipimport.py
+++ b/Lib/zipimport.py
@@ -256,17 +256,9 @@ class zipimporter(_bootstrap_external._LoaderBasics):
def get_resource_reader(self, fullname):
- """Return the ResourceReader for a package in a zip file.
-
- If 'fullname' is a package within the zip file, return the
- 'ResourceReader' object for the package. Otherwise return None.
- """
- try:
- if not self.is_package(fullname):
- return None
- except ZipImportError:
- return None
+ """Return the ResourceReader for a module in a zip file."""
from importlib.readers import ZipReader
+
return ZipReader(self, fullname)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 77455c0..579b3fd 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2489,21 +2489,6 @@ TESTSUBDIRS= idlelib/idle_test \
test/test_importlib/namespace_pkgs/project3/parent/child \
test/test_importlib/partial \
test/test_importlib/resources \
- test/test_importlib/resources/data01 \
- test/test_importlib/resources/data01/subdirectory \
- test/test_importlib/resources/data02 \
- test/test_importlib/resources/data02/one \
- test/test_importlib/resources/data02/subdirectory \
- test/test_importlib/resources/data02/subdirectory/subsubdir \
- test/test_importlib/resources/data02/two \
- test/test_importlib/resources/data03 \
- test/test_importlib/resources/data03/namespace \
- test/test_importlib/resources/data03/namespace/portion1 \
- test/test_importlib/resources/data03/namespace/portion2 \
- test/test_importlib/resources/namespacedata01 \
- test/test_importlib/resources/namespacedata01/subdirectory \
- test/test_importlib/resources/zipdata01 \
- test/test_importlib/resources/zipdata02 \
test/test_importlib/source \
test/test_inspect \
test/test_interpreters \
diff --git a/Misc/NEWS.d/next/Library/2024-08-15-09-45-34.gh-issue-121735._1q0qf.rst b/Misc/NEWS.d/next/Library/2024-08-15-09-45-34.gh-issue-121735._1q0qf.rst
new file mode 100644
index 0000000..e10b2e7
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-08-15-09-45-34.gh-issue-121735._1q0qf.rst
@@ -0,0 +1,3 @@
+When working with zip archives, importlib.resources now properly honors
+module-adjacent references (e.g. ``files(pkg.mod)`` and not just
+``files(pkg)``).