Repository

Looks good to me!

User Tools

Site Tools


Action disabled: diff
kb:lang:scripting:python:pypi

PyPI

Changelog

  • 2025-08-22: Init

Mostly referring to this user guide to upload packages onto PyPI.

Manual quick summary

Create an account, and reserve a package namespace. Create an API token for uploading.

For the project, minimally prepare a pyproject.toml with the [build-system] and [project] tables populated.

Install the build package that provides functionality to prepare the source tarball ("source distribution") and build into a wheel. Then finally upload it using twine onto PyPI.

user:~$ python -m build
user:~$ python -m twine upload dist/*
user:~$ python -m twine upload --repository testpypi dist/*  # TestPyPI

GitHub Actions for CI

Previous workflow involved manually building, and uploading to PyPI with twine using an API token. The continuous integration method does a couple things different:

  • Build is triggered with every commit push, using a series of checkout + install Python & build + upload into build environment steps
  • Publishing involves retrieving the build and publishing it directly to PyPI

Authentication is done on the PyPI via OIDC: account on PyPI specifies from which source should a distribution push be accepted, and PyPI authenticates directly with the source itself (that acts as an identity provider).

PyPI screenshot when configuring OIDC

Additional security measures taken include:

  • Separating the build task from the publish task, to avoid potential compromise of authentication tokens
  • Github environments are used to provide additional trigger requirements (e.g. extra reviewers, push only from protected branches).

To test installation from TestPyPI, add the extra repository url. As usual, be cautious when doing this in production, since supply chain attacks can occur when a private package name is suddenly resolvable to a package of the same name on the public repository.

pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple [PACKAGE]
  publish-pypi:
    name: Push wheels to PyPI
    needs: build-wheels
    runs-on: ubuntu-latest
    environment:
      name: release
      url: https://test.pypi.org/p/s15lib
    permissions:
      id-token: write
 
    steps:
      - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131  # v7.0.0
        with:
          pattern: cibw-*
          path: dist
          merge-multiple: true
 
      - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e  # v1.13.0
        with:
          repository-url: https://test.pypi.org/legacy/
          verbose: true

Build system

Different possible build backends:

Some articles on choosing a backend:

Manylinux

Building PyPI-compatible wheels with C extensions require building with manylinux (see PEP600), so that the wheels will have compatible ABI. This typically involves spinning up an old OS with a sufficiently dated glibc for building. There is the cibuildwheel package that scripts this, with Docker as a dependency to pull the relevant image bases.

Some references:

user:~$ uv pip install cibuildwheel
user:~$ python -m cibuildwheel --output-dir wheelhouse  # sudo for docker

Custom platform build can be specified in pyproject.toml:

[tool.cibuildwheel]
build = "cp38-manylinux_x86_64"
kb/lang/scripting/python/pypi.txt · Last modified: 11 days ago (23 December 2025) by justin