8000 [3.12] gh-121735: Fix module-adjacent references in zip files (GH-123… · jaraco/cpython@330cfcd · GitHub
[go: up one dir, main page]

Skip to content

Commit 330cfcd

Browse files
committed
[3.12] pythongh-121735: Fix module-adjacent references in zip files (pythonGH-123037)
* pythongh-116608: Apply style and compatibility changes from importlib_metadata. * pythongh-121735: Ensure module-adjacent resources are loadable from a zipfile. * pythongh-121735: Allow all modules to be processed by the ZipReader. * Add blurb * Remove update-zips script, unneeded. * Remove unnecessary references to removed static fixtures. * Remove zipdata fixtures, unused. (cherry picked from commit ba687d9) Co-authored-by: Jason R. Coombs <jaraco@jaraco.com>
1 parent 53af5b2 commit 330cfcd

38 files changed

+282
-294
lines changed

.gitattributes

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ Lib/test/cjkencodings/* noeol
2828
Lib/test/tokenizedata/coding20731.py noeol
2929
Lib/test/decimaltestdata/*.decTest noeol
3030
Lib/test/test_email/data/*.txt noeol
31-
Lib/test/test_importlib/resources/data01/* noeol
32-
Lib/test/test_importlib/resources/namespacedata01/* noeol
3331
Lib/test/xmltestdata/* noeol
3432

3533
# Shell scripts should have LF even on Windows because of Cygwin

Lib/importlib/resources/readers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ def files(self):
3131

3232
class ZipReader(abc.TraversableResources):
3333
def __init__(self, loader, module):
34-
_, _, name = module.rpartition('.')
35-
self.prefix = loader.prefix.replace('\\', '/') + name + '/'
34+
self.prefix = loader.prefix.replace('\\', '/')
35+
if loader.is_package(module):
36+
_, _, name = module.rpartition('.')
37+
self.prefix += name + '/'
3638
self.archive = loader.archive
3739

3840
def open_resource(self, resource):

Lib/test/test_importlib/resources/data01/__init__.py

Whitespace-only changes.
Binary file not shown.

Lib/test/test_importlib/resources/data01/subdirectory/__init__.py

Whitespace-only changes.
Binary file not shown.
Binary file not shown.

Lib/test/test_importlib/resources/data01/utf-8.file

Lines changed: 0 additions & 1 deletion
This file was deleted.

Lib/test/test_importlib/resources/data02/__init__.py

Whitespace-only changes.

Lib/test/test_importlib/resources/data02/one/__init__.py

Whitespace-only changes.

Lib/test/test_importlib/resources/data02/one/resource1.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

Lib/test/test_importlib/resources/data02/two/__init__.py

Whitespace-only changes.

Lib/test/test_importlib/resources/data02/two/resource2.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

Lib/test/test_importlib/resources/data03/__init__.py

Whitespace-only changes.

Lib/test/test_importlib/resources/data03/namespace/portion1/__init__.py

Whitespace-only changes.

Lib/test/test_importlib/resources/data03/namespace/portion2/__init__.py

Whitespace-only changes.

Lib/test/test_importlib/resources/data03/namespace/resource1.txt

Whitespace-only changes.
Binary file not shown.
Binary file not shown.

Lib/test/test_importlib/resources/namespacedata01/utf-8.file

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import unittest
22
from importlib import resources
33

4-
from . import data01
54
from . import util
65

76

@@ -19,25 +18,21 @@ def test_contents(self):
1918
assert self.expected <= contents
2019

2120

22-
class ContentsDiskTests(ContentsTests, unittest.TestCase):
23-
def setUp(self):
24-
self.data = data01
21+
class ContentsDiskTests(ContentsTests, util.DiskSetup, unittest.TestCase):
22+
pass
2523

2624

2725
class ContentsZipTests(ContentsTests, util.ZipSetup, unittest.TestCase):
2826
pass
2927

3028

31-
class ContentsNamespaceTests(ContentsTests, unittest.TestCase):
29+
class ContentsNamespaceTests(ContentsTests, util.DiskSetup, unittest.TestCase):
30+
MODULE = 'namespacedata01'
31+
3232
expected = {
3333
# no __init__ because of namespace design
3434
# no subdirectory as incidental difference in fixture
3535
'binary.file',
3636
'utf-16.file',
3737
'utf-8.file',
3838
}
39-
40-
def setUp(self):
41-
from . import namespacedata01
42-
43-
self.data = namespacedata01

Lib/test/test_importlib/resources/test_files.py

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@
77

88
from importlib import resources
99
from importlib.resources.abc import Traversable
10-
from . import data01
1110
from . import util
12-
from . import _path
13-
from test.support import os_helper
14-
from test.support import import_helper
1511

1612

1713
@contextlib.contextmanager
@@ -48,66 +44,96 @@ def test_old_parameter(self):
4844
resources.files(package=self.data)
4945

5046

51-
class OpenDiskTests(FilesTests, unittest.TestCase):
52-
def setUp(self):
53-
self.data = data01
47+
class OpenDiskTests(FilesTests, util.DiskSetup, unittest.TestCase):
48+
pass
5449

5550

5651
class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
5752
pass
5853

5954

60-
class OpenNamespaceTests(FilesTests, unittest.TestCase):
61-
def setUp(self):
62-
from . import namespacedata01
55+
class OpenNamespaceTests(FilesTests, util.DiskSetup, unittest.TestCase):
56+
MODULE = 'namespacedata01'
57+
58+
59+
class OpenNamespaceZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
60+
ZIP_MODULE = 'namespacedata01'
61+
6362

64-
self.data = namespacedata01
63+
class DirectSpec:
64+
"""
65+
Override behavior of ModuleSetup to write a full spec directly.
66+
"""
6567

68+
MODULE = 'unused'
6669

67-
class SiteDir:
68-
def setUp(self):
69-
self.fixtures = contextlib.ExitStack()
70-
self.addCleanup(self.fixtures.close)
71-
self.site_dir = self.fixtures.enter_context(os_helper.temp_dir())
72-
self.fixtures.enter_context(import_helper.DirsOnSysPath(self.site_dir))
73-
self.fixtures.enter_context(import_helper.isolated_modules())
70+
def load_fixture(self, name):
71+
self.tree_on_path(self.spec)
7472

7573

76-
class ModulesFilesTests(SiteDir, unittest.TestCase):
74+
class ModulesFiles:
75+
spec = {
76+
'mod.py': '',
77+
'res.txt': 'resources are the best',
78+
}
79+
7780
def test_module_resources(self):
7881
"""
7982
A module can have resources found adjacent to the module.
8083
"""
81-
spec = {
82-
'mod.py': '',
83-
'res.txt': 'resources are the best',
84-
}
85-
_path.build(spec, self.site_dir)
8684
import mod
8785

8886
actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8')
89-
assert actual == spec['res.txt']
87+
assert actual == self.spec['res.txt']
88+
9089

90+
class ModuleFilesDiskTests(DirectSpec, util.DiskSetup, ModulesFiles, unittest.TestCase):
91+
pass
92+
93+
94+
class ModuleFilesZipTests(DirectSpec, util.ZipSetup, ModulesFiles, unittest.TestCase):
95+
pass
9196

92-
class ImplicitContextFilesTests(SiteDir, unittest.TestCase):
93-
def test_implicit_files(self):
97+
98+
class ImplicitContextFiles:
99+
set_val = textwrap.dedent(
100+
"""
101+
import importlib.resources as res
102+
val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
103+
"""
104+
)
105+
spec = {
106+
'somepkg': {
107+
'__init__.py': set_val,
108+
'submod.py': set_val,
109+
'res.txt': 'resources are the best',
110+
},
111+
}
112+
113+
def test_implicit_files_package(self):
94114
"""
95115
Without any parameter, files() will infer the location as the caller.
96116
"""
97-
spec = {
98-
'somepkg': {
99-
'__init__.py': textwrap.dedent(
100-
"""
101-
import importlib.resources as res
102-
val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
103-
"""
104-
),
105-
'res.txt': 'resources are the best',
106-
},
107-
}
108-
_path.build(spec, self.site_dir)
109117
assert importlib.import_module('somepkg').val == 'resources are the best'
110118

119+
def test_implicit_files_submodule(self):
120+
"""
121+
Without any parameter, files() will infer the location as the caller.
122+
"""
123+
assert importlib.import_module('somepkg.submod').val == 'resources are the best'
124+
125+
126+
class ImplicitContextFilesDiskTests(
127+
DirectSpec, util.DiskSetup, ImplicitContextFiles, unittest.TestCase
128+
):
129+
pass
130+
131+
132+
class ImplicitContextFilesZipTests(
133+
DirectSpec, util.ZipSetup, ImplicitContextFiles, unittest.TestCase
134+
):
135+
pass
136+
111137

112138
if __name__ == '__main__':
113139
unittest.main()

Lib/test/test_importlib/resources/test_open.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import unittest
22

33
from importlib import resources
4-
from . import data01
54
from . import util
65

76

@@ -65,21 +64,21 @@ def test_open_text_FileNotFoundError(self):
6564
target.open(encoding='utf-8')
6665

6766

68-
class OpenDiskTests(OpenTests, unittest.TestCase):
69-
def setUp(self):
70-
self.data = data01
71-
67+
class OpenDiskTests(OpenTests, util.DiskSetup, unittest.TestCase):
68+
pass
7269

73-
class OpenDiskNamespaceTests(OpenTests, unittest.TestCase):
74-
def setUp(self):
75-
from . import namespacedata01
7670

77-
self.data = namespacedata01
71+
class OpenDiskNamespaceTests(OpenTests, util.DiskSetup, unittest.TestCase):
72+
MODULE = 'namespacedata01'
7873

7974

8075
class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
8176
pass
8277

8378

79+
class OpenNamespaceZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
80+
MODULE = 'namespacedata01'
81+
82+
8483
if __name__ == '__main__':
8584
unittest.main()

Lib/test/test_importlib/resources/test_path.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import unittest
33

44
from importlib import resources
5-
from . import data01
65
from . import util
76

97AE 87

@@ -29,9 +28,7 @@ def test_reading(self):
2928
self.assertEqual('Hello, UTF-8 world!\n', text)
3029

3130

32-
class PathDiskTests(PathTests, unittest.TestCase):
33-
data = data01
34-
31+
class PathDiskTests(PathTests, util.DiskSetup, unittest.TestCase):
3532
def test_natural_path(self):
3633
# Guarantee the internal implementation detail that
3734
# file-system-backed resources do not get the tempdir

Lib/test/test_importlib/resources/test_read.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22

33
from importlib import import_module, resources
4-
from . import data01
4+
55
from . import util
66

77

@@ -51,8 +51,8 @@ def test_read_text_with_errors(self):
5151
)
5252

5353

54-
class ReadDiskTests(ReadTests, unittest.TestCase):
55-
data = data01
54+
class ReadDiskTests(ReadTests, util.DiskSetup, unittest.TestCase):
55+
pass
5656

5757

5858
class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
@@ -70,11 +70,25 @@ def test_read_submodule_resource_by_name(self):
7070
self.assertEqual(result, b'\0\1\2\3')
7171

7272

73-
class ReadNamespaceTests(ReadTests, unittest.TestCase):
74-
def setUp(self):
75-
from . import namespacedata01
73+
class ReadNamespaceTests(ReadTests, util.DiskSetup, unittest.TestCase):
74+
MODULE = 'namespacedata01'
75+
76+
77+
class ReadNamespaceZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
78+
MODULE = 'namespacedata01'
79+
80+
def test_read_submodule_resource(self):
81+
submodule = import_module('namespacedata01.subdirectory')
82+
result = resources.files(submodule).joinpath('binary.file').read_bytes()
83+
self.assertEqual(result, bytes(range(12, 16)))
7684

77-
self.data = namespacedata01
85+
def test_read_submodule_resource_by_name(self):
86+
result = (
87+
resources.files('namespacedata01.subdirectory')
88+
.joinpath('binary.file')
89+
.read_bytes()
90+
)
91+
self.assertEqual(result, bytes(range(12, 16)))
7892

7993

8094
if __name__ == '__main__':

0 commit comments

Comments
 (0)
0