PyPI Release (Maintainers Only)

This document goes through the steps necessary to upload a release to PyPI. It is assumed that the project is in an installable state, and is ready to be uploaded. This should normally be done as part of the release workflow, but may be done separately.

() Pre-flight checks

1.a) Ensure a clean git tree

If the following prints anything, fix it before continuing:

git status --porcelain

1.b) Read the version number

The canonical version is in pyproject.toml.

VERSION="$(python - <<'PY'
import tomllib
with open("pyproject.toml", "rb") as f:
    print(tomllib.load(f)["project"]["version"])
PY
)"
echo "Preparing to publish version: ${VERSION}"

2) Ensure Fresh Build Artifacts

Remove any old build outputs:

rm -fr dist/ build/ *.egg-info

3) Build Distribution

uv run python -m build

This should create the following files:

  • dist/buffaloexample-${VERSION}*.whl

  • dist/buffaloexample-${VERSION}.tar.gz

4) Test Distribution

Use twine, version 6.1 or higher, to check these files

uv run twine check dist/*

If this fails, inspect the wheel metadata:

python - <<'PY'
import zipfile, glob
whl = glob.glob("dist/*.whl")[0]
with zipfile.ZipFile(whl) as z:
    meta = [n for n in z.namelist() if n.endswith(".dist-info/METADATA")][0]
    print(z.read(meta).decode("utf-8", errors="replace")[:400])
PY

5) Upload Release

Caution: Do not put the credentials used to authenticate to PyPI into the command line (shell history) or commit them to the project repository. You can either use shell variables that are integrated into your shell session, password manager integration, or some other secure mechanism, or you can use the ~/.pypirc file to store them.

Use twine to upload the release.

5.b) Upload to PyPI

Once the release has been confirmed to work, upload it to PyPI

uv run twine upload dist/*

6) Installation Test

To ensure that the package is available, run the following test based on where the release was published.

These are example methods for testing if the installation was successful. Modify these as needed for the specific needs of the project.

6.a) If published to PyPI

To check for a Python library:

python -m venv /tmp/pypi-venv
source /tmp/pypi-venv/bin/activate
pip install "BuffaloExample==${VERSION}"
python -c "import buffaloexample; print('import OK')"

Then to check for an application (still in the virtual environment):

command -v hello-world

6.b) If published to TestPyPI

To check for a Python library:

python -m venv /tmp/testpypi-venv
source /tmp/testpypi-venv/bin/activate
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple "BuffaloExample==${VERSION}"
python -c "import buffaloexample; print('import OK')"

Then to check for an application (still in the virtual environment):

command -v hello-world

7) If Error With Package

If there is an error with the package after uploading to PyPI, then you have to bump the version on the next attempt after the problem has been fixed. You cannot re-upload the same version.