Skip to content

Commit 56e4dba

Browse files
authored
Merge pull request #1 from python-packaging/sdist-without-requires
Handle sdist files without requirements
2 parents 91b440c + 1940706 commit 56e4dba

File tree

4 files changed

+35
-16
lines changed

4 files changed

+35
-16
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,9 @@ jobs:
2929
- name: Install
3030
run: |
3131
python -m pip install --upgrade pip
32-
make setup
3332
pip install -U .
3433
- name: Test
35-
run: make test
36-
- name: Lint
37-
run: make lint
34+
run: python -m metadata_please.tests
3835

3936
metadata_please:
4037
runs-on: ${{ matrix.os }}

metadata_please/sdist.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ def from_zip_sdist(zf: ZipFile) -> bytes:
1212
"""
1313
requires = [f for f in zf.namelist() if f.endswith("/requires.txt")]
1414
requires.sort(key=len)
15+
if not requires:
16+
return b""
17+
1518
data = zf.read(requires[0])
1619
assert data is not None
17-
requires, extras = convert_sdist_requires(data.decode("utf-8"))
20+
requires_lines, extras = convert_sdist_requires(data.decode("utf-8"))
1821

1922
buf: list[str] = []
20-
for req in requires:
23+
for req in requires_lines:
2124
buf.append(f"Requires-Dist: {req}\n")
2225
for extra in sorted(extras):
2326
buf.append(f"Provides-Extra: {extra}\n")
@@ -27,6 +30,9 @@ def from_zip_sdist(zf: ZipFile) -> bytes:
2730
def basic_metadata_from_zip_sdist(zf: ZipFile) -> BasicMetadata:
2831
requires = [f for f in zf.namelist() if f.endswith("/requires.txt")]
2932
requires.sort(key=len)
33+
if not requires:
34+
return BasicMetadata((), frozenset())
35+
3036
data = zf.read(requires[0])
3137
assert data is not None
3238
return BasicMetadata.from_sdist_pkg_info_and_requires(b"", data)
@@ -39,14 +45,16 @@ def from_tar_sdist(tf: TarFile) -> bytes:
3945
# XXX Why do ZipFile and TarFile not have a common interface ?!
4046
requires = [f for f in tf.getnames() if f.endswith("/requires.txt")]
4147
requires.sort(key=len)
48+
if not requires:
49+
return b""
4250

4351
fo = tf.extractfile(requires[0])
4452
assert fo is not None
4553

46-
requires, extras = convert_sdist_requires(fo.read().decode("utf-8"))
54+
requires_lines, extras = convert_sdist_requires(fo.read().decode("utf-8"))
4755

4856
buf: list[str] = []
49-
for req in requires:
57+
for req in requires_lines:
5058
buf.append(f"Requires-Dist: {req}\n")
5159
for extra in sorted(extras):
5260
buf.append(f"Provides-Extra: {extra}\n")
@@ -57,6 +65,8 @@ def basic_metadata_from_tar_sdist(tf: TarFile) -> BasicMetadata:
5765
# XXX Why do ZipFile and TarFile not have a common interface ?!
5866
requires = [f for f in tf.getnames() if f.endswith("/requires.txt")]
5967
requires.sort(key=len)
68+
if not requires:
69+
return BasicMetadata((), frozenset())
6070

6171
fo = tf.extractfile(requires[0])
6272
assert fo is not None

metadata_please/tests/sdist.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,23 @@ def test_basic_metadata(self) -> None:
4141
)
4242
bm = basic_metadata_from_zip_sdist(z) # type: ignore
4343
self.assertEqual(
44-
["a", "b; extra == 'e'"],
44+
("a", "b; extra == 'e'"),
4545
bm.reqs,
4646
)
4747
self.assertEqual({"e"}, bm.provides_extra)
4848

49+
def test_basic_metadata_no_requires_file(self) -> None:
50+
z = MemoryZipFile(
51+
["foo.egg-info/PKG-INFO", "foo/__init__.py"],
52+
read_value=b"\n",
53+
)
54+
bm = basic_metadata_from_zip_sdist(z) # type: ignore
55+
self.assertEqual(
56+
(),
57+
bm.reqs,
58+
)
59+
self.assertEqual(set(), bm.provides_extra)
60+
4961
def test_basic_metadata_absl_py_09(self) -> None:
5062
z = MemoryZipFile(
5163
["foo.egg-info/requires.txt", "foo/__init__.py"],
@@ -60,12 +72,12 @@ def test_basic_metadata_absl_py_09(self) -> None:
6072
)
6173
bm = basic_metadata_from_zip_sdist(z) # type: ignore
6274
self.assertEqual(
63-
[
75+
(
6476
"six",
6577
'enum34; python_version < "3.4"',
6678
# Quoting on the following line is an implementation detail
6779
"pytest; (python_version < \"3.4\") and extra == 'test'",
68-
],
80+
),
6981
bm.reqs,
7082
)
7183
self.assertEqual({"test"}, bm.provides_extra)
@@ -102,7 +114,7 @@ def test_basic_metadata(self) -> None:
102114
)
103115
bm = basic_metadata_from_tar_sdist(t) # type: ignore
104116
self.assertEqual(
105-
["a", "b; extra == 'e'"],
117+
("a", "b; extra == 'e'"),
106118
bm.reqs,
107119
)
108120
self.assertEqual({"e"}, bm.provides_extra)

metadata_please/types.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ class BasicMetadata:
1111
# Popualted from Requires-Dist or requires.txt
1212
reqs: Sequence[str]
1313
# Populated from Provides-Extra
14-
provides_extra: set[str]
14+
provides_extra: frozenset[str]
1515

1616
@classmethod
1717
def from_metadata(cls, metadata: bytes) -> BasicMetadata:
1818
msg = message_from_string(metadata.decode("utf-8"))
1919
return BasicMetadata(
2020
msg.get_all("Requires-Dist") or (),
21-
set(msg.get_all("Provides-Extra") or ()),
21+
frozenset(msg.get_all("Provides-Extra") or ()),
2222
)
2323

2424
@classmethod
@@ -33,7 +33,7 @@ def from_sdist_pkg_info_and_requires(
3333
)
3434

3535

36-
def convert_sdist_requires(data: str) -> tuple[list[str], set[str]]:
36+
def convert_sdist_requires(data: str) -> tuple[tuple[str, ...], frozenset[str]]:
3737
# This is reverse engineered from looking at a couple examples, but there
3838
# does not appear to be a formal spec. Mentioned at
3939
# https://setuptools.readthedocs.io/en/latest/formats.html#requires-txt
@@ -64,4 +64,4 @@ def convert_sdist_requires(data: str) -> tuple[list[str], set[str]]:
6464
lst.append(f"{line}; {current_markers}")
6565
else:
6666
lst.append(line)
67-
return lst, extras
67+
return tuple(lst), frozenset(extras)

0 commit comments

Comments
 (0)