Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ba687d9

Browse filesBrowse files
authored
gh-121735: Fix module-adjacent references in zip files (#123037)
* gh-116608: Apply style and compatibility changes from importlib_metadata. * gh-121735: Ensure module-adjacent resources are loadable from a zipfile. * gh-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.
1 parent 3bd942f commit ba687d9
Copy full SHA for ba687d9

40 files changed

+223
-261
lines changed

‎.gitattributes

Copy file name to clipboardExpand all lines: .gitattributes
-2Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ Lib/test/cjkencodings/* noeol
2727
Lib/test/tokenizedata/coding20731.py noeol
2828
Lib/test/decimaltestdata/*.decTest noeol
2929
Lib/test/test_email/data/*.txt noeol
30-
Lib/test/test_importlib/resources/data01/* noeol
31-
Lib/test/test_importlib/resources/namespacedata01/* noeol
3230
Lib/test/xmltestdata/* noeol
3331

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

‎Lib/importlib/resources/readers.py

Copy file name to clipboardExpand all lines: Lib/importlib/resources/readers.py
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ def files(self):
3434

3535
class ZipReader(abc.TraversableResources):
3636
def __init__(self, loader, module):
37-
_, _, name = module.rpartition('.')
38-
self.prefix = loader.prefix.replace('\\', '/') + name + '/'
37+
self.prefix = loader.prefix.replace('\\', '/')
38+
if loader.is_package(module):
39+
_, _, name = module.rpartition('.')
40+
self.prefix += name + '/'
3941
self.archive = loader.archive
4042

4143
def open_resource(self, resource):

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data01/__init__.py
Whitespace-only changes.
Binary file not shown.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data01/subdirectory/__init__.py
Whitespace-only changes.

‎Lib/test/test_importlib/resources/data01/subdirectory/binary.file

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data01/subdirectory/binary.file
-1Lines changed: 0 additions & 1 deletion
This file was deleted.
Binary file not shown.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data01/utf-8.file
-1Lines changed: 0 additions & 1 deletion
This file was deleted.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data02/__init__.py
Whitespace-only changes.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data02/one/__init__.py
Whitespace-only changes.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data02/one/resource1.txt
-1Lines changed: 0 additions & 1 deletion
This file was deleted.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt
-1Lines changed: 0 additions & 1 deletion
This file was deleted.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data02/two/__init__.py
Whitespace-only changes.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data02/two/resource2.txt
-1Lines changed: 0 additions & 1 deletion
This file was deleted.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data03/__init__.py
Whitespace-only changes.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data03/namespace/portion1/__init__.py
Whitespace-only changes.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data03/namespace/portion2/__init__.py
Whitespace-only changes.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/data03/namespace/resource1.txt
Whitespace-only changes.
Binary file not shown.

‎Lib/test/test_importlib/resources/namespacedata01/subdirectory/binary.file

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/namespacedata01/subdirectory/binary.file
-1Lines changed: 0 additions & 1 deletion
This file was deleted.
Binary file not shown.

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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/namespacedata01/utf-8.file
-1Lines changed: 0 additions & 1 deletion
This file was deleted.
+5-10Lines 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
'binary.file',
3535
'subdirectory',
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

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/test_files.py
+62-40Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66

77
from importlib import resources
88
from importlib.resources.abc import Traversable
9-
from . import data01
109
from . import util
11-
from . import _path
12-
from test.support import os_helper
13-
from test.support import import_helper
1410

1511

1612
@contextlib.contextmanager
@@ -48,70 +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
63-
64-
self.data = namespacedata01
55+
class OpenNamespaceTests(FilesTests, util.DiskSetup, unittest.TestCase):
56+
MODULE = 'namespacedata01'
6557

6658

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

7062

71-
class SiteDir:
72-
def setUp(self):
73-
self.fixtures = contextlib.ExitStack()
74-
self.addCleanup(self.fixtures.close)
75-
self.site_dir = self.fixtures.enter_context(os_helper.temp_dir())
76-
self.fixtures.enter_context(import_helper.DirsOnSysPath(self.site_dir))
77-
self.fixtures.enter_context(import_helper.isolated_modules())
63+
class DirectSpec:
64+
"""
65+
Override behavior of ModuleSetup to write a full spec directly.
66+
"""
67+
68+
MODULE = 'unused'
69+
70+
def load_fixture(self, name):
71+
self.tree_on_path(self.spec)
7872

7973

80-
class ModulesFilesTests(SiteDir, unittest.TestCase):
74+
class ModulesFiles:
75+
spec = {
76+
'mod.py': '',
77+
'res.txt': 'resources are the best',
78+
}
79+
8180
def test_module_resources(self):
8281
"""
8382
A module can have resources found adjacent to the module.
8483
"""
85-
spec = {
86-
'mod.py': '',
87-
'res.txt': 'resources are the best',
88-
}
89-
_path.build(spec, self.site_dir)
9084
import mod
9185

9286
actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8')
93-
assert actual == spec['res.txt']
87+
assert actual == self.spec['res.txt']
88+
89+
90+
class ModuleFilesDiskTests(DirectSpec, util.DiskSetup, ModulesFiles, unittest.TestCase):
91+
pass
92+
93+
94+
class ModuleFilesZipTests(DirectSpec, util.ZipSetup, ModulesFiles, unittest.TestCase):
95+
pass
96+
9497

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+
}
95112

96-
class ImplicitContextFilesTests(SiteDir, unittest.TestCase):
97-
def test_implicit_files(self):
113+
def test_implicit_files_package(self):
98114
"""
99115
Without any parameter, files() will infer the location as the caller.
100116
"""
101-
spec = {
102-
'somepkg': {
103-
'__init__.py': textwrap.dedent(
104-
"""
105-
import importlib.resources as res
106-
val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
107-
"""
108-
),
109-
'res.txt': 'resources are the best',
110-
},
111-
}
112-
_path.build(spec, self.site_dir)
113117
assert importlib.import_module('somepkg').val == 'resources are the best'
114118

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+
115137

116138
if __name__ == '__main__':
117139
unittest.main()

‎Lib/test/test_importlib/resources/test_functional.py

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/test_functional.py
+21-9Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
11
import unittest
22
import os
3+
import importlib
34

45
from test.support import warnings_helper
56

67
from importlib import resources
78

9+
from . import util
10+
811
# Since the functional API forwards to Traversable, we only test
912
# filesystem resources here -- not zip files, namespace packages etc.
1013
# We do test for two kinds of Anchor, though.
1114

1215

1316
class StringAnchorMixin:
14-
anchor01 = 'test.test_importlib.resources.data01'
15-
anchor02 = 'test.test_importlib.resources.data02'
17+
anchor01 = 'data01'
18+
anchor02 = 'data02'
1619

1720

1821
class ModuleAnchorMixin:
19-
from . import data01 as anchor01
20-
from . import data02 as anchor02
22+
@property
23+
def anchor01(self):
24+
return importlib.import_module('data01')
25+
26+
@property
27+
def anchor02(self):
28+
return importlib.import_module('data02')
29+
2130

31+
class FunctionalAPIBase(util.DiskSetup):
32+
def setUp(self):
33+
super().setUp()
34+
self.load_fixture('data02')
2235

23-
class FunctionalAPIBase:
2436
def _gen_resourcetxt_path_parts(self):
2537
"""Yield various names of a text file in anchor02, each in a subTest"""
2638
for path_parts in (
@@ -228,16 +240,16 @@ def test_text_errors(self):
228240

229241

230242
class FunctionalAPITest_StringAnchor(
231-
unittest.TestCase,
232-
FunctionalAPIBase,
233243
StringAnchorMixin,
244+
FunctionalAPIBase,
245+
unittest.TestCase,
234246
):
235247
pass
236248

237249

238250
class FunctionalAPITest_ModuleAnchor(
239-
unittest.TestCase,
240-
FunctionalAPIBase,
241251
ModuleAnchorMixin,
252+
FunctionalAPIBase,
253+
unittest.TestCase,
242254
):
243255
pass

‎Lib/test/test_importlib/resources/test_open.py

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/test_open.py
+5-10Lines changed: 5 additions & 10 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,24 +64,20 @@ 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

8479
class OpenNamespaceZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
85-
ZIP_MODULE = 'namespacedata01'
80+
MODULE = 'namespacedata01'
8681

8782

8883
if __name__ == '__main__':

‎Lib/test/test_importlib/resources/test_path.py

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/test_path.py
+1-4Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import unittest
44

55
from importlib import resources
6-
from . import data01
76
from . import util
87

98

@@ -25,9 +24,7 @@ def test_reading(self):
2524
self.assertEqual('Hello, UTF-8 world!\n', path.read_text(encoding='utf-8'))
2625

2726

28-
class PathDiskTests(PathTests, unittest.TestCase):
29-
data = data01
30-
27+
class PathDiskTests(PathTests, util.DiskSetup, unittest.TestCase):
3128
def test_natural_path(self):
3229
# Guarantee the internal implementation detail that
3330
# file-system-backed resources do not get the tempdir

‎Lib/test/test_importlib/resources/test_read.py

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/test_read.py
+6-9Lines changed: 6 additions & 9 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):
@@ -68,15 +68,12 @@ def test_read_submodule_resource_by_name(self):
6868
self.assertEqual(result, bytes(range(4, 8)))
6969

7070

71-
class ReadNamespaceTests(ReadTests, unittest.TestCase):
72-
def setUp(self):
73-
from . import namespacedata01
74-
75-
self.data = namespacedata01
71+
class ReadNamespaceTests(ReadTests, util.DiskSetup, unittest.TestCase):
72+
MODULE = 'namespacedata01'
7673

7774

7875
class ReadNamespaceZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
79-
ZIP_MODULE = 'namespacedata01'
76+
MODULE = 'namespacedata01'
8077

8178
def test_read_submodule_resource(self):
8279
submodule = import_module('namespacedata01.subdirectory')

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.