# packaging: the shared implementation of Python packaging specifications

> packaging is the Python Packaging Authority library that implements the interoperability specifications other tools depend on, covering versions, specifiers, markers, requirements, tags, metadata, lockfiles and licenses. It fits any tool that has to reason about what a Python package is, and it is a library to call rather than a program to run.

**pypa/packaging** — Core utilities for Python packages

- Repository: https://github.com/pypa/packaging
- Website: https://packaging.pypa.io/
- Stars: 752 · Forks: 359
- Language: Python
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/pypa-packaging

## One implementation of the specifications, so two tools do not disagree

packaging is the reference implementation of a set of Python packaging interoperability specifications, and the project description explains the reasoning in two categories. Some of the specifications have clearly one correct behaviour, and the example given is PEP 440 for version handling. Others benefit greatly from having a single shared implementation, and the example there is PEP 425 for the wheel format.

That is the whole pitch. Two tools that each parse a version string will eventually disagree about a trailing zero or a local segment, and two tools that each read package metadata will eventually disagree about what a field means. The specifications settle what the answer is, and this library is where that answer is implemented so that everyone calls the same code.

The scope is wide but bounded. The README lists what the project includes as version handling, specifiers, markers, requirements, tags, metadata, lockfiles and utilities, and the documentation covers the same set in more detail, adding licenses, direct URL helpers, dependency groups and errors.

The audience is other libraries and tools rather than end users. A build backend, a resolver, a linter that flags dependency problems, or an application that needs to compare the version it is running against the version it requires would all end up here. The project is hosted under the Python Packaging Authority, with documentation at packaging.pypa.io, and the licence situation is worth checking before you build on it.

## Calendar based versioning instead of semantic version numbers

The README states the versioning scheme in one sentence: the packaging library uses calendar based versioning, in the form YY.N.

The release list shows how that plays out. The recent versions are 26.3, published on 2026-08-04, 26.2 on 2026-04-24, and 26.1 on 2026-04-14. So the year comes first and the second number counts releases within that year. The last push was on 2026-10-20, which is after the newest tag, so the main branch carries work that is not in any release.

For a dependency this is mostly a non-issue, since packaging is normally installed by a resolver that picks a version for you. It matters in two situations. The first is a project that pins dependencies exactly, which for a library like this is unusual but happens in locked environments. The second is the compatibility question: a YY.N scheme signals that the project expects to keep changing its API surface in a way a semantic version would not, and a consumer that wants stability should pin rather than float.

The version is also declared as dynamic in the project metadata, which is the flit convention: the version lives in the source tree, and the build backend reads it, rather than it being duplicated in the configuration.

## No dependencies, Python 3.10 and later, and a dual licence

The project metadata is short enough to read in full, and two fields in it shape how you can use the library.

The first is that there are no runtime dependencies at all.

```toml
dependencies = []
```

That is deliberate for a library that sits underneath installers and build backends. A dependency here would be a dependency for every tool in the Python packaging chain, and the compatibility matrix would grow with it. The test dependencies, on the other hand, are substantial: coverage with the toml extra, hypothesis for property based testing, pip, pretend, pytest, and tomli for interpreters before Python 3.11. The presence of hypothesis is a signal about how the library is checked, since it generates inputs rather than enumerating them.

The second field is the licence, and it is not a single one. The project metadata declares a dual licence, Apache-2.0 OR BSD-2-Clause, and the repository carries both a LICENSE.APACHE and a LICENSE.BSD file alongside a LICENSE file. The top level entry for the licence is recorded as a custom licence GitHub cannot classify, so the operative terms sit in those files rather than in the licence field the repository page displays.

The Python floor is requires-python of 3.10 or later, and the classifier list runs from Python 3.10 through 3.15, includes CPython and PyPy implementations, and carries a free threading resilient classifier, which tells you the project is at least aware of the no GIL build.

## Installing with pip and building with flit

The installation instruction in the README is one line, and it is the standard one for a library on PyPI.

```bash
    pip install packaging
```

The documentation for the project is at packaging.pypa.io, and it is the place to look for the API of each area, since the README itself only lists the topics. The same page carries the project development and security documentation, and the README points at CONTRIBUTING.rst for how to contribute and how to report a potential security issue. A SECURITY.md file is present at the top level as well, and the code of conduct is the PSF Code of Conduct.

Building the project is handled by flit. The build system section of the configuration requires flit_core of at least 3.12 and names flit_core.buildapi as the build backend, and the version is dynamic, so the release version is read from the source rather than from the configuration file. The sdist configuration includes the tests directory, the docs directory and the changelog in the source distribution, which is what lets a downstream packager run the test suite against an sdist.

The repository also carries a noxfile.py at the top level, a pre-commit configuration, a tasks directory, an asv configuration with a benchmarks directory, and a tests directory. The asv setup and the separate benchmark dependency group indicate that the project tracks its own performance, and pip appears in that group as well, so the benchmarks are run against a real installer rather than a mock.

## Dependency groups instead of extras, and a split of tooling concerns

The dependency groups in the project metadata are worth reading closely, because they reflect a shift in how the project separates concerns.

