NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #2 most downloaded on PyPI
Core utilities for Python packages
Last release 2 months ago
04 Aug 2026
Ships fairly regularly
a new release about every 3 months
Nearly every release is documented
notes for 50 of 50 stable releases
1 version withdrawn
withdrawn after publishing
12 years old
54 releases · first in 2014
Add a public VersionRange API and SpecifierSet.to_range() , representing the versions a specifier set accepts as an interval set that supports interse
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, #1270, #1298)Metadata-Version: 2.6. (#1194)limit argument to parse_tag() for compressed tag sets. (#1220)prefer_sdist_predicate argument to Pylock.select() to prefer source distributions over wheels for selected packages. (#1334)pure_python_tags() to generate the pure-Python tags for a Python version without touching the running platform. (#1346)SpecifierSet.is_subset(), SpecifierSet.is_superset(), and SpecifierSet.is_disjoint(), which compare the versions two specifier sets accept. (#1313)linux_* platform tags over manylinux and musllinux tags on Linux. (#160)InvalidVersion instead of TypeError when Version is given a non-string. (#1319)InvalidVersion for non-string pre-release letters passed to Version.from_parts. (#1241)AttributeError when hashing internally trimmed versions. (#1242)SpecifierSet.is_unsatisfiable for post-release boundary intersections. (#1257)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)Requirement's specifier prereleases override across a pickle round trip. (#1204)InvalidRequirement instead of InvalidSpecifier when a requirement contains an invalid specifier. (#1332)==1.0.post1.*. (#1299)extra and dependency_groups values in nested markers at parse time. (#1246, #1310)UndefinedComparison when a set-valued variable like extras is used outside the membership form. (#1265)UndefinedEnvironmentName (a KeyError subclass) for missing environment keys during marker evaluation. (#1276)InvalidMarker / InvalidRequirement instead of leaking a low-level error. (#1249)from_email validation errors into one ExceptionGroup instead of raising the first. (#1268)Description-Content-Type values. (#1329){field} placeholders in error messages. (#1327)unparsed instead of asserting. (#1247)InvalidMetadata and CyclicDependencyGroup picklable. (#1328)str.splitlines recognizes when writing a header with RFC822Message. (#1356)InvalidLicenseExpression for misplaced WITH clauses and empty LicenseRef- names. (#1266)InvalidLicenseExpression instead of KeyError for a LicenseRef- with a + suffix. (#1219)InvalidTag from parse_tag() for tags with the wrong number of components. (#1238)parse_wheel_filename() and parse_tag(). (#1234)is_normalized_name now rejects names with collapsed double hyphens like a--b. (#1230)abi3t tags. (#1245)warn argument to generic_tags() in sys_tags(). (#1264)SystemError for an empty or malformed CPython EXT_SUFFIX. (#1271, #1301)fat3 architecture name (was fat32). (#1199)pylock artifact file names derived from a url so that local versions (e.g. a wheel with 2.12.1+cu130 encoded as 2.12.1%2Bcu130) yield a valid file name. (#1314)tags sequence in Pylock.select() instead of falling back to sys_tags(). (#1349)Pylock.select() on Python builds that report a non-PEP 440 python_full_version (e.g. 3.15.0+). (#1179)pylock files. (#1244)PylockSelectError to packaging.pylock.__all__. (#1202)DirectUrl credential stripping for passwords containing @. (#1218)direct_url. (#1240)direct_url. (#1297)InvalidRequirement errors while resolving dependency groups instead of leaking them. (#1302)Specifier and SpecifierSet filtering with the new range engine. (#1120, #1259)_manylinux module lookup process-wide. (#1254)__slots__ to Requirement and the token classes. (#1320, #1258)parse_tag(), and skip platform.mac_ver() when the version and arch are given. (#1253, #1256, #1236, #1255)packaging.pylock. (#1339)packaging.metadata fields are validated. (#1342)RFC822Policy in the low-level packaging.metadata interface. (#1222)versionadded / versionchanged directives 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)Full Changelog: 26.2...26.3
One column per quarter.
Fix incorrect sysconfig var name for pyemscripten by @ryanking13 in #1160
Fixes:
Version, Specifier, SpecifierSet, Tag, Marker, and Requirement pickle-safepackaging._structures module) by @eachimei and @henryiii in #1163, #1168, #1170, and #1171Documentation:
Internal:
Full Changelog: 26.1...26.2
Fixes:
Fix incorrect sysconfig var name for pyemscripten in (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) (1163, 1168, 1170, 1171)
Re-export ExceptionGroup in metadata for now in (1164)
Documentation:
Add errors section and fix missing details in (1159)
Document our property-based test suite in (1167)
Fix a DirectUrl typo in (1169)
Add example of is_unsatisfiable in (1166)
Internal:
Enable the auditor persona on zizmor in (1158)
Test new pickle guarantees in (1174)
Use new native ReadTheDocs uv integration in (1175)
PEP 783: add handling for Emscripten wheel tags by @hoodmane in #804 (old name used in implementation, will be fixed in next release)
Features:
abi3.abi3t free-threading tag by @ngoldbaum in #1099packaging.dependency_groups module, based on the dependency-groups package by @sirosen in #1065packaging.direct_url module by @sbidoul in #944packaging.errors module by @henryiii in #1071SpecifierSet.is_unsatisfiable using ranges (new internals that will be expanded in future versions) by @notatallshaw in #1119create_compatible_tags_selector to select compatible tags by @sbidoul in #1110key argument to SpecifierSet.filter() by @frostming in #1068& and | for Marker's by @henryiii in #1146Version.__replace__ and add Version.from_parts by @henryiii in #1078parse_wheel_filename by @r266-tech in #1150Behavior adaptations:
<V.postN to match spec by @notatallshaw in #1140>V to match spec by @notatallshaw in #1141format_full_version to _format_full_version to make it visibly private by @r266-tech in #1125Pylock (PEP 751) updates:
select function by @sbidoul in #1092select() method and PylockSelectError by @r266-tech in #1153filename property to PackageSdist and PackageWheel, more validation by @sbidoul in #1095Fixes:
> comparison for versions with dev+local segments by @veeceey in #1097InfinityType and NegativeInfinityType by @bysiber in #1093SpecifierSet by @notatallshaw in #1109key parameter in SpecifierSet.filter when specifiers are empty and prerelease is False by @notatallshaw in #1096repr output by @henryiii in #1090Specifier's === uses original string, not normalized, when available by @notatallshaw in #1124ValueError by @notatallshaw in #1155Performance:
Version to Version comparison by skipping _key property by @notatallshaw in #1083Version hash value in dedicated slot by @notatallshaw in #1118_cmpkey to remove use of custom objects by @notatallshaw in #1116__replace__ in Specifier comparison if not needed by @notatallshaw in #1081SpecifierSet use tuple instead of frozenset for _specs by @notatallshaw in #1108SpecifierSet filtering by implementing cost-based ordering by @notatallshaw in #1105SpecifierSet.filter by @notatallshaw in #1076__slots__ to Marker by @henryiii in #1147Specifier regex by @sirosen in #1106Internal:
collections.namedtuple in tests by @henryiii in #1070dir() / tab-completion in REPL by @henryiii in #1011__all__/__dir__ by @henryiii in #1069SpecifierSet.prereleases by @notatallshaw in #1073_compare_compatible by @notatallshaw in #1100Specifier.prereleases by @notatallshaw in #1074Specifier.prereleases by @notatallshaw in #1072Documentation:
Version.from_parts() by @Jackenmen in #1134& and | operators for combining Marker objects by @r266-tech in #1151Version documentation by @henryiii in #1089Performance improvements since 26.0, from the new integrated benchmark suite:
uv run asv run 26.0..HEAD
Full Changelog: 26.0...26.1
Features:
PEP 783: add handling for Emscripten wheel tags in (804) (old name used in implementation, fixed in next release)
PEP 803: add handling for the abi3.abi3t free-threading tag in (1099)
PEP 735: add packaging.dependency_groups module, based on the dependency-groups package in (1065)
Add the packaging.direct_url module in (944)
Add the packaging.errors module in (1071)
Add SpecifierSet.is_unsatisfiable using ranges (new internals that will be expanded in future versions) in (1119)
Add create_compatible_tags_selector to select compatible tags in (1110)
Add a key argument to SpecifierSet.filter() in (1068)
Support & and | for Marker's in (1146)
Normalize Version.__replace__ and add Version.from_parts in (1078)
Add an option to validate compressed tag set sort order in parse_wheel_filename in (1150)
Behavior adaptations:
Narrow exclusion of pre-releases for <V.postN to match spec in (1140)
Narrow exclusion of post-releases for >V to match spec in (1141)
Rename format_full_version to _format_full_version to make it visibly private in (1125)
Restrict local version to ASCII in (1102)
Pylock (PEP 751) updates:
Add pylock select function in (1092)
Document pylock select() method and PylockSelectError in (1153)
Add filename property to PackageSdist and PackageWheel, more validation in (1095)
Give preference to path over url in (1128)
Validate name/version consistency in file names in (1114)
Fixes:
Fix > comparison for versions with dev+local segments in (1097)
Fix incorrect self-comparison for InfinityType and NegativeInfinityType in (1093)
Canonicalize when deduplicating specifiers in SpecifierSet in (1109)
Fix charset error message formatting in (1121)
Handle the key parameter in SpecifierSet.filter when specifiers are empty and prerelease is False in (1096)
Standardize inner components of repr output in (1090)
Specifier's === uses original string, not normalized, when available in (1124)
Propagate int-max-str-digits ValueError in (1155)
Performance:
Add fast path for parsing simple versions (digits and dots only) in (1082)
Add fast path for Version to Version comparison by skipping _key property in (1083)
Cache Version hash value in dedicated slot in (1118)
Overhaul _cmpkey to remove use of custom objects in (1116)
Skip __replace__ in Specifier comparison if not needed in (1081)
SpecifierSet use tuple instead of frozenset for _specs in (1108)
Speed up complex SpecifierSet filtering by implementing cost-based ordering in (1105)
Speed up wildcard comparison in (1111)
Stream PEP440 filtering in SpecifierSet.filter in (1076)
Add __slots__ to Marker in (1147)
Simply Specifier regex in (1106)
Skip using the hash property internally in (1115)
Internal:
Add downstream testing in (1049)
Benchmarking suite in (1059)
Benchmark variance reduction in (1107)
Add property-based tests for PEP 440 in (1144)
Clean up a few collections.namedtuple in tests in (1070)
Do not reload the tags module in tests in (1152)
Limit dir() / tab-completion in REPL in (1011)
Add more __all__`/`__dir__ in (1069)
Cleanup pre-commit a bit in (1080)
Pin pre-commit file, use dependabot in (1133)
Remove sphinx-toolbox in (1135)
Use docs group, faster readthedocs in (1061)
Add free-threaded Python to CI in (1098)
Simplify combining SpecifierSet.prereleases in (1073)
Use direct operator methods in _compare_compatible in (1100)
Remove unnecessary if statement in Specifier.prereleases in (1074)
Remove unneeded nesting in Specifier.prereleases in (1072)
Documentation:
Fix documentation: grammar, typos, formatting, and outdated references in (1084)
Fix versionadded for Version.from_parts() in (1134)
Document & and | operators for combining Marker objects in (1151)
Expand and clean up Version documentation in (1089)
Move license docs inline in (1131)
Move markers inline in (1104)
Move tags docs to inline in (1130)
Move utilities inline, like other modules in (1103)
Deprecate ._version ( _Version , a NamedTuple ) by @henryiii in #995 and #1062
Read about the performance improvements here: https://iscinumpy.dev/post/packaging-faster.
Features:
__replace__ for Version by @henryiii in #1003Version and Specifier by @henryiii in #1004Behavior adaptations:
Specifier.contains, SpecifierSet.contains, and SpecifierSet.filter by @notatallshaw in #897SpecifierSet.filter by @notatallshaw in #942SpecifierSet by @notatallshaw in #951False instead of raising for .contains with invalid version by @Liam-DeVoe in #932Specifier and SpecifierSet's filter and contains method. by @notatallshaw in #954Version on certain marker keys, return False on unequal ordered comparsions by @JP-Ellis in #939Fixes:
_hash when unpickling Tag() by @dholth in #860Specifier.prereleases by @notatallshaw in #896_GLibCVersion NamedTuple in _manylinux by @cthoyt in #868() by @bwoodsend in #879'name' format by @di in #925Requirements string by @henryiii in #953packaging logger instead by @henryiii in #965Marker and Requirement by @henryiii in #1022Marker fails to construct by @henryiii in #1033Performance:
_manylinux.py by @cthoyt in #869Version by @bearomorphism in #913Versions in specifier comparison by @notatallshaw in #986Specifier's Version by @notatallshaw in #985Version a little faster by @henryiii in #987Version regex cleanup by @henryiii in #990_key in Version by @notatallshaw in #989 and regression for packaging_legacy fixed by @henryiii in #1048canonicalize_version by @henryiii in #993fullmatch in a couple more places by @henryiii in #992fullmatch for markers too by @henryiii in #1029map instead of generator by @henryiii in #996._version (_Version, a NamedTuple) by @henryiii in #995 and #1062Version creation in canonicalize_version by @henryiii in #994__slots__ to Version by @henryiii in #1001__slots__ to Specifiers by @henryiii in #1002__slots__ to Nodes by @henryiii in #1032version.__replace__ in specifier comparison by @notatallshaw in #999_get_spec_version in more places in Specifier by @notatallshaw in #1005set construction out of function by @henryiii in #1012Version.__str__ by about 10% by @henryiii in #997canonicalize_name by avoiding a regex by @henryiii in #1030, #1047, and #1064Type annotations:
parse_wheel_filename docs by @ncoghlan in #973_version in .version.Version by @brettcannon in #927specifier.py by @subhajitsaha01 in #930Internal:
license-files by @DimitriPapadopoulos in #924python_version by @trim21 in #908ruff was renamed ruff-check in pre-commit by @henryiii in #933log_level is better than log_cli_level by @henryiii in #956partition in _parse_project_urls by @henryiii in #1013packaging.licenses by @notatallshaw in #1039contextlib.contextmanager by @SpecLad in #1046Note truncated.
Features:
PEP 751: support pylock (900)
PEP 794: import name metadata (948)
Support for writing metadata to a file (846)
Support __replace__ on Version (1003)
Support positional pattern matching for Version and SpecifierSet (1004)
Behavior adaptations:
PEP 440 handling of prereleases for Specifier.contains, SpecifierSet.contains, and SpecifierSet.filter (897)
Handle PEP 440 edge case in SpecifierSet.filter (942)
Adjust arbitrary equality intersection preservation in SpecifierSet (951)
Return False instead of raising for .contains with invalid version (932)
Support arbitrary equality on arbitrary strings for Specifier and SpecifierSet's filter and contains method. (954)
Only try to parse as Version on certain marker keys, return False on unequal ordered comparisons (939)
Fixes:
Update _hash when unpickling Tag() (860)
Correct comment and simplify implicit prerelease handling in Specifier.prereleases (896)
Use explicit _GLibCVersion NamedTuple in _manylinux (868)
Detect invalid license expressions containing () (879)
Correct regex for metadata 'name' format (925)
Improve the message around expecting a semicolon (833)
Support nested parens in license expressions (931)
Add space before at symbol in Requirements string (953)
A root logger use found, use a packaging logger instead (965)
Better support for subclassing Marker and Requirement (1022)
Normalize all extras, not just if it comes first (1024)
Don't produce a broken repr if Marker fails to construct (1033)
Performance:
Avoid recompiling regexes in the tokenizer for a 3x speedup (1019)
Improve performance in _manylinux.py (869)
Minor cleanups to Version (913)
Skip redundant creation of Version's in specifier comparison (986)
Cache the Specifier's Version (985)
Make Version a little faster (987)
Minor Version regex cleanup (990)
Faster regex on Python 3.11.5+ for Version (988, 1055)
Lazily calculate _key in Version (989, 1048)
Faster canonicalize_version (993)
Use re.fullmatch in a couple more places (992, 1029)
Use map instead of generator (996)
Deprecate ._version (_Version, a NamedTuple) (995, 1062)
Avoid duplicate Version creation in canonicalize_version (994)
Add __slots__ to core classes (1001, 1002, 1032)
Use Version.__replace__ in specifier comparison (999)
Use _get_spec_version in more places in Specifier (1005)
Pull set construction out of function (1012)
Letter normalization dict for prereleases and the like (1014)
Use str.partition in _parse_project_urls (1013)
Avoid normalizing extras again when comparing (1028)
Speed up Version.__str__ by about 10% (997)
Much faster canonicalize_name by avoiding a regex (1030, 1047, 1064)
Faster zero stripping (1058)
Type annotations:
Fix a type annotation (907)
Fix type hint of function used with contextlib.contextmanager (1046)
Fix tags return type in parse_wheel_filename docs (973)
Add type hint for _version in .version.Version (927)
Changed static type annotations in prereleases setter method in specifier.py (930)
Statically type our test suite (982)
Internal:
Test and declare support for Python 3.14 (901)
Change our license metadata to use an SPDX license expression (881, 924)
Expand the Ruff checks run on our codebase (835, 957, 959, 963, 956, 961, 964, 958, 960, 968, 967, 966, 969, 980, 979, 962, 984, 972)
Add spell checking (904, 910, 1015)
Improve links back to source in the documentation (991)
Add case insensitivity tests for arbitrary equality (975)
Fix incorrectly implicitly concatenated string in specifiers test (946)
Simpler else instead of assert in a check (1027, 1031)
Synchronize documentation and code for markers (1008)
Use the GitHub Actions slim runner for the all pass check (1021)
Use actionlint to check CI workflows (1052)
Use Trusted Publishing (893, 1043, 1045, 1051)
Use zizmor to check CI (1035)
Test on first public release of CPython 3.11 and newer (1056)
Since the final release candidate: Faster canonicalize_name, especially on Python 3.12 and 3.13, where performance regressed previously (1064), and reintroduce (deprecated) support for Version._.version (1062).
Support CPython 3.11.0-3.11.4 and older PyPy3.11 by @henryiii in #1055
Fixes:
Performance:
CI:
actionlint to check CI workflows by @miketheman in #1052Documentation:
Full Changelog: 26.0rc2...26.0rc3
Third release candidate for 26.0. Avoid bug in CPython 3.11.0-3.11.4 and older PyPy 3.11 (1055).
Fix regression in (private) Version._key for packaging_legacy by @henryiii in #1048
Fixes:
Performance:
Internal:
contextlib.contextmanager by @SpecLad in #1046Full Changelog: 26.0rc1...26.0rc2
Second release candidate for 26.0. Fixed a regression in (private) Version._key for packaging_legacy (1048), and speed up stripping zeros a little more (1058).
PEP 751: support pylock by @sbidoul in #900
Features:
__replace__ for Version by @henryiii in #1003Version and Specifier by @henryiii in #1004Behavior adaptations:
Specifier.contains, SpecifierSet.contains, and SpecifierSet.filter by @notatallshaw in #897SpecifierSet.filter by @notatallshaw in #942SpecifierSet by @notatallshaw in #951False instead of raising for .contains with invalid version by @Liam-DeVoe in #932Specifier and SpecifierSet's filter and contains method. by @notatallshaw in #954Version on certain marker keys, return False on unequal ordered comparsions by @JP-Ellis in #939Fixes:
_hash when unpickling Tag() by @dholth in #860Specifier.prereleases by @notatallshaw in #896_GLibCVersion NamedTuple in _manylinux by @cthoyt in #868() by @bwoodsend in #879'name' format by @di in #925Requirements string by @henryiii in #953packaging logger instead by @henryiii in #965Marker and Requirement by @henryiii in #1022Marker fails to construct by @henryiii in #1033Performance:
_manylinux.py by @cthoyt in #869Version by @bearomorphism in #913Versions in specifier comparison by @notatallshaw in #986Specifier's Version by @notatallshaw in #985Version a little faster by @henryiii in #987Version regex cleanup by @henryiii in #990_key in Version by @notatallshaw in #989canonicalize_version by @henryiii in #993fullmatch in a couple more places by @henryiii in #992fullmatch for markers too by @henryiii in #1029map instead of generator by @henryiii in #996._version (_Version, a NamedTuple) by @henryiii in #995Version creation in canonicalize_version by @henryiii in #994__slots__ to Version by @henryiii in #1001__slots__ to Specifiers by @henryiii in #1002__slots__ to Nodes by @henryiii in #1032version.__replace__ in specifier comparison by @notatallshaw in #999_get_spec_version in more places in Specifier by @notatallshaw in #1005set construction out of function by @henryiii in #1012Version.__str__ by about 10% by @henryiii in #997canonicalize_name by avoiding a regex by @henryiii in #1030Type annotations:
parse_wheel_filename docs by @ncoghlan in #973_version in .version.Version by @brettcannon in #927specifier.py by @subhajitsaha01 in #930Internal:
license-files by @DimitriPapadopoulos in #924python_version by @trim21 in #908ruff was renamed ruff-check in pre-commit by @henryiii in #933log_level is better than log_cli_level by @henryiii in #956partition in _parse_project_urls by @henryiii in #1013packaging.licenses by @notatallshaw in #1039Note truncated.
Re-add a test for Unicode file name parsing by @Siddhesh-Agarwal in #863
Full Changelog: 24.2...25.0
PEP 751: Add support for extras and dependency_groups markers. (885)
PEP 738: Add support for Android platform tags. (880)
The source is auto-formatted with ruff, not black by @DimitriPapadopoulos in #798
__str__ by @aryanpingle in #817> and < by @notatallshaw in #794Metadata.name isn't normalized by @brettcannon in #842Full Changelog: 24.1...24.2
PEP 639: Implement License-Expression and License-File (828)
Use !r formatter for error messages with filenames (844)
Add support for PEP 730 iOS tags (832)
Fix prerelease detection for > and < (794)
Fix uninformative error message (830)
Refactor canonicalize_version (793)
Patch python_full_version unconditionally (825)
Fix doc for canonicalize_version to mention strip_trailing_zero and a typo in a docstring (801)
Fix typo in Version __str__ (817)
Support creating a SpecifierSet from an iterable of Specifier objects (775)
pyupgrade/black/isort/flake8 → ruff by @DimitriPapadopoulos in #769
_parser docstring by @pradyunsg in #784markers.default_environment() by @edgarrmondragon in #753Full Changelog: 24.0...24.1
Document markers.default_environment() (753).
Add support for Python 3.13 (783).
Modernise type annotations (785).
Work around platform.python_version() returning non PEP 440 compliant version for non-tagged CPython builds (802).
Stop using deprecated/removed keys by @dstufft in https://github.com/pypa/packaging/pull/739
validate kwarg by @pradyunsg in https://github.com/pypa/packaging/pull/731test_glibc_version_string_ctypes_raise_oserror if ctypes is unavailable by @kevinchang96 in https://github.com/pypa/packaging/pull/741Py_NOGIL to Py_GIL_DISABLED by @hugovk in https://github.com/pypa/packaging/pull/747Full Changelog: https://github.com/pypa/packaging/compare/23.2...24.0
Do specifier matching correctly when the specifier contains an epoch number and has more components than the version (683)
Support the experimental --disable-gil builds in packaging.tags (727)
BREAKING: Make optional metadata.Metadata attributes default to None (733)
Fix errors when trying to access the description_content_type, keywords, and requires_python attributes on metadata.Metadata when those values have not been provided (733)
Fix a bug preventing the use of the built in ExceptionGroup on versions of Python that support it (725)
Bump pip version to avoid known vulnerabilities by @joycebrum in https://github.com/pypa/packaging/pull/720
_manylinux._is_compatible by @mayeut in https://github.com/pypa/packaging/pull/700armv7l manylinux/musllinux wheels on armv8l by @mayeut in https://github.com/pypa/packaging/pull/690Full Changelog: https://github.com/pypa/packaging/compare/23.1...23.2
Document calendar-based versioning scheme (716)
Enforce that the entire marker string is parsed (687)
Requirement parsing no longer automatically validates the URL (120)
Canonicalize names for requirements comparison (644)
Introduce metadata.Metadata (along with metadata.ExceptionGroup and metadata.InvalidMetadata; 570)
Introduce the validate keyword parameter to utils.normalize_name() (570)
Introduce utils.is_normalized_name() (570)
Make utils.parse_sdist_filename() and utils.parse_wheel_filename() raise InvalidSdistFilename and InvalidWheelFilename, respectively, when the version component of the name is invalid
Remove support for Python 3.7 (783)
chore: add typed classifier by @miketheman in https://github.com/pypa/packaging/pull/655
_key on _BaseVersion by @di in https://github.com/pypa/packaging/pull/669Full Changelog: https://github.com/pypa/packaging/compare/23.0...23.1
Parse raw metadata (671)
Import underlying parser functions as an underscored variable (663)
Improve error for local version label with unsupported operators (675)
Add dedicated error for specifiers with incorrect .* suffix
Replace spaces in platform names with underscores (620)
Relax typing of _key on _BaseVersion (669)
Handle prefix match with zeros at end of prefix correctly (674)
Remove unused LPAREN token from tokenizer by @hrnciar in https://github.com/pypa/packaging/pull/630
specifiers.BaseSpecifier.filter() by @henryiii in https://github.com/pypa/packaging/pull/643"extra" to be None in the marker environment by @pradyunsg in https://github.com/pypa/packaging/pull/650Full Changelog: https://github.com/pypa/packaging/compare/22.0...23.0
Allow "extra" to be None in the marker environment (650)
Refactor tags._generic_api to use EXT_SUFFIX (607)
Correctly handle trailing whitespace on URL requirements (642)
Fix typing for specifiers.BaseSpecifier.filter() (643)
Use stable Python 3.11 in tests (641)
Correctly handle non-normalised specifiers in requirements (634)
Move to src/ layout (626)
Remove __about__ file, in favour of keeping constants in __init__ (626)
Fix compatible version specifier incorrectly strip trailing '0' by @kasium in https://github.com/pypa/packaging/pull/493
__hash__/__eq__ to requirements by @abravalheri in https://github.com/pypa/packaging/pull/499setup-python v3 and use caching for GHA by @brettcannon in https://github.com/pypa/packaging/pull/521actions/* to their latest major versions by @mayeut in https://github.com/pypa/packaging/pull/557metadata module with a data class for core metadata by @brettcannon in https://github.com/pypa/packaging/pull/518utils.NormalizedName by @brettcannon in https://github.com/pypa/packaging/pull/565LegacySpecifier and LegacyVersion by @pradyunsg in https://github.com/pypa/packaging/pull/407metadata, versions and specifiers API documentation to sphinx.ext.autodoc by @pradyunsg in https://github.com/pypa/packaging/pull/572SpecifierSet.__iter__ by @hauntsaninja in https://github.com/pypa/packaging/pull/575>=5.0.0 by @mayeut in https://github.com/pypa/packaging/pull/586namedtuple by @layday in https://github.com/pypa/packaging/pull/589Metadata to str by @brettcannon in https://github.com/pypa/packaging/pull/596versionchanged for 21.3 by @brettcannon in https://github.com/pypa/packaging/pull/599packaging.metadata by @pradyunsg in https://github.com/pypa/packaging/pull/603Full Changelog: https://github.com/pypa/packaging/compare/21.3...22.0
Explicitly declare support for Python 3.11 (587)
Remove support for Python 3.6 (500)
Remove LegacySpecifier and LegacyVersion (407)
Add __hash__ and __eq__ to Requirement (499)
Add a cpNNN-none-any tag (541)
Adhere to 685 when evaluating markers with extras (545)
Allow accepting locally installed prereleases with SpecifierSet (515)
Allow pre-release versions in marker evaluation (523)
Correctly parse ELF for musllinux on Big Endian (538)
Document packaging.utils.NormalizedName (565)
Document exceptions raised by functions in packaging.utils (544)
Fix compatible version specifier incorrectly strip trailing 0 (493)
Fix macOS platform tags with old macOS SDK (513)
Forbid prefix version matching on pre-release/post-release segments (563)
Normalize specifier version for prefix matching (561)
Improve documentation for packaging.specifiers and packaging.version. (572)
Marker.evaluate will now assume evaluation environment with empty extra. Evaluating markers like "extra == 'xyz'" without passing any extra in the environment will no longer raise an exception (550)
Remove dependency on pyparsing, by replacing it with a hand-written parser. This package now has no runtime dependencies (468)
Update return type hint for Specifier.filter and SpecifierSet.filter to use Iterator instead of Iterable (584)
Replace the blank pyparsing 3 exclusion with a 3.0.5 exclusion (#481, #486)
pp3-none-any tag (#311)Update changelog entry for 21.1
Full Changelog: https://github.com/pypa/packaging/compare/21.1...21.2
Update branch names in release process in https://github.com/pypa/packaging/pull/444
Full Changelog: https://github.com/pypa/packaging/compare/21.0...21.1
PEP 656: musllinux support (411)
411)396)parse_sdist_filename (:issue:429)_hash attribute to short-circuit tag equality comparisons (:issue:417)specifier argument to SpecifierSet (:issue:437)403)366)Version.post and Version.dev (:issue:393)UnparsedVersion (:issue:398)packaging.specifiers.filter() (:issue:430)canonicalize_version() (:issue:402)Run isort over the code base (377)
377)379)387 and :issue:389)Revert back to setuptools for compatibility purposes for some Linux distros (363)
363)372)No unreleased changes.
No unreleased changes.
.. note:: This release was subsequently yanked, and these changes were included in 20.7.
Note
This release was subsequently yanked, and these changes were included in 20.7.
Fix flit configuration, to include LICENSE files (357)
Make intel a recognized CPU architecture for the universal macOS platform tag (361)
Add some missing type hints to packaging.requirements (issue:350)
Deprecate the LegacyVersion and LegacySpecifier classes (321)
Officially support Python 3.9 (343)
Deprecate the LegacyVersion and LegacySpecifier classes (321)
Handle OSError on non-dynamic executables when attempting to resolve the glibc version string.
Canonicalize version before comparing specifiers. (282)
Canonicalize version before comparing specifiers. (282)
Change type hint for canonicalize_name to return packaging.utils.NormalizedName. This enables the use of static typing tools (like mypy) to detect mixing of normalized and un-normalized names.
* Fix changelog for 20.2.
Fix changelog for 20.2.
Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8, aarch64), to report the wrong bitness.
Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8, aarch64), to report the wrong bitness.
Fix a bug caused by reuse of an exhausted iterator.
Add proper trove classifiers for PyPy support (198)
Add type hints (:issue:191)
Add proper trove classifiers for PyPy support (:issue:198)
Scale back depending on ctypes for manylinux support detection (:issue:171)
Use sys.implementation.name where appropriate for packaging.tags (:issue:193)
Expand upon the API provded by packaging.tags: interpreter_name(), mac_platforms(), compatible_tags(), cpython_tags(), generic_tags() (:issue:187)
Officially support Python 3.8 (:issue:232)
Add major, minor, and micro aliases to packaging.version.Version (:issue:226)
Properly mark packaging has being fully typed by adding a py.typed file (:issue:226)
Remove dependency on attrs (178, 179)
Remove dependency on attrs (178, 179)
Use appropriate fallbacks for CPython ABI tag (181, 185)
Add manylinux2014 support (186)
Improve ABI detection (181)
Properly handle debug wheels for Python 3.8 (172)
Improve detection of debug builds on Windows (194)
Add the packaging.tags module. (156)
Add the packaging.tags module. (156)
Correctly handle two-digit versions in python_version (119)
Fix string representation of PEP 508 direct URL requirements with markers.
Fix string representation of PEP 508 direct URL requirements with markers.
Better handling of file URLs
This allows for using file:///absolute/path, which was previously prevented due to the missing netloc.
This allows for all file URLs that urlunparse turns back into the original URL to be valid.
Improve error messages when invalid requirements are given. (129)
Improve error messages when invalid requirements are given. (129)
Fix utils.canonicalize_version when supplying non PEP 440 versions.
Fix utils.canonicalize_version when supplying non PEP 440 versions.
Drop support for python 2.6, 3.2, and 3.3.
Drop support for python 2.6, 3.2, and 3.3.
Define minimal pyparsing version to 2.0.2 (91).
Add epoch, release, pre, dev, and post attributes to Version and LegacyVersion (34).
Add Version().is_devrelease and LegacyVersion().is_devrelease to make it easy to determine if a release is a development release.
Add utils.canonicalize_version to canonicalize version strings or Version instances (121).
Fix markers that utilize in so that they render correctly.
Fix markers that utilize in so that they render correctly.
Fix an erroneous test on Python RC releases.
Add support for the deprecated python_implementation marker which was an undocumented setuptools marker in addition to the newer markers.
Add support for the deprecated python_implementation marker which was an undocumented setuptools marker in addition to the newer markers.
Add support for the deprecated, PEP 345 environment markers in addition to the newer markers.
Add support for the deprecated, PEP 345 environment markers in addition to the newer markers.
Fix a regression in parsing requirements with whitespaces between the comma separators.
Fix a regression in parsing requirements with whitespaces between the comma separators.
Fix a regression in parsing requirements like foo (==4).
Fix a regression in parsing requirements like foo (==4).
Fix a bug where packaging.requirements:Requirement was overly strict when matching legacy requirements.
Fix a bug where packaging.requirements:Requirement was overly strict when matching legacy requirements.
Add a function that implements the name canonicalization from PEP 503.
Add a function that implements the name canonicalization from PEP 503.
Implement requirement specifiers from PEP 508.
Implement requirement specifiers from PEP 508.
Relicense so that packaging is available under *either* the Apache License, Version 2.0 or a 2 Clause BSD license.
Relicense so that packaging is available under either the Apache License, Version 2.0 or a 2 Clause BSD license.
Support installation of packaging when only distutils is available.
Fix == comparison when there is a prefix and a local version in play. (41).
Implement environment markers from PEP 508.
Normalize post-release spellings for rev/r prefixes. 35
Normalize post-release spellings for rev/r prefixes. 35
Fix an error where the arbitrary specifier (===) was not correctly allowing pre-releases when it was being used.
Fix an error where the arbitrary specifier (===) was not correctly allowing pre-releases when it was being used.
Expose the specifier and version parts through properties on the Specifier classes.
Allow iterating over the SpecifierSet to get access to all of the Specifier instances.
Allow testing if a version is contained within a specifier via the in operator.
Fix a logic error that was causing inconsistent answers about whether or not a pre-release was contained within a SpecifierSet or not.
Fix a logic error that was causing inconsistent answers about whether or not a pre-release was contained within a SpecifierSet or not.
Add Version().is_postrelease and LegacyVersion().is_postrelease to make it easy to determine if a release is a post release.
Add Version().is_postrelease and LegacyVersion().is_postrelease to make it easy to determine if a release is a post release.
Add Version().base_version and LegacyVersion().base_version to make it easy to get the public version without any pre or post release markers.
Support the update to PEP 440 which removed the implied !=V.* when using either >V or <V and which instead special cased the handling of pre-releases, post-releases, and local versions when using >V or <V.
Normalize release candidates as rc instead of c.
Normalize release candidates as rc instead of c.
Expose the VERSION_PATTERN constant, a regular expression matching a valid version.
Ensure that versions are normalized before comparison when used in a specifier with a less than (<) or greater than (>) operator.
Ensure that versions are normalized before comparison when used in a specifier with a less than (<) or greater than (>) operator.
BACKWARDS INCOMPATIBLE Refactor specifier support so that it can sanely handle legacy specifiers as well as PEP 440 specifiers.
BACKWARDS INCOMPATIBLE Refactor specifier support so that it can sanely handle legacy specifiers as well as PEP 440 specifiers.
BACKWARDS INCOMPATIBLE Move the specifier support out of packaging.version into packaging.specifiers.
Add prerelease support to Specifier.
Add prerelease support to Specifier.
Remove the ability to do item in Specifier() and replace it with Specifier().contains(item) in order to allow flags that signal if a prerelease should be accepted or not.
Add a method Specifier().filter() which will take an iterable and returns an iterable with items that do not match the specifier filtered out.
Allow LegacyVersion and Version to be sorted together.
Allow LegacyVersion and Version to be sorted together.
Add packaging.version.parse() to enable easily parsing a version string as either a Version or a LegacyVersion depending on it's PEP 440 validity.
.. _master: https://github.com/pypa/packaging/
Initial release.
Initial release.
On this page
Changelog
unreleased
26.3 - 2026-08-03
26.2 - 2026-04-24
26.1 - 2026-04-14
26.0 - 2026-01-20
26.0rc3 - 2026-01-15
26.0rc2 - 2026-01-12
26.0rc1 - 2026-01-09
25.0 - 2025-04-19
24.2 - 2024-11-08
24.1 - 2024-06-10
24.0 - 2024-03-10
23.2 - 2023-10-01
23.1 - 2023-04-12
23.0 - 2023-01-08
22.0 - 2022-12-07
21.3 - 2021-11-17
21.2 - 2021-10-29
21.1 - 2021-10-29
21.0 - 2021-07-03
20.9 - 2021-01-29
20.8 - 2020-12-11
20.7 - 2020-11-28
20.6 - 2020-11-28
20.5 - 2020-11-27
20.4 - 2020-05-19
20.3 - 2020-03-05
20.2 - 2020-03-05
20.1 - 2020-01-24
20.0 - 2020-01-06
19.2 - 2019-09-18
19.1 - 2019-07-30
19.0 - 2019-01-20
18.0 - 2018-09-26
17.1 - 2017-02-28
17.0 - 2017-02-28
16.8 - 2016-10-29
16.7 - 2016-04-23
16.6 - 2016-03-29
16.5 - 2016-02-26
16.4 - 2016-02-22
16.3 - 2016-02-21
16.2 - 2016-02-09
16.1 - 2016-02-07
16.0 - 2016-01-19
15.3 - 2015-08-01
15.2 - 2015-05-13
15.1 - 2015-04-13
15.0 - 2015-01-02
14.5 - 2014-12-17
14.4 - 2014-12-15
14.3 - 2014-11-19
14.2 - 2014-09-10
14.1 - 2014-09-08
14.0 - 2014-09-05
Your coding agent can read these notes before it upgrades. Set up the MCP server →