8000 Merge branch 'main' into bugfix/multiplexed-descendants · python/importlib_metadata@289aadb · GitHub
[go: up one dir, main page]

Skip to content

Commit 289aadb

Browse files
authored
Merge branch 'main' into bugfix/multiplexed-descendants
2 parents 7fb0260 + ff16bd3 commit 289aadb

File tree

11 files changed

+111
-68
lines changed

11 files changed

+111
-68
lines changed

.coveragerc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
omit =
33
# leading `*/` for pytest-dev/pytest-cov#456
44
*/.tox/*
5-
*/pep517-build-env-*
65
*/_itertools.py
76
*/_legacy.py
87
*/simple.py
98
*/_path.py
9+
disable_warnings =
10+
couldnt-parse
1011

1112
[report]
1213
show_missing = True

CHANGES.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ v5.11.0
44
* #265: ``MultiplexedPath`` now honors multiple subdirectories
55
in ``iterdir`` and ``joinpath``.
66

7+
v5.10.3
8+
=======
9+
10+
* Packaging refresh, including fixing EncodingWarnings
11+
and some tests cleanup.
12+
713
v5.10.2
814
=======
915

docs/using.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,27 @@ example could also be written as::
122122
eml = files(email.tests.data).joinpath('message.eml').read_text()
123123

124124

125+
Namespace Packages
126+
==================
127+
128+
``importlib_resources`` supports namespace packages as anchors just like
129+
any other package. Similar to modules in a namespace package,
130+
resources in a namespace package are not allowed to collide by name.
131+
For example, if two packages both expose ``nspkg/data/foo.txt``, those
132+
resources are unsupported by this library. The package will also likely
133+
experience problems due to the collision with installers.
134+
135+
It's perfectly valid, however, for two packages to present different resources
136+
in the same namespace package, regular package, or subdirectory.
137+
For example, one package could expose ``nspkg/data/foo.txt`` and another
138+
expose ``nspkg/data/bar.txt`` and those two packages could be installed
139+
into separate paths, and the resources should be queryable::
140+
141+
data = importlib_resources.files('nspkg').joinpath('data')
142+
data.joinpath('foo.txt').read_text()
143+
data.joinpath('bar.txt').read_text()
144+
145+
125146
File system or zip file
126147
=======================
127148

importlib_resources/tests/_path.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import pathlib
22
import functools
33

4+
from typing import Dict, Union
5+
46

57
####
6-
# from jaraco.path 3.4
8+
# from jaraco.path 3.4.1
9+
10+
FilesSpec = Dict[str, Union[str, bytes, 'FilesSpec']] # type: ignore
711

812

9-
def build(spec, prefix=pathlib.Path()):
13+
def build(spec: FilesSpec, prefix=pathlib.Path()):
1014
"""
1115
Build a set of files/directories, as described by the spec.
1216
@@ -23,15 +27,17 @@ def build(spec, prefix=pathlib.Path()):
2327
... "baz.py": "# Some code",
2428
... }
2529
... }
26-
>>> tmpdir = getfixture('tmpdir')
27-
>>> build(spec, tmpdir)
30+
>>> target = getfixture('tmp_path')
31+
>>> build(spec, target)
32+
>>> target.joinpath('foo/baz.py').read_text(encoding='utf-8')
33+
'# Some code'
2834
"""
2935
for name, contents in spec.items():
3036
create(contents, pathlib.Path(prefix) / name)
3137

3238

3339
@functools.singledispatch
34-
def create(content, path):
40+
def create(content: Union[str, bytes, FilesSpec], path):
3541
path.mkdir(exist_ok=True)
3642
build(content, prefix=path) # type: ignore
3743

@@ -43,7 +49,7 @@ def _(content: bytes, path):
4349

4450
@create.register
4551
def _(content: str, path):
46-
path.write_text(content)
52+
path.write_text(content, encoding='utf-8')
4753

4854

4955
# end from jaraco.path

importlib_resources/tests/test_compatibilty_files.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@ def test_orphan_path_name(self):
6464

6565
def test_spec_path_open(self):
6666
self.assertEqual(self.files.read_bytes(), b'Hello, world!')
67-
self.assertEqual(self.files.read_text(), 'Hello, world!')
67+
self.assertEqual(self.files.read_text(encoding='utf-8'), 'Hello, world!')
6868

6969
def test_child_path_open(self):
7070
self.assertEqual((self.files / 'a').read_bytes(), b'Hello, world!')
71-
self.assertEqual((self.files / 'a').read_text(), 'Hello, world!')
71+
self.assertEqual(
72+
(self.files / 'a').read_text(encoding='utf-8'), 'Hello, world!'
73+
)
7274