```toml
[dependency-groups]
test = [
  "coverage[toml]>=7.2.0",
  "hypothesis>=6.0.0",
  "pip>=21.1",
  "pretend",
  "pytest>=6.2.0",
  "tomli; python_version<'3.11'",
  "tomli_w",
]
```

These are groups, not extras. An extras mechanism puts optional dependencies into the published package metadata, so a user installing a feature pulls them. Groups are for the project's own development, which is why the list is split by purpose: a test group, a docs group that contains only furo, a benchmark group with asv and pip, and a dev group that includes the test and benchmark groups by reference.

That separation means the published wheel carries no optional dependency baggage at all, which is consistent with the empty dependencies list. A consumer that needs a specific piece of packaging functionality, such as reading lockfiles or handling licenses, imports it from the same distribution.

The tomli marker is the other detail. The conditional dependency on tomli for interpreters before Python 3.11 is there because the standard library only grew tomllib in 3.11, and the project supports 3.10. The test group also pulls in tomli_w for writing TOML back out, and pretend, which is a small library for stubbing objects in tests.

## Where a hand written version parser goes wrong

The reason this library exists is best illustrated by the specifications it implements, and the first one in the README is PEP 440, the version scheme. That specification is not a simple rule.

A version number in the Python ecosystem has a public part, a pre-release segment, a post-release segment, a development release segment, a local version segment, and an epoch. Each of those can be present or absent, and each has ordering rules. A development release sorts before the final release it leads to. A post release sorts after. A local version is used for local builds and sorts after the same public version without one. An epoch overrides everything when two versions differ only in epoch.

Then there are the version specifiers, the expressions that say what a requirement accepts, with comparison operators and compatible release clauses that have their own semantics. And then the environment markers, the conditional expressions that decide whether a dependency applies at all, which are evaluated against the target environment rather than the current one.

Writing that correctly in a regex is possible but is exactly the kind of code that passes every test you wrote and fails on an input nobody thought of. A library that owns one implementation, driven by property based tests with hypothesis, moves that risk to a single place. The cost is that the API is larger than the task looks, and the documentation is the real product: a caller who does not know whether a specifier is inclusive has to read the docs, and the docs are structured by topic rather than by task.

## Metadata, tags, lockfiles and licenses, all in one distribution

The newer areas in the list are what make this more than a version library.

Metadata handling is the field that the ecosystem formats disagree on most, and having one implementation of it is what lets a tool read a package's declared dependencies without caring which metadata version the author used. Tags are the other end of the problem: a tag names a platform and an interpreter and a wheel format, and producing the right set of tags for an environment is a fiddly enumeration, which is why the wheel specification is cited as an example of work that benefits from a shared implementation.

Lockfiles appear in the documentation list under the name pylock, which is a filename format rather than a generic word, and the presence of a lockfile implementation in a library at this level is a signal about how the packaging specifications have consolidated. Direct URL helpers and dependency groups sit alongside it, and licenses are documented as their own topic.

That breadth is the argument for depending on this library rather than on a narrower one. A tool that needs version comparison, marker evaluation and metadata reading would otherwise pull three packages, and each of those is a chance for the answers to diverge from the specifications. The trade-off is that the API surface is wide and the learning cost sits in the documentation, so a caller working on one narrow task still has to find its way through a larger API.

## Conclusion

packaging fits any tool that parses versions, evaluates environment markers or reads package metadata, and it does not fit a project that only installs packages, since pip already brings it in for that job. Before depending on it directly, check the Python floor, because the project metadata declares requires-python of 3.10 or later and the classifier list stops at Python 3.15, and pin the version you want, because the library uses calendar based versioning with YY.N releases rather than semantic version numbers.

## FAQ

### How do I install the packaging library?

The README says to use pip to install these utilities, with the command pip install packaging. The library requires Python 3.10 or later and has no runtime dependencies.

### What is the versioning scheme used by the packaging library?

It uses calendar based versioning in the form YY.N, and the recent releases are 26.3, 26.2 and 26.1. The version is declared dynamic in the project metadata and is read from the source by the flit build backend.

### What license is the packaging library released under?

The project metadata declares a dual license of Apache-2.0 OR BSD-2-Clause, and the repository contains LICENSE.APACHE and LICENSE.BSD files alongside the LICENSE file. The licence field on the repository page is recorded as a custom license GitHub cannot classify.

### What does the packaging library cover?

The README lists version handling, specifiers, markers, requirements, tags, metadata, lockfiles and utilities, and the documentation adds licenses, direct URL helpers, dependency groups and errors. The library implements the Python packaging interoperability specifications, including PEP 440 and PEP 425.

### Does the packaging library have any runtime dependencies?

No. The project metadata declares an empty dependencies list, which keeps the library usable underneath installers and build backends. Test, documentation, benchmark and development dependencies are declared separately as dependency groups.

## Sources

- [Issues](https://github.com/pypa/packaging/issues)
- [Project website](https://packaging.pypa.io/)
- [pypa/packaging on GitHub](https://github.com/pypa/packaging)
- [README](https://github.com/pypa/packaging/blob/main/README.md)
- [Releases](https://github.com/pypa/packaging/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pypa-packaging
