Set requires-python to the oldest Python version you actually test, written as a lower bound such as >= 3.9. Avoid upper bounds. Installers use this field to skip releases that do not match the user's Python, so a wrong value either blocks users or lets broken installs through.
What the field does
requires-python in the project table maps to the Requires-Python core metadata field. The packaging guide describes it as declaring the minimum version of Python you support. Installers that understand this metadata, including pip, compare it to the running interpreter.
If your newest release does not match, pip looks back through older releases until it finds one that does. That behaviour is what lets you drop old Python versions without stranding users on old interpreters.
Syntax
[project]
requires-python = ">= 3.9"
You can also exclude specific point releases.
requires-python = ">= 3.7, != 3.7.0, != 3.7.1"
The guide says exclusion rules must follow the version specifiers specification. For how those operators behave, see our explainer on compatible release versus exact match.
Why not add an upper cap
The packaging guide says to avoid upper bounds on this field, for example >= 3.8, < 3.10, because they can cause different errors and version conflicts. The reason is practical. Dependency resolvers need to find a set of versions that all agree. If your library caps Python below a future release, every project that uses your library is blocked from upgrading Python, even if your code would have worked.
If a future Python release truly breaks your code, release a fix or a new version, not a pre emptive ban.
Requires-python versus classifiers
Classifiers like "Programming Language :: Python :: 3.12" are for search and browsing on PyPI. The guide states plainly that this information is not used for installing. Only requires-python restricts what can be installed.
| Field | Controls installs | Shows on PyPI |
|---|---|---|
| requires-python | Yes | As metadata |
| Python version classifiers | No | Yes, for browsing |
Choosing the lower bound
- List the Python versions your CI runs.
- Take the oldest one that passes.
- Use that as the lower bound.
- Remove the bound's old versions from CI only when you also change the field.
Do not claim a version you do not test. A lower bound is a promise to every user on that version.
Keep it honest over time
When a Python release reaches end of life, many libraries raise their lower bound at the next major or minor release. Doing so is safe for existing users, because pip falls back to the last release that supports their interpreter. We cover the mechanics in how to drop support for an old Python version.
Safeguard's dependency scanning and SBOM generation record the versions your product resolves to. A free scan shows what you ship today.
Quick check
Does requires-python affect old releases already on PyPI?
Metadata is stored per release. Changing the field only affects the release you build next.
Is the Python classifier enough to block old interpreters?
No. Classifiers are for searching and browsing. Use requires-python to restrict installs.
Should I set an upper bound for the next Python release?
The packaging guide advises against it, because it can cause resolver errors and blocks users from upgrading.
Sources
- Writing your pyproject.toml, Python Packaging User Guide
- Dropping support for older Python versions, Python Packaging User Guide
- Packaging Python Projects, Python Packaging User Guide
Last reviewed: 19 October 2026.