pypi / packaging
packaging
What changed in packaging 26, from its official release notes, upgrade guides and advisories. Sources checked 2026-10-18.
23 breaking21 new APIs and options1 performance9 behavior and defaults21 fixes and other
See which of these apply to your code, ranked, in your agent:
npx @unstale/cli initpackaging 2675 changes
Breaking changes 23
Malformed Description-Content-Type values are now rejected.
Local directories in direct_url now require absolute file URLs.
Misplaced WITH clauses and empty LicenseRef- names now raise InvalidLicenseExpression.
Names containing collapsed double hyphens, such as a--b, are now rejected.
Linux tag generation now prefers native linux_* tags over manylinux and musllinux tags.
Serialization now retains parentheses around nested marker groups.
Tags with the wrong number of components now raise InvalidTag.
Empty tag components are now rejected.
TOML booleans are now rejected where integers are expected.
Python 3.8 is no longer supported; packaging requires Python 3.9 or later.
An invalid specifier inside a requirement now raises InvalidRequirement instead of InvalidSpecifier.
Requirements and markers ending in a line break are now rejected.
Set-valued variables such as extras now raise UndefinedComparison when used outside the membership form.
Marker evaluation raises this KeyError subclass when an environment key is missing.
Passing a non-string to Version now raises InvalidVersion instead of TypeError.
Non-string pre-release letters now raise InvalidVersion.
Wheel filenames ending in a newline are now rejected.
Wheel tags with a non-identifier interpreter component are now rejected.
The helper is renamed to _format_full_version to make it visibly private.
ValueError from int-max-str-digits is now propagated.
Local version identifiers are now restricted to ASCII.
Pylock now gives preference to a path over a URL.
The === operator uses the original string when available instead of its normalized form.
New APIs and options 21
Adds a limit argument for parsing compressed tag sets.
Generates pure-Python tags for a Python version without consulting the running platform.
Adds a predicate to prefer source distributions over wheels for selected packages.
Compares the versions accepted by two specifier sets to determine whether they are disjoint.
Compares the versions accepted by two specifier sets to determine whether one is a subset of the other.
Compares the versions accepted by two specifier sets to determine whether one is a superset of the other.
Converts a SpecifierSet to a VersionRange representing the versions it accepts.
Adds an interval-set API for version ranges, with set operations, relations, membership tests, filtering, and conversion to a SpecifierSet where a PEP 440 form exists.
Converts a VersionRange back to a SpecifierSet when a PEP 440 form exists.
Adds a function for selecting compatible tags.
Marker objects can now be combined with the & and | operators.
Adds a filename property to PackageSdist.
Adds a filename property to PackageWheel.
Adds a module for working with dependency groups, based on the dependency-groups package.
Adds the packaging.direct_url module.
Adds the packaging.errors module.
Adds an option to validate the sort order of compressed tag sets when parsing wheel filenames.
Adds a select function for pylock.
Adds a key argument for filtering values with a SpecifierSet.
Adds a method that determines whether a SpecifierSet is unsatisfiable using ranges.
Adds Version.from_parts; Version.__replace__ also normalizes its result.
Performance 1
Complex SpecifierSet filtering is faster through cost-based ordering.
Behavior and default changes 9
Values in nested markers are normalized at parse time.
Requested extra names are normalized before requirements are compared or hashed.
InvalidMetadata and CyclicDependencyGroup can now be pickled.
Metadata parsing now accepts Metadata-Version 2.6.
Specifier and SpecifierSet filtering now use the new range engine.
The warn argument is now forwarded to generic_tags.
Wheel tag handling now recognizes the free-threading abi3.abi3t tag.
Pylock validates that names and versions are consistent with file names.
Specifiers are now canonicalized when deduplicating them in a SpecifierSet.
Fixes and other changes 21
Malformed dependency-group parses are no longer cached.
InvalidRequirement errors are collected during dependency-group resolution instead of leaking.
Hashing internally trimmed versions no longer raises AttributeError.
Malformed string literal errors are wrapped in InvalidMarker or InvalidRequirement rather than leaking a low-level error.
Marker serialization preserves quoting semantics so serialized markers parse back to the same marker.
Empty tag components are now rejected.
An explicitly empty tags sequence is used as-is instead of falling back to sys_tags().
PylockSelectError is now included in packaging.pylock.__all__.
A requirement's specifier prereleases override is preserved across a pickle round trip.
Hashing now agrees with equality for trailing-zero-equivalent specifiers, allowing equal requirements to deduplicate in sets and dictionaries.
Corrects unsatisfiability results for post-release boundary intersections.
An empty project name is now rejected by both filename parsers.
Re-exports ExceptionGroup for now.
Version, Specifier, SpecifierSet, Tag, Marker, and Requirement are pickle-safe and backward-compatible with pickles created in 25.0–26.1, including references to the removed packaging._structures module.
Corrects the sysconfig variable name for pyemscripten.
Fixes incorrect self-comparison for InfinityType.
Fixes incorrect self-comparison for NegativeInfinityType.
Post-release exclusion for >V is narrowed to match the specification.
Pre-release exclusion for <V.postN is narrowed to match the specification.
Fixes handling of key when the specifier set is empty and prerelease is False.
Fixes > comparisons for versions with dev and local segments.