# packaging: what changed from 25 to 26 - Product: packaging (https://whatsnew.fyi/product/packaging) - Vendor: packaging - Range: changelog entries numbered after 25.0 up to and including 26.3, stable releases only - Entries below: 4 releases (newest first) - Resolved: 25 is 25.0 and 26 is 26.3, the newest stable release of each major we track - Carrying security changes: 0 · CVEs mentioned: 0 · Mentioning breaking changes: 0 · Removing or deprecating something: 1 - Page: https://whatsnew.fyi/product/packaging/compare/25...26 What's New is an index, not a publisher: every entry below links to the vendor's own release notes, which are the authoritative source. Entries are labelled where they are hand-curated sample data, pre-releases, or drawn from a secondary source such as a developer blog. Reuse: the summaries, labels and curation here are © What's New. Quote freely with attribution and a link back; wholesale republication of the corpus is not permitted — terms: https://whatsnew.fyi/terms. The vendors' own release notes remain their publishers'. ## What changed (69 changes, grouped by kind) ### Added #### 26.3 (2026-08-04) - Add a public VersionRange API and SpecifierSet.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 - Add VersionRange.to_specifier_set() to convert a range back to a SpecifierSet where a PEP 440 form exists - Accept Metadata-Version: 2.6 per PEP 808 - Add a limit argument to parse_tag() for compressed tag sets - Add a prefer_sdist_predicate argument to Pylock.select() to prefer source distributions over wheels for selected packages - Add pure_python_tags() to generate the pure-Python tags for a Python version without touching the running platform - Add SpecifierSet.is_subset(), SpecifierSet.is_superset(), and SpecifierSet.is_disjoint() to compare the versions two specifier sets accept #### 26.1 (2026-04-14) - Add handling for Emscripten wheel tags per PEP 783 - Add handling for the abi3.abi3t free-threading tag per PEP 803 - Add packaging.dependency_groups module based on PEP 723 - Add the packaging.direct_url module - Add the packaging.errors module - Add SpecifierSet.is_unsatisfiable using ranges - Add create_compatible_tags_selector to select compatible tags - Add a key argument to SpecifierSet.filter() - Support & and | operators for Marker objects - Add Version.from_parts and normalize Version.__replace__ - Add an option to validate compressed tag set sort order in parse_wheel_filename - Add pylock select function for PEP 751 - Add filename property to PackageSdist and PackageWheel #### 26.0 (2026-01-21) - Support PEP 751 for pylock - Support PEP 794 import name metadata - Support writing metadata - Support __replace__ for Version - Support positional pattern matching for Version and Specifier ### Changed #### 26.3 (2026-08-04) - Prefer native linux_* platform tags over manylinux and musllinux tags on Linux #### 26.1 (2026-04-14) - Narrow exclusion of pre-releases for V to match spec - Rename format_full_version to _format_full_version to make it visibly private - Restrict local version to ASCII - Give preference to path over url in pylock #### 26.0 (2026-01-21) - Adjust PEP 440 handling of prereleases for Specifier.contains, SpecifierSet.contains, and SpecifierSet.filter - Handle PEP 440 edge case in SpecifierSet.filter - Adjust arbitrary equality intersection preservation in SpecifierSet - Return False instead of raising for .contains with invalid version - Support arbitrary equality on arbitrary strings for Specifier and SpecifierSet's filter and contains methods - Only try to parse as Version on certain marker keys, return False on unequal ordered comparisons ### Fixed #### 26.3 (2026-08-04) - Raise InvalidVersion instead of TypeError when Version is given a non-string - Raise InvalidVersion for non-string pre-release letters passed to Version.from_parts - Fix an AttributeError when hashing internally trimmed versions - Fix SpecifierSet.is_unsatisfiable for post-release boundary intersections - Make Requirement.__hash__ consistent with __eq__ for trailing-zero-equivalent specifiers so equal requirements hash equal and deduplicate in sets and dicts - Normalize requested extra names before comparing or hashing requirements - Preserve a Requirement's specifier prereleases override across a pickle round trip - Raise InvalidRequirement instead of InvalidSpecifier when a requirement contains an invalid specifier - Clarify the error for post-release prefix wildcards like ==1.0.post1.* - Preserve quoting semantics when serializing marker values so round-tripped markers parse back to the same marker - Keep the parentheses of a nested group when serializing markers - Normalize extra and dependency_groups values in nested markers at parse time #### 26.2 (2026-04-24) - Fix incorrect sysconfig var name for pyemscripten - Make Version, Specifier, SpecifierSet, Tag, Marker, and Requirement pickle-safe and backward-compatible with pickles created in 25.0-26.1 including references to the removed packaging._structures module - Re-export ExceptionGroup for now #### 26.1 (2026-04-14) - Fix > comparison for versions with dev+local segments - Fix incorrect self-comparison for InfinityType and NegativeInfinityType - Canonicalize when deduplicating specifiers in SpecifierSet - Fix charset error message formatting - Handle the key parameter in SpecifierSet.filter when specifiers are empty and prerelease is False - Specifier === uses original string, not normalized, when available - Propagate int-max-str-digits ValueError #### 26.0 (2026-01-21) - Update _hash when unpickling Tag() - Correct comment and simplify implicit prerelease handling in Specifier.prereleases - Use explicit _GLibCVersion NamedTuple in _manylinux - Detect invalid license expressions containing () - Correct regex for metadata 'name' format - Improve the message around expecting a semicolon - Support nested parens in license expressions - Add space before at symbol in Requirements string - Use packaging logger instead of root logger ### Removed #### 26.3 (2026-08-04) - Drop support for Python 3.8; packaging now requires Python 3.9 or later ## Release notes ### 26.3 - Date: 2026-08-04 - Version: 26.3 - Original notes: https://github.com/pypa/packaging/releases/tag/26.3 - Permalink: https://whatsnew.fyi/product/packaging/releases/26.3 - **added** — Add a public VersionRange API and SpecifierSet.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 - **added** — Add VersionRange.to_specifier_set() to convert a range back to a SpecifierSet where a PEP 440 form exists - **added** — Accept Metadata-Version: 2.6 per PEP 808 - **added** — Add a limit argument to parse_tag() for compressed tag sets - **added** — Add a prefer_sdist_predicate argument to Pylock.select() to prefer source distributions over wheels for selected packages - **added** — Add pure_python_tags() to generate the pure-Python tags for a Python version without touching the running platform - **added** — Add SpecifierSet.is_subset(), SpecifierSet.is_superset(), and SpecifierSet.is_disjoint() to compare the versions two specifier sets accept - **removed** — Drop support for Python 3.8; packaging now requires Python 3.9 or later - **changed** — Prefer native linux_* platform tags over manylinux and musllinux tags on Linux - **fixed** — Raise InvalidVersion instead of TypeError when Version is given a non-string - **fixed** — Raise InvalidVersion for non-string pre-release letters passed to Version.from_parts - **fixed** — Fix an AttributeError when hashing internally trimmed versions - **fixed** — Fix SpecifierSet.is_unsatisfiable for post-release boundary intersections - **fixed** — Make Requirement.__hash__ consistent with __eq__ for trailing-zero-equivalent specifiers so equal requirements hash equal and deduplicate in sets and dicts - **fixed** — Normalize requested extra names before comparing or hashing requirements - **fixed** — Preserve a Requirement's specifier prereleases override across a pickle round trip - **fixed** — Raise InvalidRequirement instead of InvalidSpecifier when a requirement contains an invalid specifier - **fixed** — Clarify the error for post-release prefix wildcards like ==1.0.post1.* - **fixed** — Preserve quoting semantics when serializing marker values so round-tripped markers parse back to the same marker - **fixed** — Keep the parentheses of a nested group when serializing markers - **fixed** — Normalize extra and dependency_groups values in nested markers at parse time ##### What's Changed ###### Features * Add a public `VersionRange` API and `SpecifierSet.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 a `SpecifierSet` where a PEP 440 form exists. ([#1267](https://github.com/pypa/packaging/pull/1267), [#1270](https://github.com/pypa/packaging/pull/1270), [#1298](https://github.com/pypa/packaging/pull/1298)) * PEP 808: accept `Metadata-Version: 2.6`. ([#1194](https://github.com/pypa/packaging/pull/1194)) * Add a `limit` argument to `parse_tag()` for compressed tag sets. ([#1220](https://github.com/pypa/packaging/issues/1220)) * Add a `prefer_sdist_predicate` argument to `Pylock.select()` to prefer source distributions over wheels for selected packages. ([#1334](https://github.com/pypa/packaging/pull/1334)) * Add `pure_python_tags()` to generate the pure-Python tags for a Python version without touching the running platform. ([#1346](https://github.com/pypa/packaging/pull/1346)) * Add `SpecifierSet.is_subset()`, `SpecifierSet.is_superset()`, and `SpecifierSet.is_disjoint()`, which compare the versions two specifier sets accept. ([#1313](https://github.com/pypa/packaging/pull/1313)) ###### Behavior adaptations * Drop support for Python 3.8; packaging now requires Python 3.9 or later. ([#1157](https://github.com/pypa/packaging/pull/1157)) * Prefer native `linux_*` platform tags over `manylinux` and `musllinux` tags on Linux. ([#160](https://github.com/pypa/packaging/issues/160)) ###### Fixes for versions and specifiers * Raise `InvalidVersion` instead of `TypeError` when `Version` is given a non-string. ([#1319](https://github.com/pypa/packaging/pull/1319)) * Raise `InvalidVersion` for non-string pre-release letters passed to `Version.from_parts`. ([#1241](https://github.com/pypa/packaging/pull/1241)) * Fix an `AttributeError` when hashing internally trimmed versions. ([#1242](https://github.com/pypa/packaging/pull/1242)) * Fix `SpecifierSet.is_unsatisfiable` for post-release boundary intersections. ([#1257](https://github.com/pypa/packaging/pull/1257)) ###### Fixes for requirements and markers * Make `Requirement.__hash__` consistent with `__eq__` for trailing-zero-equivalent specifiers (e.g. `foo==1.0.0` and `foo==1.0.0.0`), so equal requirements hash equal and deduplicate in sets and dicts. ([#1232](https://github.com/pypa/packaging/pull/1232)) * Normalize requested extra names before comparing or hashing requirements. ([#644](https://github.com/pypa/packaging/issues/644)) * Preserve a `Requirement`'s specifier `prereleases` override across a pickle round trip. ([#1204](https://github.com/pypa/packaging/issues/1204)) * Raise `InvalidRequirement` instead of `InvalidSpecifier` when a requirement contains an invalid specifier. ([#1332](https://github.com/pypa/packaging/pull/1332)) * Clarify the error for post-release prefix wildcards like `==1.0.post1.*`. ([#1299](https://github.com/pypa/packaging/pull/1299)) * Preserve quoting semantics when serializing marker values, so round-tripped markers parse back to the same marker. ([#1213](https://github.com/pypa/packaging/pull/1213)) * Keep the parentheses of a nested group when serializing markers. ([#1316](https://github.com/pypa/packaging/pull/1316)) * Normalize `extra` and `dependency_groups` values in nested markers at parse time. ([#1246](https://github.com/pypa/packaging/pull/1246), [#1310](https://github.com/pypa/packaging/pull/1310)) * Raise `UndefinedComparison` when a set-valued variable like `extras` is used outside the membership form. ([#1265](https://github.com/pypa/packaging/pull/1265)) * Raise `UndefinedEnvironmentName` (a `KeyError` subclass) for missing environment keys during marker evaluation. ([#1276](http _[Truncated at 4000 characters — full notes: https://github.com/pypa/packaging/releases/tag/26.3]_ ### 26.2 - Date: 2026-04-24 - Version: 26.2 - Original notes: https://github.com/pypa/packaging/releases/tag/26.2 - Permalink: https://whatsnew.fyi/product/packaging/releases/26.2 - **fixed** — Fix incorrect sysconfig var name for pyemscripten - **fixed** — Make Version, Specifier, SpecifierSet, Tag, Marker, and Requirement pickle-safe and backward-compatible with pickles created in 25.0-26.1 including references to the removed packaging._structures module - **fixed** — Re-export ExceptionGroup for now ##### What's Changed Fixes: * Fix incorrect sysconfig var name for pyemscripten by @ryanking13 in https://github.com/pypa/packaging/pull/1160 * Make `Version`, `Specifier`, `SpecifierSet`, `Tag`, `Marker`, and `Requirement` pickle-safe and backward-compatible with pickles created in 25.0-26.1 (including references to the removed ``packaging._structures`` module) by @eachimei and @henryiii in https://github.com/pypa/packaging/pull/1163, https://github.com/pypa/packaging/pull/1168, https://github.com/pypa/packaging/pull/1170, and https://github.com/pypa/packaging/pull/1171 * fix: re-export ExceptionGroup for now by @henryiii in https://github.com/pypa/packaging/pull/1164 Documentation: * docs: add errors section and fix missing details by @henryiii in https://github.com/pypa/packaging/pull/1159 * docs(dev): document property-based test suite by @r266-tech in https://github.com/pypa/packaging/pull/1167 * Fix typo in DirectUrl documentation by @sbidoul in https://github.com/pypa/packaging/pull/1169 * docs(specifiers): add is_unsatisfiable() usage example by @r266-tech in https://github.com/pypa/packaging/pull/1166 Internal: * Enable the auditor persona on zizmor by @henryiii in https://github.com/pypa/packaging/pull/1158 * Test new pickle guarantees by @henryiii in https://github.com/pypa/packaging/pull/1174 * Use native uv integration in rtd by @henryiii in https://github.com/pypa/packaging/pull/1175 ##### New Contributors * @ryanking13 made their first contribution in https://github.com/pypa/packaging/pull/1160 * @eachimei made their first contribution in https://github.com/pypa/packaging/pull/1163 **Full Changelog**: https://github.com/pypa/packaging/compare/26.1...26.2 ### 26.1 - Date: 2026-04-14 - Version: 26.1 - Original notes: https://github.com/pypa/packaging/releases/tag/26.1 - Permalink: https://whatsnew.fyi/product/packaging/releases/26.1 - **added** — Add handling for Emscripten wheel tags per PEP 783 - **added** — Add handling for the abi3.abi3t free-threading tag per PEP 803 - **added** — Add packaging.dependency_groups module based on PEP 723 - **added** — Add the packaging.direct_url module - **added** — Add the packaging.errors module - **added** — Add SpecifierSet.is_unsatisfiable using ranges - **added** — Add create_compatible_tags_selector to select compatible tags - **added** — Add a key argument to SpecifierSet.filter() - **added** — Support & and | operators for Marker objects - **added** — Add Version.from_parts and normalize Version.__replace__ - **added** — Add an option to validate compressed tag set sort order in parse_wheel_filename - **added** — Add pylock select function for PEP 751 - **added** — Add filename property to PackageSdist and PackageWheel - **changed** — Narrow exclusion of pre-releases for V to match spec - **changed** — Rename format_full_version to _format_full_version to make it visibly private - **changed** — Restrict local version to ASCII - **changed** — Give preference to path over url in pylock - **fixed** — Fix > comparison for versions with dev+local segments - **fixed** — Fix incorrect self-comparison for InfinityType and NegativeInfinityType - **fixed** — Canonicalize when deduplicating specifiers in SpecifierSet - **fixed** — Fix charset error message formatting - **fixed** — Handle the key parameter in SpecifierSet.filter when specifiers are empty and prerelease is False - **fixed** — Specifier === uses original string, not normalized, when available - **fixed** — Propagate int-max-str-digits ValueError Features: * ~~PEP 783: add handling for Emscripten wheel tags by @hoodmane in https://github.com/pypa/packaging/pull/804~~ (old name used in implementation, will be fixed in next release) * PEP 803: add handling for the `abi3.abi3t` free-threading tag by @ngoldbaum in https://github.com/pypa/packaging/pull/1099 * PEP 723: add `packaging.dependency_groups` module, based on the `dependency-groups` package by @sirosen in https://github.com/pypa/packaging/pull/1065 * Add the `packaging.direct_url` module by @sbidoul in https://github.com/pypa/packaging/pull/944 * Add the `packaging.errors` module by @henryiii in https://github.com/pypa/packaging/pull/1071 * Add `SpecifierSet.is_unsatisfiable` using ranges (new internals that will be expanded in future versions) by @notatallshaw in https://github.com/pypa/packaging/pull/1119 * Add `create_compatible_tags_selector` to select compatible tags by @sbidoul in https://github.com/pypa/packaging/pull/1110 * Add a `key` argument to `SpecifierSet.filter()` by @frostming in https://github.com/pypa/packaging/pull/1068 * Support `&` and `|` for `Marker`'s by @henryiii in https://github.com/pypa/packaging/pull/1146 * Normalize `Version.__replace__` and add `Version.from_parts` by @henryiii in https://github.com/pypa/packaging/pull/1078 * Add an option to validate compressed tag set sort order in `parse_wheel_filename` by @r266-tech in https://github.com/pypa/packaging/pull/1150 Behavior adaptations: * Narrow exclusion of pre-releases for `V` to match spec by @notatallshaw in https://github.com/pypa/packaging/pull/1141 * Rename `format_full_version` to `_format_full_version` to make it visibly private by @r266-tech in https://github.com/pypa/packaging/pull/1125 * Restrict local version to ASCII by @henryiii in https://github.com/pypa/packaging/pull/1102 Pylock (PEP 751) updates: * Add pylock `select` function by @sbidoul in https://github.com/pypa/packaging/pull/1092 * Document pylock `select()` method and `PylockSelectError` by @r266-tech in https://github.com/pypa/packaging/pull/1153 * Add `filename` property to `PackageSdist` and `PackageWheel`, more validation by @sbidoul in https://github.com/pypa/packaging/pull/1095 * Give preference to path over url by @sbidoul in https://github.com/pypa/packaging/pull/1128 * Validate name/version consistency in file names by @sbidoul in https://github.com/pypa/packaging/pull/1114 Fixes: * Fix `>` comparison for versions with dev+local segments by @veeceey in https://github.com/pypa/packaging/pull/1097 * Fix incorrect self-comparison for `InfinityType` and `NegativeInfinityType` by @bysiber in https://github.com/pypa/packaging/pull/1093 * Canonicalize when deduplicating specifiers in `SpecifierSet` by @notatallshaw in https://github.com/pypa/packaging/pull/1109 * Fix charset error message formatting by @notatallshaw in https://github.com/pypa/packaging/pull/1121 * Handle the `key` parameter in `SpecifierSet.filter` when specifiers are empty and prerelease is `False` by @notatallshaw in https://github.com/pypa/packaging/pull/1096 * Standardize inner components of `repr` output by @henryiii in https://github.com/pypa/packaging/pull/1090 * `Specifier`'s `===` uses original string, not normalized, when available by @notatallshaw in https://github.com/pypa/packaging/pull/1124 * Propagate int-max-str-digits `ValueError` by @notatallshaw in https://github.com/pypa/packaging/pull/1155 Performance: * Add fast path for parsing simple versions (digits and dots only) by @notatallshaw in https://github.com/pypa/packaging/pull/1082 * Add fast path for `Version` to `Version` comparison by skipping `_key` property by @notatallshaw in https://github.com/pypa/packaging/pull/1083 * Cache `Version` hash value in dedicated slot by @notatallshaw in https://github.com/pypa/packaging/pull/1118 * Ove _[Truncated at 4000 characters — full notes: https://github.com/pypa/packaging/releases/tag/26.1]_ ### 26.0 - Date: 2026-01-21 - Version: 26.0 - Original notes: https://github.com/pypa/packaging/releases/tag/26.0 - Permalink: https://whatsnew.fyi/product/packaging/releases/26.0 - **added** — Support PEP 751 for pylock - **added** — Support PEP 794 import name metadata - **added** — Support writing metadata - **added** — Support __replace__ for Version - **added** — Support positional pattern matching for Version and Specifier - **changed** — Adjust PEP 440 handling of prereleases for Specifier.contains, SpecifierSet.contains, and SpecifierSet.filter - **changed** — Handle PEP 440 edge case in SpecifierSet.filter - **changed** — Adjust arbitrary equality intersection preservation in SpecifierSet - **changed** — Return False instead of raising for .contains with invalid version - **changed** — Support arbitrary equality on arbitrary strings for Specifier and SpecifierSet's filter and contains methods - **changed** — Only try to parse as Version on certain marker keys, return False on unequal ordered comparisons - **fixed** — Update _hash when unpickling Tag() - **fixed** — Correct comment and simplify implicit prerelease handling in Specifier.prereleases - **fixed** — Use explicit _GLibCVersion NamedTuple in _manylinux - **fixed** — Detect invalid license expressions containing () - **fixed** — Correct regex for metadata 'name' format - **fixed** — Improve the message around expecting a semicolon - **fixed** — Support nested parens in license expressions - **fixed** — Add space before at symbol in Requirements string - **fixed** — Use packaging logger instead of root logger Read about the performance improvements here: . ##### What's Changed Features: * PEP 751: support pylock by @sbidoul in https://github.com/pypa/packaging/pull/900 * PEP 794: import name metadata by @brettcannon in https://github.com/pypa/packaging/pull/948 * Support writing metadata by @henryiii in https://github.com/pypa/packaging/pull/846 * Support `__replace__` for `Version` by @henryiii in https://github.com/pypa/packaging/pull/1003 * Support positional pattern matching for `Version` and `Specifier` by @henryiii in https://github.com/pypa/packaging/pull/1004 Behavior adaptations: * PEP 440 handling of prereleases for `Specifier.contains`, `SpecifierSet.contains`, and `SpecifierSet.filter` by @notatallshaw in https://github.com/pypa/packaging/pull/897 * Handle PEP 440 edge case in `SpecifierSet.filter` by @notatallshaw in https://github.com/pypa/packaging/pull/942 * Adjust arbitrary equality intersection preservation in `SpecifierSet` by @notatallshaw in https://github.com/pypa/packaging/pull/951 * Return `False` instead of raising for `.contains` with invalid version by @Liam-DeVoe in https://github.com/pypa/packaging/pull/932 * Support arbitrary equality on arbitrary strings for `Specifier` and `SpecifierSet`'s `filter` and `contains` method. by @notatallshaw in https://github.com/pypa/packaging/pull/954 * Only try to parse as `Version` on certain marker keys, return `False` on unequal ordered comparsions by @JP-Ellis in https://github.com/pypa/packaging/pull/939 Fixes: * Update `_hash` when unpickling `Tag()` by @dholth in https://github.com/pypa/packaging/pull/860 * Correct comment and simplify implicit prerelease handling in `Specifier.prereleases` by @notatallshaw in https://github.com/pypa/packaging/pull/896 * Use explicit `_GLibCVersion` `NamedTuple` in `_manylinux` by @cthoyt in https://github.com/pypa/packaging/pull/868 * Detect invalid license expressions containing `()` by @bwoodsend in https://github.com/pypa/packaging/pull/879 * Correct regex for metadata `'name'` format by @di in https://github.com/pypa/packaging/pull/925 * Improve the message around expecting a semicolon by @pradyunsg in https://github.com/pypa/packaging/pull/833 * Support nested parens in license expressions by @Liam-DeVoe in https://github.com/pypa/packaging/pull/931 * Add space before at symbol in `Requirements` string by @henryiii in https://github.com/pypa/packaging/pull/953 * A root logger use found by ruff LOG, use `packaging` logger instead by @henryiii in https://github.com/pypa/packaging/pull/965 * Better support for subclassing `Marker` and `Requirement` by @henryiii in https://github.com/pypa/packaging/pull/1022 * Normalize all extras, not just if it comes first by @henryiii in https://github.com/pypa/packaging/pull/1024 * Don't produce a broken repr if `Marker` fails to construct by @henryiii in https://github.com/pypa/packaging/pull/1033 Performance: * Avoid recompiling regexes in the tokenizer for a 3x speedup by @hauntsaninja in https://github.com/pypa/packaging/pull/1019 * Improve performance in `_manylinux.py` by @cthoyt in https://github.com/pypa/packaging/pull/869 * Minor cleanups to `Version` by @bearomorphism in https://github.com/pypa/packaging/pull/913 * Skip redundant creation of `Version`s in specifier comparison by @notatallshaw in https://github.com/pypa/packaging/pull/986 * Cache `Specifier`'s Version by @notatallshaw in https://github.com/pypa/packaging/pull/985 * Make `Version` a little faster by @henryiii in https://github.com/pypa/packaging/pull/987 * Minor `Version` regex cleanup by @henryiii in https://github.com/pypa/packaging/pull/990 * Faster regex on Python 3.11.5+ by @henryiii in https://github.com/pypa/packaging/pull/988 and https://github.com/pypa/packaging/pull/1055 * Lazily calculate `_key` in `Version` by @notatallshaw in https://github.com/pypa/packaging/pull/989 and regression for `packaging_legac _[Truncated at 4000 characters — full notes: https://github.com/pypa/packaging/releases/tag/26.0]_