Checking & Testing Distributions
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the Python Reference bibliography. |
A release cannot be edited after upload, so check the files you are about to upload, not the source tree. This page runs the
checks on birdtally's dist/ with twine 7.0.0 (readme-renderer 46.0), check-wheel-contents 0.6.3 and pip 26.2.2 on CPython
3.14.6, on 2026-10-22. TestPyPI is described, not used: nothing from this section is uploaded to a public index.
twine check
twine check validates the metadata of each file and renders its long description the way PyPI does:
python -m pip install twine==7.0.0
twine check --strict dist/*
Checking dist/birdtally-0.1.0-py3-none-any.whl: PASSED
Checking dist/birdtally-0.1.0.tar.gz: PASSED
A reStructuredText README with a Sphinx role (:py:func: around birdtally.tally_by_site), which plain docutils on PyPI does not know,
fails with exit code 1:
Checking dist/birdtally-0.1.0-py3-none-any.whl: FAILED
ERROR `long_description` has syntax errors in markup and would not be
rendered on PyPI.
line 4: Error: Unknown interpreted text role "py:func".
PyPI rejects such an upload ("the description failed to render"). Without a readme, the check passes with warnings, and
--strict turns warnings into a failure:
Checking …/dist/birdtally-0.1.0-py3-none-any.whl: PASSED with warnings
WARNING `long_description_content_type` missing. defaulting to `text/x-rst`.
WARNING `long_description` missing.
Use --strict in CI. Markdown READMEs are rendered as GitHub-flavoured Markdown by default (CommonMark with
text/markdown; variant=CommonMark); Markdown errors do not fail the check, so look at the rendering on TestPyPI once.
check-wheel-contents
twine check does not look at the files in a wheel. The third-party check-wheel-contents does: duplicate files, .pyc files,
modules outside the package, an empty wheel. A typo in hatchling’s wheel target (packages = ["src/birdtaly"]) made build
succeed and twine pass, but produced a wheel with no code:
python -m build -w
twine check dist/*.whl
check-wheel-contents dist/*.whl
Successfully built birdtally-0.1.0-py3-none-any.whl
Checking …/birdtally-0.1.0-py3-none-any.whl: PASSED
…/birdtally-0.1.0-py3-none-any.whl: W007: Wheel library is empty
…/birdtally-0.1.0-py3-none-any.whl: W008: Wheel is empty
check-wheel-contents exits with 1 here and prints OK for the correct wheel. It does not notice a missing license file (the
license-files mistake shown on
Preparing a Library
passed), so keep unzip -l in the routine.
Source: check-wheel-contents.
Testing the artefacts, not the tree
Tests in the project directory can import the source instead of the installed package (less so with a src layout). Install the built files into a fresh environment, without the index, and run the tests and the CLI there:
python -m venv /tmp/release-check
/tmp/release-check/bin/python -m pip install --no-index --find-links dist/ birdtally
/tmp/release-check/bin/birdtally examples/survey.csv --by site
/tmp/release-check/bin/python -m pip install pytest
cd /tmp && /tmp/release-check/bin/python -m pytest -q …/birdtally/tests
Installing collected packages: birdtally
Successfully installed birdtally-0.1.0
{
"north-marsh": 6,
"east-dunes": 3
}
.. [100%]
2 passed in 0.01s
Run pytest from outside the project so the source tree is not on sys.path. Then test the sdist the same way, forcing pip to
build it (--no-binary birdtally). Offline, that fails, because building needs the backend:
Processing ./dist/birdtally-0.1.0.tar.gz
Installing build dependencies: started
Installing build dependencies: finished with status 'error'
error: subprocess-exited-with-error
× installing build dependencies for birdtally did not run successfully.
│ exit code: 1
╰─> [3 lines of output]
Looking in links: dist/
ERROR: Could not find a version that satisfies the requirement hatchling>=1.27 (from versions: none)
ERROR: No matching distribution found for hatchling>=1.27
[end of output]
With the index available, pip install dist/birdtally-0.1.0.tar.gz built and installed it. This is exactly what a user without a
matching wheel experiences, so it is worth the minute. build’s default (wheel built from the sdist) already covers most sdist
mistakes.
Source: build: Testing the sdist.
TestPyPI
TestPyPI (https://test.pypi.org/) is a separate instance of PyPI for rehearsing a release (not run here):
-
it needs its own account and tokens, and its own trusted publishers;
-
its database is pruned from time to time, accounts included;
-
it has the same rules as PyPI: a filename can be used once, so bump a dev or post version for each rehearsal (
0.1.0.dev1,0.1.0.dev2); -
upload with
twine upload --repository testpypi dist/*(see Publishing with build & twine); -
install from it with
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ birdtally, so that dependencies still come from PyPI.
That last command merges both indexes, so any project name could be served by TestPyPI, where anyone can upload anything: a dependency-confusion risk. Use it only in a throwaway environment, or pin the dependencies with hashes. Size-limit increases are requested per index, so a project that needed one on PyPI may hit TestPyPI’s default limit.