7375
def test_orphan_path_open(self):
7476
with self.assertRaises(FileNotFoundError):

importlib_resources/tests/test_files.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def test_module_resources(self):
8484
_path.build(spec, self.site_dir)
8585
import mod
8686

87-
actual = resources.files(mod).joinpath('res.txt').read_text()
87+
actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8')
8888
assert actual == spec['res.txt']
8989

9090

@@ -98,7 +98,7 @@ def test_implicit_files(self):
9898
'__init__.py': textwrap.dedent(
9999
"""
100100
import importlib_resources as res
101-
val = res.files().joinpath('res.txt').read_text()
101+
val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
102102
"""
103103
),
104104
'res.txt': 'resources are the best',

importlib_resources/tests/test_open.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def execute(self, package, path):
1515
class CommonTextTests(util.CommonTests, unittest.TestCase):
1616
def execute(self, package, path):
1717
target = resources.files(package).joinpath(path)
18-
with target.open():
18+
with target.open(encoding='utf-8'):
1919
pass
2020

2121

@@ -28,7 +28,7 @@ def test_open_binary(self):
2828

2929
def test_open_text_default_encoding(self):
3030
target = resources.files(self.data) / 'utf-8.file'
31-
with target.open() as fp:
31+
with target.open(encoding='utf-8') as fp:
3232
result = fp.read()
3333
self.assertEqual(result, 'Hello, UTF-8 world!\n')
3434

importlib_resources/tests/test_read.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def execute(self, package, path):
1313

1414
class CommonTextTests(util.CommonTests, unittest.TestCase):
1515
def execute(self, package, path):
16-
resources.files(package).joinpath(path).read_text()
16+
resources.files(package).joinpath(path).read_text(encoding='utf-8')
1717

1818

1919
class ReadTests:
@@ -22,7 +22,11 @@ def test_read_bytes(self):
2222
self.assertEqual(result, b'\0\1\2\3')
2323

2424
def test_read_text_default_encoding(self):
25-
result = resources.files(self.data).joinpath('utf-8.file').read_text()
25+
result = (
26+
resources.files(self.data)
27+
.joinpath('utf-8.file')
28+
.read_text(encoding='utf-8')
29+
)
2630
self.assertEqual(result, 'Hello, UTF-8 world!\n')
2731

2832
def test_read_text_given_encoding(self):

importlib_resources/tests/test_resource.py

Lines changed: 39 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import sys
23
import unittest
34
import importlib_resources as resources
@@ -8,7 +9,7 @@
89
from . import zipdata01, zipdata02
910
from . import util
1011
from importlib import import_module
11-
from ._compat import import_helper, unlink
12+
from ._compat import import_helper, os_helper, unlink
1213

1314

1415
class ResourceTests:
@@ -140,84 +141,71 @@ def test_unrelated_contents(self):
140141
)
141142

142143

144+
@contextlib.contextmanager
145+
def zip_on_path(dir):
146+
data_path = pathlib.Path(zipdata01.__file__)
147+
source_zip_path = data_path.parent.joinpath('ziptestdata.zip')
148+
zip_path = pathlib.Path(dir) / f'{uuid.uuid4()}.zip'
149+
zip_path.write_bytes(source_zip_path.read_bytes())
150+
sys.path.append(str(zip_path))
151+
import_module('ziptestdata')
152+
153+
try:
154+
yield
155+
finally:
156+
with contextlib.suppress(ValueError):
157+
sys.path.remove(str(zip_path))
158+
159+
with contextlib.suppress(KeyError):
160+
del sys.path_importer_cache[str(zip_path)]
161+
del sys.modules['ziptestdata']
162+
163+
with contextlib.suppress(OSError):
164+
unlink(zip_path)
165+
166+
143167
class DeletingZipsTest(unittest.TestCase):
144168
"""Having accessed resources in a zip file should not keep an open
145169
reference to the zip.
146170
"""
147171

148-
ZIP_MODULE = zipdata01
149-
150172
def setUp(self):
173+
self.fixtures = contextlib.ExitStack()
174+
self.addCleanup(self.fixtures.close)
175+
151176
modules = import_helper.modules_setup()
152177
self.addCleanup(import_helper.modules_cleanup, *modules)
153178

