Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compatibility kludge to work with older packaging #1217

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ jobs:
- ubuntu-latest
- macos-latest
- windows-latest
tox-environment:
- py
include:
# Test with the oldest supported ``packaging`` version.
- platform: ubuntu-latest
python-version: "3.8"
tox-environment: py-packaging240
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4.2.2
Expand All @@ -59,7 +66,7 @@ jobs:
- name: Run type-checking
run: python -m tox -e types
- name: Run tests
run: python -m tox -e py
run: python -m tox -e ${{ matrix.tox-environment }}

# Because the tests can be flaky, they shouldn't be required for merge, but
# it's still helpful to run them on PRs. See:
Expand Down
2 changes: 2 additions & 0 deletions changelog/1217.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix compatibility kludge for invalid License-File metadata entries emitted by
build backends to work also with ``packaging`` version 24.0.
41 changes: 41 additions & 0 deletions tests/fixtures/everything.metadata23
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Metadata-Version: 2.3
Name: BeagleVote
Version: 1.0a2
Platform: ObscureUnix
Platform: RareDOS
Supported-Platform: RedHat 7.2
Supported-Platform: i386-win32-2791
Summary: A module for collecting votes from beagles.
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Keywords: dog,puppy,voting,election
Home-page: http://www.example.com/~cschultz/bvote/
Download-URL: …/BeagleVote-0.45.tgz
Author: C. Schultz, Universal Features Syndicate,
Los Angeles, CA <cschultz@peanuts.example.com>
Author-email: "C. Schultz" <cschultz@example.com>
Maintainer: C. Schultz, Universal Features Syndicate,
Los Angeles, CA <cschultz@peanuts.example.com>
Maintainer-email: "C. Schultz" <cschultz@example.com>
License: This software may only be obtained by sending the
author a postcard, and then the user promises not
to redistribute it.
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console (Text Based)
Provides-Extra: pdf
Requires-Dist: reportlab; extra == 'pdf'
Requires-Dist: pkginfo
Requires-Dist: PasteDeploy
Requires-Dist: zope.interface (>3.5.0)
Requires-Dist: pywin32 >1.0; sys_platform == 'win32'
Requires-Python: >=3
Requires-External: C
Requires-External: libpng (>=1.5)
Requires-External: make; sys_platform != "win32"
Project-URL: Bug Tracker, http://bitbucket.org/tarek/distribute/issues/
Project-URL: Documentation, https://example.com/BeagleVote
Provides-Dist: OtherProject
Provides-Dist: AnotherProject (3.4)
Provides-Dist: virtual_package; python_version >= "3.4"
Dynamic: Obsoletes-Dist

This description intentionally left blank.
57 changes: 34 additions & 23 deletions tests/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
import json
import string

import packaging
import pretend
import pytest
from packaging import metadata
from packaging import version

from twine import exceptions
from twine import package as package_file
Expand Down Expand Up @@ -175,6 +177,10 @@ def test_package_safe_name_is_correct(pkg_name, expected_name):
def test_metadata_keys_consistency():
"""Check that the translation keys exist in the respective ``TypedDict``."""
raw_keys = metadata.RawMetadata.__annotations__.keys()
if version.Version(packaging.__version__) < version.Version("24.1"):
# ``packaging`` version 24.0 and earlier do not know about the
# License-Expression and License-File metadata fields yet.
raw_keys = raw_keys | set(("license_files", "license_expression"))
assert set(package_file._RAW_TO_PACKAGE_METADATA.keys()).issubset(raw_keys)
package_keys = package_file.PackageMetadata.__annotations__.keys()
assert set(package_file._RAW_TO_PACKAGE_METADATA.values()).issubset(package_keys)
Expand Down Expand Up @@ -434,32 +440,37 @@ def test_package_from_unrecognized_file_error():
assert "Unknown distribution format" in err.value.args[0]


