What is requires-python?
requires-python is a field in pyproject.tomlThe standard configuration file for Python projects. Declares the project name, version, dependencies, build system, and tool settings in one place.
Learn more →
that declares which Python versions your project supports. Package managers read it before installing anything, and resolversThe part of a package manager that chooses a set of package versions satisfying every dependency constraint.
use it to filter out incompatible dependency versions. It lives in the [project] table, standardized by PEP 621:
[project]
name = "my-project"
requires-python = ">=3.10"That single line tells every tool in the ecosystem: this project runs on Python 3.10 and above.
How the syntax works
The value is a version specifier string, the same syntax used for package version constraints. The most common pattern is a lower bound:
| Specifier | Meaning |
|---|---|
>=3.10 |
Python 3.10 or newer (the default recommendation) |
>=3.10,<4 |
Python 3.10 through 3.x (equivalent in practice, since Python 4 does not exist) |
>=3.10,<3.14 |
Python 3.10 through 3.13 only |
==3.12.* |
Only Python 3.12.x (any patch) |
A bare lower bound like >=3.10 is the right choice for most projects. It supports every future Python release without requiring a metadata update each October when a new CPython version ships.
How uv uses requires-python
uv treats requires-python as a hard constraint at two points:
Interpreter selection. When uv run or uv sync picks a Python interpreterThe program that reads and executes Python code. When you run "python3 hello.py", python3 is the interpreter.
, the interpreter’s version must satisfy requires-python. If the pinned version in .python-version falls outside the range, uv stops with an incompatibility error.
Dependency resolution. uv lock resolves dependencies across the full range that requires-python allows. A project declaring >=3.9 forces the resolver to find versions compatible with 3.9 through the latest release. If a dependencyAn external package your project needs, listed in pyproject.toml so tools can install it automatically.
dropped 3.9 support, the resolver fails even on a machine running 3.14. Raising the requires-python floor is often the fix. See how to debug uv dependency resolution failures.
How pip uses requires-python
pip checks requires-python during installation. If the running interpreter falls outside the declared range, pip skips that release and tries an older one whose requires-python matches. If no compatible release exists, the install fails.
Build tools like setuptools and the uv build backend embed requires-python into the package’s core metadata (Requires-Python header) when building a wheelA prebuilt Python package file (.whl) that installs without compiling anything. The standard distribution format for Python packages.
Learn more →
or sdist. That metadata travels with the package to PyPIThe Python Package Index, the public repository where Python packages are published and downloaded from. "pip install requests" fetches requests from PyPI.
Learn more →
, where the index uses it to filter downloads by interpreter version.
How requires-python differs from .python-version
These two files both name Python versions but answer different questions:
.python-version |
requires-python |
|
|---|---|---|
| Purpose | Pins the development interpreter | Declares supported versions |
| Typical value | 3.13 (one version) |
>=3.10 (a range) |
| Who reads it | Version managers (uv, pyenv) | Package managers, resolvers, PyPI |
| Shipped with the package | No | Yes (embedded in metadata) |
| Affects end users | No | Yes |
Your .python-version might say 3.13 while requires-python says >=3.10. That means you develop on 3.13 but your package works for anyone on 3.10 or newer. The pinned version must fall within the requires-python range, or uv reports an incompatibility error.
For the full story on .python-version, see What is a .python-version file?
Common mistakes
Too-narrow bounds. Setting ==3.12.* locks the project to one minor version. Every user on 3.13 or 3.14 is excluded, and the constraint must be manually widened each year. Use >=3.12 unless the project genuinely cannot run on newer interpreters.
Too-wide bounds. Claiming >=3.8 while only testing on 3.12 and 3.13 promises compatibility that does not exist. The resolver believes the claim, and users on 3.8 discover the lie at runtime. Set the floor to the oldest version your CI matrix covers.
Forgetting to update after dropping old versions. Removing Python 3.9 compatibility from code (dropping from __future__ import annotations, using match statements, removing 3.9-era workarounds) without raising the requires-python floor leaves the metadata out of sync. pip and uv still offer the package to 3.9 users, who then hit runtime errors.
Confusing it with .python-version. Editing requires-python does not change which interpreter uv run uses; that is controlled by .python-version. Editing .python-version does not change which versions the package supports; that is requires-python. Changing supported versions requires editing both: lower the requires-python floor, then uv python pin the new development version. See how to change the Python version of a uv project.
When to raise the floor
The CPython release cycle gives each version five years of support. When a version reaches end-of-life, dropping it from requires-python is safe for most projects. Raising the floor unlocks newer syntax, drops compatibility workarounds, and (in uv projects) can resolve dependency conflicts caused by old versions dragging in outdated transitive pins.
A practical rule: set the floor to the oldest version your CI tests against, and raise it when you stop testing against an older version.