Raise requires-python in your next release and check that your wheels are tagged correctly. Installers that read this metadata will give users on old interpreters the last release that still supports them, instead of a broken install. You do not need to delete or edit anything old.
Why this works
The Python Packaging User Guide explains the mechanism. Installers that follow core metadata 1.2 or newer, such as pip, match the running Python against the Requires-Python field. If the newest release does not match, the installer attempts the last distribution that did.
So dropping Python support is a metadata change, not a deletion. Old releases stay available for users who still need them.
Steps
- Pick the new minimum. Choose a version you test in CI. Do not guess.
- Edit pyproject.toml. Set the lower bound, for example
requires-python = ">= 3.10". Avoid upper bounds. - Fix leftover wheel tags. The guide warns that wheels built by older setuptools projects for both Python 2 and 3 were often tagged py2.py3. When dropping Python 2, change that to py3. If your config sets
universal = 1in a bdist_wheel section, remove it or set it to 0. - Update classifiers. Remove version classifiers for versions you dropped, so the PyPI page matches.
- Build with
python -m build. Do not call setup.py directly, because the guide says those commands are deprecated. - Check the metadata. Open PKG-INFO in the sdist and confirm Requires-Python shows your new value.
- Validate with twine. The guide recommends installing twine and checking the metadata before upload.
- Release with a clear version bump and changelog entry. Use whichever bump your versioning policy says signals a compatibility change.
What users will see
| User's Python | Result after your release |
|---|---|
| Meets the new minimum | Gets the new release |
| Below the new minimum | Gets the last older release that matches |
| Uses an installer that ignores the field | May get a failed install or import errors |
The last row is why the guide states a requirement: this workflow relies on users installing with pip or another installer that supports metadata 1.2.
Pitfalls
- Forgetting to tag wheels correctly. A wheel tagged for versions you no longer support is misleading.
- Dropping and adding features in one release. If a user lands on the older release, they miss both. Keep the release focused.
- Not backporting security fixes. Users on the older line stay on the older release. If a vulnerability affects it, decide whether to publish a patch for that line, or say plainly that it is unsupported.
That last point matters for security. Python itself stops issuing security fixes for old branches, and dependencies you pull in may drop support too. Check our note on which Python versions still get security fixes.
Safeguard's dependency scanning and SBOM generation can show which of your product's components sit on an unsupported line. A free scan is a place to start.
Quick check
Do I need to yank old releases when I drop a Python version?
No. Yanking is for broken or vulnerable releases. Old releases keep serving users on old Python.
What if a user ignores the metadata?
Installers that do not support core metadata 1.2 may fetch the newest release anyway. Mention the change in your changelog.
Should I cap the upper Python version?
The packaging guide advises against upper bounds on this field because they cause conflicts.
Sources
- Dropping support for older Python versions, Python Packaging User Guide
- Writing your pyproject.toml, Python Packaging User Guide
- Is setup.py deprecated?, Python Packaging User Guide
Last reviewed: 19 October 2026.