Safeguard
Developer Tools•

How do you test a Python release on TestPyPI before publishing?

Upload to TestPyPI with twine, install with an index URL, add an extra index for dependencies, and avoid its traps. A short rehearsal for every release.

Safeguard Security Team
4 min read

Register a separate TestPyPI account, upload your build with twine upload --repository testpypi dist/*, then install it into a clean virtual environment using TestPyPI as the index. This rehearses the real release without touching PyPI. Mind the dependency trap described below.

What TestPyPI is

TestPyPI is a separate instance of the Python Package Index at test.pypi.org, meant for trying out distribution tools and processes without affecting the real index. It has its own database, so your PyPI login does not work there. The packaging guide also notes the database may be periodically pruned, so accounts and packages can disappear.

Steps

  1. Register at TestPyPI with a separate account and verify your email.
  2. Create an API token on TestPyPI. The tutorial walks through setting one up, and notes you only see the token once.
  3. Build with python -m build.
  4. Check with twine check dist/*.
  5. Upload.
python3 -m twine upload --repository testpypi dist/*
  1. Install in a clean environment.
python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps your-package
  1. Import and run a small test.

The dependency trap

The tutorial uses --no-deps for a reason. TestPyPI does not have the same packages as the real index, so installing dependencies from it may fail or fetch something unexpected. If your library has dependencies and you want to test the full install, the guide says to add the real index as an extra.

python3 -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ your-package

Be careful with this form. Mixing two indexes is how a test install can pull in something unexpected, which is the risk the tutorial warns about. For a hygienic rehearsal, install dependencies first from the real index, then install your package with --no-deps from TestPyPI.

Version numbers

TestPyPI follows the same filename rules as PyPI, so you cannot upload the same filename twice. Use a development or pre release version for rehearsals, such as a dev suffix, and keep your real version number for the real upload. The version specifiers spec describes dev, alpha, beta and release candidate segments.

What a rehearsal catches

  • A README that fails to render.
  • Missing files in the wheel.
  • Wrong metadata, such as a missing Requires-Python.
  • A broken entry point for a command line tool.
  • Publishing workflow permission errors, if you use a trusted publisher on TestPyPI.

Rehearse the automation, not just the files

The packaging guide for GitHub Actions configures two trusted publishers, one for TestPyPI and one for PyPI, and uses separate environments named for each. That lets CI exercise the same workflow end to end. Our trusted publisher setup steps cover the mechanics.

Safeguard's SBOM and dependency scanning can be part of the same rehearsal. A free scan on the built project is an easy start.

Quick check

Do my PyPI credentials work on TestPyPI?

No. The two services have separate databases, so you need a separate account and token for TestPyPI.

Why does the tutorial use no-deps?

TestPyPI does not carry the same packages as PyPI, so installing dependencies from it can fail or pick up something unexpected.

Can I reuse a version I tested on TestPyPI?

On the real PyPI, yes, because it is a different index. Use a distinct pre release version for rehearsals to avoid confusion.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.