154-
data_path = pathlib.Path(self.ZIP_MODULE.__file__)
155-
data_dir = data_path.parent
156-
self.source_zip_path = data_dir / 'ziptestdata.zip'
157-
self.zip_path = pathlib.Path(f'{uuid.uuid4()}.zip').absolute()
158-
self.zip_path.write_bytes(self.source_zip_path.read_bytes())
159-
sys.path.append(str(self.zip_path))
160-
self.data = import_module('ziptestdata')
161-
162-
def tearDown(self):
163-
try:
164-
sys.path.remove(str(self.zip_path))
165-
except ValueError:
166-
pass
167-
168-
try:
169-
del sys.path_importer_cache[str(self.zip_path)]
170-
del sys.modules[self.data.__name__]
171-
except KeyError:
172-
pass
173-
174-
try:
175-
unlink(self.zip_path)
176-
except OSError:
177-
# If the test fails, this will probably fail too
178-
pass
179+
temp_dir = self.fixtures.enter_context(os_helper.temp_dir())
180+
self.fixtures.enter_context(zip_on_path(temp_dir))
179181

180182
def test_iterdir_does_not_keep_open(self):
181-
c = [item.name for item in resources.files('ziptestdata').iterdir()]
182-
self.zip_path.unlink()
183-
del c
183+
[item.name for item in resources.files('ziptestdata').iterdir()]
184184

185185
def test_is_file_does_not_keep_open(self):
186-
c = resources.files('ziptestdata').joinpath('binary.file').is_file()
187-
self.zip_path.unlink()
188-
del c
186+
resources.files('ziptestdata').joinpath('binary.file').is_file()
189187

190188
def test_is_file_failure_does_not_keep_open(self):
191-
c = resources.files('ziptestdata').joinpath('not-present').is_file()
192-
self.zip_path.unlink()
193-
del c
189+
resources.files('ziptestdata').joinpath('not-present').is_file()
194190

195191
@unittest.skip("Desired but not supported.")
196192
def test_as_file_does_not_keep_open(self): # pragma: no cover
197-
c = resources.as_file(resources.files('ziptestdata') / 'binary.file')
198-
self.zip_path.unlink()
199-
del c
193+
resources.as_file(resources.files('ziptestdata') / 'binary.file')
200194

201195
def test_entered_path_does_not_keep_open(self):
202196
"""
203197
Mimic what certifi does on import to make its bundle
204198
available for the process duration.
205199
"""
206-
c = resources.as_file(
207-
resources.files('ziptestdata') / 'binary.file'
208-
).__enter__()
209-
self.zip_path.unlink()
210-
del c
200+
resources.as_file(resources.files('ziptestdata') / 'binary.file').__enter__()
211201

212202
def test_read_binary_does_not_keep_open(self):
213-
c = resources.files('ziptestdata').joinpath('binary.file').read_bytes()
214-
self.zip_path.unlink()
215-
del c
203+
resources.files('ziptestdata').joinpath('binary.file').read_bytes()
216204

217205
def test_read_text_does_not_keep_open(self):
218-
c = resources.files('ziptestdata').joinpath('utf-8.file').read_text()
219-
self.zip_path.unlink()
220-
del c
206+
resources.files('ziptestdata').joinpath('utf-8.file').read_text(
207+
encoding='utf-8'
208+
)
221209

222210

223211
class ResourceFromNamespaceTest01(unittest.TestCase):

pytest.ini

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
[pytest]
22
norecursedirs=dist build .tox .eggs
33
addopts=--doctest-modules
4-
doctest_optionflags=ALLOW_UNICODE ELLIPSIS
54
filterwarnings=
5+
## upstream
6+
67
# Ensure ResourceWarnings are emitted
78
default::ResourceWarning
89

@@ -18,3 +19,14 @@ filterwarnings=
1819
ignore:<class 'pytest_flake8.Flake8Item'> is not using a cooperative constructor:pytest.PytestDeprecationWarning
1920
ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestDeprecationWarning
2021
ignore:Flake8Item is an Item subclass and should not be a collector:pytest.PytestWarning
22+
23+
# shopkeep/pytest-black#67
24+
ignore:'encoding' argument not specified::pytest_black
25+
26+
# realpython/pytest-mypy#152
27+
ignore:'encoding' argument not specified::pytest_mypy
28+
29+
# python/cpython#100750
30+
ignore:'encoding' argument not specified::platform
31+
32+
## end upstream

tox.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ toxworkdir={env:TOX_WORK_DIR:.tox}
88

99
[testenv]
1010
deps =
11+
setenv =
12+
PYTHONWARNDEFAULTENCODING = 1
1113
commands =
1214
pytest {posargs}
1315
usedevelop = True
14-
extras = testing
16+
extras =
17+
testing
1518

1619
[testenv:docs]
1720
extras =

0 commit comments

Comments
 (0)
0