What's Changed
Features
- Add a public
VersionRangeAPI andSpecifierSet.to_range(), representing the versions a specifier set accepts as an interval set that supports intersection, union, difference, complement, set relations, membership tests, and filtering.VersionRange.to_specifier_set()converts a range back to aSpecifierSetwhere a PEP 440 form exists. (#1267, #1270, #1298) - PEP 808: accept
Metadata-Version: 2.6. (#1194) - Add a
limitargument toparse_tag()for compressed tag sets. (#1220) - Add a
prefer_sdist_predicateargument toPylock.select()to prefer source distributions over wheels for selected packages. (#1334) - Add
pure_python_tags()to generate the pure-Python tags for a Python version without touching the running platform. (#1346) - Add
SpecifierSet.is_subset(),SpecifierSet.is_superset(), andSpecifierSet.is_disjoint(), which compare the versions two specifier sets accept. (#1313)
Behavior adaptations
- Drop support for Python 3.8; packaging now requires Python 3.9 or later. (#1157)
- Prefer native
linux_*platform tags overmanylinuxandmusllinuxtags on Linux. (#160)
Fixes for versions and specifiers
- Raise
InvalidVersioninstead ofTypeErrorwhenVersionis given a non-string. (#1319) - Raise
InvalidVersionfor non-string pre-release letters passed toVersion.from_parts. (#1241) - Fix an
AttributeErrorwhen hashing internally trimmed versions. (#1242) - Fix
SpecifierSet.is_unsatisfiablefor post-release boundary intersections. (#1257)
Fixes for requirements and markers
- Make
Requirement.__hash__consistent with__eq__for trailing-zero-equivalent specifiers (e.g.foo==1.0.0andfoo==1.0.0.0), so equal requirements hash equal and deduplicate in sets and dicts. (#1232) - Normalize requested extra names before comparing or hashing requirements. (#644)
- Preserve a
Requirement's specifierprereleasesoverride across a pickle round trip. (#1204) - Raise
InvalidRequirementinstead ofInvalidSpecifierwhen a requirement contains an invalid specifier. (#1332) - Clarify the error for post-release prefix wildcards like
==1.0.post1.*. (#1299) - Preserve quoting semantics when serializing marker values, so round-tripped markers parse back to the same marker. (#1213)
- Keep the parentheses of a nested group when serializing markers. (#1316)
- Normalize
extraanddependency_groupsvalues in nested markers at parse time. (#1246, #1310) - Raise
UndefinedComparisonwhen a set-valued variable likeextrasis used outside the membership form. (#1265) - Raise
UndefinedEnvironmentName(aKeyErrorsubclass) for missing environment keys during marker evaluation. (#1276) - Wrap malformed string literal errors in
InvalidMarker/InvalidRequirementinstead of leaking a low-level error. (#1249) - Reject requirements and markers with a trailing line break. (#1345)
Fixes for metadata and licenses
- Collect all
from_emailvalidation errors into oneExceptionGroupinstead of raising the first. (#1268) - Accept the UTF-8 charset case-insensitively in email payloads. (#1330)
- Reject malformed
Description-Content-Typevalues. (#1329) - Don't rewrite user values that contain
{field}placeholders in error messages. (#1327) - Route multipart email payloads to
unparsedinstead of asserting. (#1247) - Make
InvalidMetadataandCyclicDependencyGrouppicklable. (#1328) - Fold every line boundary
str.splitlinesrecognizes when writing a header withRFC822Message. (#1356) - Raise
InvalidLicenseExpressionfor misplacedWITHclauses and emptyLicenseRef-names. (#1266) - Raise
InvalidLicenseExpressioninstead ofKeyErrorfor aLicenseRef-with a+suffix. (#1219)
Fixes for tags and filenames
- Raise
InvalidTagfromparse_tag()for tags with the wrong number of components. (#1238) - Reject empty tag components in
parse_wheel_filename()andparse_tag(). (#1234) - Reject an empty project name in the wheel and sdist filename parsers. (#1305)
- Reject wheel filenames with a trailing newline. (#1341)
- Reject wheel tags whose interpreter component is not an identifier. (#577)
is_normalized_namenow rejects names with collapsed double hyphens likea--b. (#1230)- Fix duplicate explicit
abi3ttags. (#1245) - Forward the
warnargument togeneric_tags()insys_tags(). (#1264) - Raise
SystemErrorfor an empty or malformed CPythonEXT_SUFFIX. (#1271, #1301) - Fix a typo in the macOS
fat3architecture name (wasfat32). (#1199)
Fixes for pylock, direct URLs, and dependency groups
- Percent-decode
pylockartifact file names derived from aurlso that local versions (e.g. a wheel with2.12.1+cu130encoded as2.12.1%2Bcu130) yield a valid file name. (#1314) - Use an explicitly empty
tagssequence inPylock.select()instead of falling back tosys_tags(). (#1349) - Fix
Pylock.select()on Python builds that report a non-PEP 440python_full_version(e.g.3.15.0+). (#1179) - Reject TOML booleans where integers are expected in
pylockfiles. (#1244) - Add
PylockSelectErrortopackaging.pylock.__all__. (#1202) - Fix
DirectUrlcredential stripping for passwords containing@. (#1218) - Parse the URL scheme case-insensitively when checking for file URLs in
direct_url. (#1240) - Require absolute file URLs for local directories in
direct_url. (#1297) - Collect
InvalidRequirementerrors while resolving dependency groups instead of leaking them. (#1302) - Don't cache malformed dependency group parses. (#1248)
Performance
- Implement
SpecifierandSpecifierSetfiltering with the new range engine. (#1120, #1259) - Cache the default marker environment. (#1250)
- Cache the
_manylinuxmodule lookup process-wide. (#1254) - Add
__slots__toRequirementand the token classes. (#1320, #1258) - Keep range caches across canonicalization, precompile the wheel project-name pattern, simplify
parse_tag(), and skipplatform.mac_ver()when the version and arch are given. (#1253, #1256, #1236, #1255)
Documentation
- Describe the validation scope of
packaging.pylock. (#1339) - Explain which
packaging.metadatafields are validated. (#1342) - Document
RFC822Policyin the low-levelpackaging.metadatainterface. (#1222) - Enable nitpicky mode and render missing classes and fields in the API reference. (#1196, #1225, #1277)
- Add missing
versionadded/versionchangeddirectives and many small docstring fixes across the API reference. (#1205, #1207, #1208, #1209, #1211, #1216, #1217, #1223, #1272, #1273, #1274, #1291, #1300, #1303, #1317, #1344)
Internal
- Add Python 3.15 to the test matrix. (#1190)
- Add a musl/Alpine test job and make the test suite pass on musl. (#1226, #1227)
- Expand the downstream test matrix by ten projects. (#1261)
- Update to mypy 2. (#1191)
- Use nox's uv integration. (#1057)
New Contributors
- @Sean-Kenneth-Doherty made their first contribution in #1187
- @jsirois made their first contribution in #1199
- @Alan-K-Biju-7 made their first contribution in #1217
- @facutuesca made their first contribution in #1218
- @NovaLux12 made their first contribution in #1317
- @atsuoishimoto made their first contribution in #1314
- @gaoflow made their first contribution in #1291
- @jacobdr made their first contribution in #608
- @deepakganesh78 made their first contribution in #1351
- @rajath201 made their first contribution in #1356
Full Changelog: 26.2...26.3