@pytest.mark.parametrize(
"read_data, filtered",
[
pytest.param(
"Metadata-Version: 2.1\n"
"Name: test-package\n"
"Version: 1.0.0\n"
"License-File: LICENSE\n",
True,
id="invalid License-File",
),
pytest.param(
"Metadata-Version: 2.4\n"
"Name: test-package\n"
"Version: 1.0.0\n"
"License-File: LICENSE\n",
False,
id="valid License-File",
),
],
)
def test_setuptools_license_file(read_data, filtered, monkeypatch):
def test_setuptools_license_file_invalid(monkeypatch):
"""Drop License-File metadata entries if Metadata-Version is less than 2.4."""
read_data = (
"Metadata-Version: 2.1\n"
"Name: test-package\n"
"Version: 1.0.0\n"
"License-File: LICENSE\n"
)
monkeypatch.setattr(package_file.wheel.Wheel, "read", lambda _: read_data)
filename = "tests/fixtures/twine-1.5.0-py2.py3-none-any.whl"

package = package_file.PackageFile.from_filename(filename, comment=None)
meta = package.metadata_dictionary()
assert "license_file" not in meta


@pytest.mark.skipif(
version.Version(packaging.__version__) < version.Version("24.1"),
reason="packaging is too old",
)
def test_setuptools_license_file_valid(monkeypatch):
"""License-File metadata entries are kept when Metadata-Version is 2.4."""
read_data = (
"Metadata-Version: 2.4\n"
"Name: test-package\n"
"Version: 1.0.0\n"
"License-File: LICENSE\n"
)
monkeypatch.setattr(package_file.wheel.Wheel, "read", lambda _: read_data)
filename = "tests/fixtures/twine-1.5.0-py2.py3-none-any.whl"

package = package_file.PackageFile.from_filename(filename, comment=None)
meta = package.metadata_dictionary()
assert filtered != ("license_file" in meta)
assert "license_file" in meta
10 changes: 8 additions & 2 deletions tests/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
import logging
from contextlib import contextmanager

import packaging
import pretend
import pytest
import requests
from packaging import version

from twine import package
from twine import repository
Expand Down Expand Up @@ -61,8 +63,12 @@ def test_iterables_are_flattened():

def test_all_metadata_fields_are_flattened(monkeypatch):
"""Verify that package metadata fields are correctly flattened."""
# This file contains all metadata fields known up to metadata version 2.4.
metadata = open("tests/fixtures/everything.metadata")
if version.Version(packaging.__version__) < version.Version("24.1"):
# All metadata fields up to metadata version 2.3
metadata = open("tests/fixtures/everything.metadata23")
else:
# All metadata fields up to metadata version 2.4
metadata = open("tests/fixtures/everything.metadata24")
monkeypatch.setattr(package.wheel.Wheel, "read", metadata.read)
filename = "tests/fixtures/twine-1.5.0-py2.py3-none-any.whl"
data = package.PackageFile.from_filename(
Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
minversion = 3.8
envlist = lint,types,py{38,39,310,311,312,313},integration,docs
envlist = lint,types,py{38,39,310,311,312,313}{,-packaging240},integration,docs
isolated_build = True

[testenv]
Expand All @@ -9,6 +9,7 @@ deps =
pytest
pytest-socket
coverage
packaging240: packaging==24.0
passenv =
PYTEST_ADDOPTS
setenv =
Expand Down
25 changes: 17 additions & 8 deletions twine/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,7 @@ def from_filename(cls, filename: str, comment: Optional[str]) -> "PackageFile":

# Parse and validate metadata.
meta, unparsed = metadata.parse_email(data)
if unparsed:
raise exceptions.InvalidDistribution(
"Invalid distribution metadata: {}".format(
"; ".join(
f"unrecognized or malformed field {key!r}" for key in unparsed
)
)
)

# setuptools emits License-File metadata fields while declaring
# Metadata-Version 2.1. This is invalid because the metadata
# specification does not allow to add arbitrary fields, and because
Expand All @@ -232,6 +225,22 @@ def from_filename(cls, filename: str, comment: Optional[str]) -> "PackageFile":
# than 2.4.
if version.Version(meta.get("metadata_version", "0")) < version.Version("2.4"):
meta.pop("license_files", None)
# Support for metadata version 2.4 requires packaging version 24.1
# or later. When parsing metadata with an older packaging, the
# invalid License-File fields are not understood and added to the
# unparsed dictionary. Remove them to avoid triggering the
# following check.
unparsed.pop("license-file", None)

if unparsed:
raise exceptions.InvalidDistribution(
"Invalid distribution metadata: {}".format(
"; ".join(
f"unrecognized or malformed field {key!r}" for key in unparsed
)
)
)

try:
metadata.Metadata.from_raw(meta)
except metadata.ExceptionGroup as group:
Expand Down
Loading