Skip to content

Commit ecf7e91

Browse files
committed
Fix handling of continuation lines in Description metadata field
Fixes #1218.
1 parent aa3a910 commit ecf7e91

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

changelog/1220.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix handling of multiline ``Description`` metadata fields.

tests/test_package.py

+55
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,58 @@ def test_setuptools_license_file(read_data, filtered, monkeypatch):
463463
package = package_file.PackageFile.from_filename(filename, comment=None)
464464
meta = package.metadata_dictionary()
465465
assert filtered != ("license_file" in meta)
466+
467+
468+
@pytest.mark.parametrize(
469+
"description,expected",
470+
[
471+
# fmt: off
472+
pytest.param(
473+
"\n"
474+
"Two\n"
475+
"Lines\n",
476+
"Two\nLines\n",
477+
id="body",
478+
),
479+
pytest.param(
480+
"Description: Two\n"
481+
" |Lines\n"
482+
" |\n",
483+
"Two\nLines\n",
484+
id="multiline-header",
485+
),
486+
pytest.param(
487+
"Description: Two\n"
488+
" Lines\n"
489+
" \n",
490+
"Two\nLines\n",
491+
id="multiline-header-setuptools",
492+
),
493+
pytest.param(
494+
"Description: Two\n"
495+
" Lines\n"
496+
" Maybe Three",
497+
"Two\n Lines\n Maybe Three",
498+
id="multiline-inconsistent",
499+
),
500+
pytest.param(
501+
"Description: Two\n"
502+
" |Lines\n"
503+
" Maybe Three",
504+
"Two\n |Lines\n Maybe Three",
505+
id="multiline-mixed",
506+
),
507+
# fmt: on
508+
],
509+
)
510+
def test_description_field_continuation(description, expected, monkeypatch):
511+
"""License-File metadata entries are kept when Metadata-Version is 2.4."""
512+
read_data = (
513+
"Metadata-Version: 2.4\n"
514+
"Name: test-package\n"
515+
"Version: 1.0.0\n" + description
516+
)
517+
monkeypatch.setattr(package_file.wheel.Wheel, "read", lambda _: read_data)
518+
filename = "tests/fixtures/twine-1.5.0-py2.py3-none-any.whl"
519+
package = package_file.PackageFile.from_filename(filename, comment=None)
520+
assert package.metadata["description"] == expected

twine/package.py

+27
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,28 @@ def _safe_name(name: str) -> str:
6565
return re.sub("[^A-Za-z0-9.]+", "-", name)
6666

6767

68+
def _dedent(string: str) -> str:
69+
"""Remove line continuation suffix used for the ``Description`` metadata field.
70+
71+
The metadata standard prescribes that continuation lines should be
72+
prefixed with 7 spaces followed by a pipe character, however, setuptools
73+
used to prefix continuation lines with 8 spaces. Handle both here.
74+
75+
If not all lines following the first start with either of the accepted
76+
prefixes, the string is returned without modifications.
77+
"""
78+
lines = string.splitlines()
79+
if (
80+
False # This is here only to force black into something sensible.
81+
or all(line.startswith(" |") for line in lines[1:])
82+
or all(line.startswith(" ") for line in lines[1:])
83+
):
84+
for i in range(1, len(lines)):
85+
lines[i] = lines[i][8:]
86+
return "\n".join(lines)
87+
return string
88+
89+
6890
# Map ``metadata.RawMetadata`` fields to ``PackageMetadata`` fields. Some
6991
# fields are renamed to match the names expected in the upload form.
7092
_RAW_TO_PACKAGE_METADATA = {
@@ -232,6 +254,11 @@ def from_filename(cls, filename: str, comment: Optional[str]) -> "PackageFile":
232254
# than 2.4.
233255
if version.Version(meta.get("metadata_version", "0")) < version.Version("2.4"):
234256
meta.pop("license_files", None)
257+
258+
description = meta.get("description", None)
259+
if description is not None:
260+
meta["description"] = _dedent(description)
261+
235262
try:
236263
metadata.Metadata.from_raw(meta)
237264
except metadata.ExceptionGroup as group:

0 commit comments

Comments
 (0)