Skip to content

Commit ccf3141

Browse files
authored
fix(packaging): Format METADATA correctly if given empty requires_file (#2771)
An empty `requires_file` used to be okay, but at some point regressed to leaving an empty line (due to the `metadata.replace(...)`) in the `METADATA` file - rendering the wheel uninstallable. This PR initially attempted to solve that by introducing a new list that processed `METADATA` lines go into, rather than relying on repeated string replacement. But it seems like the repeated string replace actually did more than simply process one line at a time, so I reverted to a single substitution at the end.
1 parent a0400e9 commit ccf3141

File tree

5 files changed

+51
-2
lines changed

5 files changed

+51
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Unreleased changes template.
9696
* (toolchains) Run the check on the Python interpreter in isolated mode, to ensure it's not affected by userland environment variables, such as `PYTHONPATH`.
9797
* (toolchains) Ensure temporary `.pyc` and `.pyo` files are also excluded from the interpreters repository files.
9898
* (pypi) Run interpreter version call in isolated mode, to ensure it's not affected by userland environment variables, such as `PYTHONPATH`.
99+
* (packaging) An empty `requires_file` is treated as if it were omitted, resulting in a valid `METADATA` file.
99100

100101
{#v0-0-0-added}
101102
### Added

examples/wheel/BUILD.bazel

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,12 @@ starlark # Example comment
294294
""".splitlines(),
295295
)
296296

297+
write_file(
298+
name = "empty_requires_file",
299+
out = "empty_requires.txt",
300+
content = [""],
301+
)
302+
297303
write_file(
298304
name = "extra_requires_file",
299305
out = "extra_requires.txt",
@@ -324,6 +330,15 @@ py_wheel(
324330
deps = [":example_pkg"],
325331
)
326332

333+
py_wheel(
334+
name = "empty_requires_files",
335+
distribution = "empty_requires_files",
336+
python_tag = "py3",
337+
requires_file = ":empty_requires.txt",
338+
version = "0.0.1",
339+
deps = [":example_pkg"],
340+
)
341+
327342
# Package just a specific py_libraries, without their dependencies
328343
py_wheel(
329344
name = "minimal_data_files",
@@ -367,6 +382,7 @@ py_test(
367382
":custom_package_root_multi_prefix",
368383
":custom_package_root_multi_prefix_reverse_order",
369384
":customized",
385+
":empty_requires_files",
370386
":extra_requires",
371387
":filename_escaping",
372388
":minimal_data_files",

examples/wheel/wheel_test.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,6 @@ def test_requires_file_and_extra_requires_files(self):
483483
if line.startswith(b"Requires-Dist:"):
484484
requires.append(line.decode("utf-8").strip())
485485

486-
print(requires)
487486
self.assertEqual(
488487
[
489488
"Requires-Dist: tomli>=2.0.0",
@@ -495,6 +494,29 @@ def test_requires_file_and_extra_requires_files(self):
495494
requires,
496495
)
497496

497+
def test_empty_requires_file(self):
498+
filename = self._get_path("empty_requires_files-0.0.1-py3-none-any.whl")
499+
500+
with zipfile.ZipFile(filename) as zf:
501+
self.assertAllEntriesHasReproducibleMetadata(zf)
502+
metadata_file = None
503+
for f in zf.namelist():
504+
if os.path.basename(f) == "METADATA":
505+
metadata_file = f
506+
self.assertIsNotNone(metadata_file)
507+
508+
metadata = zf.read(metadata_file).decode("utf-8")
509+
metadata_lines = metadata.splitlines()
510+
511+
requires = []
512+
for i, line in enumerate(metadata_lines):
513+
if line.startswith("Name:"):
514+
self.assertTrue(metadata_lines[i + 1].startswith("Version:"))
515+
if line.startswith("Requires-Dist:"):
516+
requires.append(line.strip())
517+
518+
self.assertEqual([], requires)
519+
498520
def test_minimal_data_files(self):
499521
filename = self._get_path("minimal_data_files-0.0.1-py3-none-any.whl")
500522

python/packaging.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ def py_wheel(
101101
102102
Currently only pure-python wheels are supported.
103103
104+
:::{versionchanged} VERSION_NEXT_FEATURE
105+
From now on, an empty `requires_file` is treated as if it were omitted, resulting in a valid
106+
`METADATA` file.
107+
:::
108+
104109
Examples:
105110
106111
```python

tools/wheelmaker.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,12 @@ def get_new_requirement_line(reqs_text, extra):
599599

600600
reqs.append(get_new_requirement_line(reqs_text, extra))
601601

602-
metadata = metadata.replace(meta_line, "\n".join(reqs))
602+
if reqs:
603+
metadata = metadata.replace(meta_line, "\n".join(reqs))
604+
# File is empty
605+
# So replace the meta_line entirely, including removing newline chars
606+
else:
607+
metadata = re.sub(re.escape(meta_line) + r"(?:\r?\n)?", "", metadata, count=1)
603608

604609
maker.add_metadata(
605610
metadata=metadata,

0 commit comments

Comments
 (0)