Repository: pypa/twine
Pull request #1222 (2025-01-24 19:43:33)

build(deps): bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.12.3 to 1.12.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/pypa/gh-action-pypi-publish/releases">pypa/gh-action-pypi-publish's releases</a>.</em></p>
<blockquote>
<h2>v1.12.4</h2>
<!-- raw HTML omitted -->
<h2>✨ What's Changed</h2>
<p>The main theme of this patch release that the support for uploading <a href="https://packaging.python.org/en/latest/specifications/core-metadata/#license-expression">PEP 639 licensing metadata</a> to PyPI has been fixed in <a href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/327">#327</a>.</p>
<h2>🛠️ Internal Updates</h2>
<p>A few smaller updates include the attestation existence being checked earlier in the process now, listing all the violating files together, not just one (PR <a href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/315">#315</a>).
And the lock file with the software available in runtime has been re-pinned in <a href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/329">#329</a>.
Additionally, the CI now runs the smoke-tests against both Ubuntu 22.04 and 24.04 explicitly via da900af96347cc027433720ad4f122117645459d.</p>
<p><strong>🪞 Full Diff</strong>: <a href="https://github.com/pypa/gh-action-pypi-publish/compare/v1.12.3...v1.12.4">https://github.com/pypa/gh-action-pypi-publish/compare/v1.12.3...v1.12.4</a></p>
<p><strong>🧔‍♂️ Release Manager:</strong> <a href="https://github.com/sponsors/webknjaz"><code>@​webknjaz</code></a> <a href="https://stand-with-ukraine.pp.ua">🇺🇦</a></p>
<p><strong>🙏 Special Thanks</strong> to <a href="https://github.com/dnicolodi"><code>@​dnicolodi</code></a><a href="https://github.com/sponsors/dnicolodi">💰</a> and <a href="https://github.com/woodruffw"><code>@​woodruffw</code></a><a href="https://github.com/sponsors/woodruffw">💰</a> for releasing the license metadata support fix in Twine!</p>
<p><strong>💬 Discuss</strong> <a href="https://bsky.app/profile/webknjaz.me/post/3lghhtwey522z">on Bluesky 🦋</a>, <a href="https://mastodon.social/@webknjaz/113881243992941874">on Mastodon 🐘</a> and <a href="https://github.com/pypa/gh-action-pypi-publish/discussions/330">on GitHub</a>.</p>
<p><a href="https://github.com/sponsors/webknjaz"><img src="https://img.shields.io/badge/%40webknjaz-transparent?logo=githubsponsors&amp;logoColor=%23EA4AAA&amp;label=Sponsor&amp;color=2a313c" alt="GH Sponsors badge" /></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/76f52bc884231f62b9a034ebfe128415bbaabdfc"><code>76f52bc</code></a> Merge pull request <a href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/329">#329</a> from webknjaz/maintenance/runtime-lockfile-24-02-2025</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/72de13b11d3d3407c18160a2fed48a095082a875"><code>72de13b</code></a> 📌 Mass-upgrade transitive dependency pins</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/1995f2e046709561040c91d6e926611f308b69ed"><code>1995f2e</code></a> Merge pull request <a href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/327">#327</a> from webknjaz/maintenance/twine-6.1-pep639</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/29f40bd9f907432a646ecd9a54e7b6640eddf1bc"><code>29f40bd</code></a> 📦 Enable metadata 2.4 support in Twine</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/10df67dae045599d4687ac5a225e5a0bf311f033"><code>10df67d</code></a> 📦 Enable support for PEP 639 metadata</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/e0449d218c594c5fc8e8f39778fd566354610b58"><code>e0449d2</code></a> 🧪 Integrate a unified <code>alls-green</code> GHA status</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/cebc64f283e4649cec01dbdeda3437192dc10d14"><code>cebc64f</code></a> 🧪 Bump setuptools in smoke test to v75.8.0</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/da900af96347cc027433720ad4f122117645459d"><code>da900af</code></a> 🧪 Run smoke tests against Ubuntu 24 and 22</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/8cafb5c2bf2f478231c9abbba1feb4edb6ccf405"><code>8cafb5c</code></a> 💰 Sync the funding config</li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish/commit/916e57631f04a497e4bec0e29e80684e45b4305e"><code>916e576</code></a> Merge pull request <a href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/315">#315</a> from webknjaz/refactoring/attestations-exist-bundle</li>
<li>Additional commits viewable in <a href="https://github.com/pypa/gh-action-pypi-publish/compare/67339c736fd9354cd4f8cb0b744f2b82a74b5c70...76f52bc884231f62b9a034ebfe128415bbaabdfc">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pypa/gh-action-pypi-publish&package-manager=github_actions&previous-version=1.12.3&new-version=1.12.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

> **Note**
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.



Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 5d07d19..2b872d6 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -15,8 +15,8 @@ concurrency:
 env:
   FORCE_COLOR: "1"
   TOX_TESTENV_PASSENV: "FORCE_COLOR"
-  MIN_PYTHON_VERSION: "3.8"
-  DEFAULT_PYTHON_VERSION: "3.10"
+  MIN_PYTHON_VERSION: "3.9"
+  DEFAULT_PYTHON_VERSION: "3.11"
 
 permissions:
   contents: read
@@ -26,7 +26,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4.2.2
-      - uses: actions/setup-python@v5.3.0
+      - uses: actions/setup-python@v5.5.0
         with:
           python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
       - name: Install dependencies
@@ -38,7 +38,6 @@ jobs:
     strategy:
       matrix:
         python-version:
-          - "3.8"
           - "3.9"
           - "3.10"
           - "3.11"
@@ -48,10 +47,17 @@ jobs:
           - ubuntu-latest
           - macos-latest
           - windows-latest
+        tox-environment:
+          - py
+        include:
+          # Test with the oldest supported ``packaging`` version.
+          - platform: ubuntu-latest
+            python-version: "3.9"
+            tox-environment: py-packaging240
     runs-on: ${{ matrix.platform }}
     steps:
       - uses: actions/checkout@v4.2.2
-      - uses: actions/setup-python@v5.3.0
+      - uses: actions/setup-python@v5.5.0
         with:
           python-version: ${{ matrix.python-version }}
       - name: Install dependencies
@@ -59,7 +65,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:
@@ -69,7 +75,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4.2.2
-      - uses: actions/setup-python@v5.3.0
+      - uses: actions/setup-python@v5.5.0
         with:
           python-version: ${{ env.MIN_PYTHON_VERSION }}
       - name: Install dependencies
@@ -81,7 +87,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4.2.2
-      - uses: actions/setup-python@v5.3.0
+      - uses: actions/setup-python@v5.5.0
         with:
           python-version: ${{ env.MIN_PYTHON_VERSION }}
       - name: Install dependencies
@@ -115,7 +121,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4.2.2
-      - uses: actions/setup-python@v5.3.0
+      - uses: actions/setup-python@v5.5.0
         with:
           python-version: ${{ env.MIN_PYTHON_VERSION }}
       - name: Install dependencies
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e0d5ae3..96be391 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -22,7 +22,7 @@ jobs:
         uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683"
 
       - name: "Setup Python"
-        uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b"
+        uses: "actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55"
         with:
           python-version: "3.x"
 
@@ -40,7 +40,7 @@ jobs:
           cd dist && echo "hashes=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT
 
       - name: "Upload dists"
-        uses: "actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08"
+        uses: "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02"
         with:
           name: "dist"
           path: "dist/"
@@ -53,7 +53,7 @@ jobs:
       actions: read
       contents: write
       id-token: write # Needed to access the workflow's OIDC identity.
-    uses: "slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0"
+    uses: "slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0"
     with:
       base64-subjects: "${{ needs.build.outputs.hashes }}"
       upload-assets: true
@@ -70,10 +70,10 @@ jobs:
 
     steps:
     - name: "Download dists"
-      uses: "actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16"
+      uses: "actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e"
       with:
         name: "dist"
         path: "dist/"
 
     - name: "Publish dists to PyPI"
-      uses: "pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70"
+      uses: "pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc"
diff --git a/changelog/1217.bugfix.rst b/changelog/1217.bugfix.rst
new file mode 100644
index 0000000..46a34e3
--- /dev/null
+++ b/changelog/1217.bugfix.rst
@@ -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.
diff --git a/changelog/1224.bugfix.rst b/changelog/1224.bugfix.rst
new file mode 100644
index 0000000..7883dcc
--- /dev/null
+++ b/changelog/1224.bugfix.rst
@@ -0,0 +1,1 @@
+Fix a couple of incorrectly rendered error messages.
diff --git a/changelog/1229.bugfix.rst b/changelog/1229.bugfix.rst
new file mode 100644
index 0000000..a5dc0ac
--- /dev/null
+++ b/changelog/1229.bugfix.rst
@@ -0,0 +1,2 @@
+``twine`` now enforces ``keyring >= 21.2.0``, which was previously
+implicitly required by API usage.
diff --git a/pyproject.toml b/pyproject.toml
index 62c32c0..33ad884 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,7 +21,6 @@ classifiers = [
 	"Programming Language :: Python",
 	"Programming Language :: Python :: 3",
 	"Programming Language :: Python :: 3 :: Only",
-	"Programming Language :: Python :: 3.8",
 	"Programming Language :: Python :: 3.9",
 	"Programming Language :: Python :: 3.10",
 	"Programming Language :: Python :: 3.11",
@@ -29,7 +28,7 @@ classifiers = [
 	"Programming Language :: Python :: 3.13",
 	"Programming Language :: Python :: Implementation :: CPython",
 ]
-requires-python = ">=3.8"
+requires-python = ">=3.9"
 dependencies = [
 	"readme-renderer >= 35.0",
 	"requests >= 2.20",
@@ -37,7 +36,7 @@ dependencies = [
 	"urllib3 >= 1.26.0",
 	"importlib-metadata >= 3.6; python_version < '3.10'",
 	# workaround for missing binaries on these platforms, see #1158
-	"keyring >= 15.1; platform_machine != 'ppc64le' and platform_machine != 's390x'",
+	"keyring >= 21.2.0; platform_machine != 'ppc64le' and platform_machine != 's390x'",
 	"rfc3986 >= 1.4.0",
 	"rich >= 12.0.0",
 	"packaging >= 24.0",
@@ -61,7 +60,7 @@ upload = "twine.commands.upload:main"
 register = "twine.commands.register:main"
 
 [project.optional-dependencies]
-keyring = ["keyring >= 15.1"]
+keyring = ["keyring >= 21.2.0"]
 
 [project.scripts]
 twine = "twine.__main__:main"
diff --git a/tox.ini b/tox.ini
index bf36c31..3789e33 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
-minversion = 3.8
-envlist = lint,types,py{38,39,310,311,312,313},integration,docs
+minversion = 3.9
+envlist = lint,types,py{39,310,311,312,313}{,-packaging240},integration,docs
 isolated_build = True
 
 [testenv]
@@ -9,6 +9,7 @@ deps =
     pytest
     pytest-socket
     coverage
+    packaging240: packaging==24.0
 passenv =
     PYTEST_ADDOPTS
 setenv =
diff --git a/twine/package.py b/twine/package.py
index 831bcdb..3bd28d0 100644
--- a/twine/package.py
+++ b/twine/package.py
@@ -214,14 +214,7 @@ class 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
@@ -232,6 +225,22 @@ class 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:
diff --git a/twine/sdist.py b/twine/sdist.py
index 4808e88..06a121e 100644
--- a/twine/sdist.py
+++ b/twine/sdist.py
@@ -40,14 +40,14 @@ class TarGzSDist(SDist):
             root = os.path.commonpath(sdist.getnames())
             if root in {".", "/", ""}:
                 raise exceptions.InvalidDistribution(
-                    "Too many top-level members in sdist archive: {self.filename}"
+                    f"Too many top-level members in sdist archive: {self.filename}"
                 )
             # ...containing the package metadata in a ``PKG-INFO`` file.
             with suppress(KeyError):
                 member = sdist.getmember(root.rstrip("/") + "/PKG-INFO")
                 if not member.isfile():
                     raise exceptions.InvalidDistribution(
-                        "PKG-INFO is not a regular file: {self.filename}"
+                        f"PKG-INFO is not a regular file: {self.filename}"
                     )
                 fd = sdist.extractfile(member)
                 assert fd is not None, "for mypy"
@@ -69,7 +69,7 @@ class ZipSDist(SDist):
             root = os.path.commonpath(sdist.namelist())
             if root in {".", "/", ""}:
                 raise exceptions.InvalidDistribution(
-                    "Too many top-level members in sdist archive: {self.filename}"
+                    f"Too many top-level members in sdist archive: {self.filename}"
                 )
             # ...containing the package metadata in a ``PKG-INFO`` file.
             with suppress(KeyError):


Test patch:
diff --git a/tests/fixtures/everything.metadata23 b/tests/fixtures/everything.metadata23
new file mode 100644
index 0000000..2e02c7c
--- /dev/null
+++ b/tests/fixtures/everything.metadata23
@@ -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.
diff --git a/tests/fixtures/everything.metadata b/tests/fixtures/everything.metadata24
similarity index 100%
rename from tests/fixtures/everything.metadata
rename to tests/fixtures/everything.metadata24
diff --git a/tests/test_package.py b/tests/test_package.py
index d843fa1..78f40a5 100644
--- a/tests/test_package.py
+++ b/tests/test_package.py
@@ -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
@@ -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)
@@ -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
diff --git a/tests/test_repository.py b/tests/test_repository.py
index 7526a36..e89a371 100644
--- a/tests/test_repository.py
+++ b/tests/test_repository.py
@@ -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
@@ -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(
diff --git a/tests/test_sdist.py b/tests/test_sdist.py
index a619dfe..da8fa23 100644
--- a/tests/test_sdist.py
+++ b/tests/test_sdist.py
@@ -141,7 +141,10 @@ def test_pkg_info_not_regular_file(tmp_path):
         },
     )
 
-    with pytest.raises(exceptions.InvalidDistribution, match="PKG-INFO is not a reg"):
+    with pytest.raises(
+        exceptions.InvalidDistribution,
+        match=r"^PKG-INFO is not a reg.*test-1.2.3.tar.gz$",
+    ):
         sdist.SDist(str(filepath)).read()
 
 
@@ -162,7 +165,10 @@ def test_multiple_top_level(archive_format, tmp_path):
         },
     )
 
-    with pytest.raises(exceptions.InvalidDistribution, match="Too many top-level"):
+    with pytest.raises(
+        exceptions.InvalidDistribution,
+        match=r"^Too many top-level.*test-1.2.3.(tar.gz|zip)$",
+    ):
         sdist.SDist(str(filepath)).read()