Packaging, Wheels, pyproject.toml & PyPI Publishing
Packaging Python libraries and services requires building distribution artifacts compliant with modern PEP standards (PEP 517, PEP 518, PEP 621). Understanding the difference between Source Distributions (sdist) and Built Distributions (Wheels / .whl), build backend compilation (hatchling, setuptools), and secure PyPI publishing via trusted publishing workflows is essential for library authors.
This chapter details pyproject.toml build backend configurations, sdist vs wheel binary formats, C-extension cibuildwheel compilation, and uv publish / twine publishing.
1. Distribution Artifacts: sdist vs. whl (Wheel)
Building a Python package (python -m build or uv build) outputs two distinct distribution artifacts in dist/:
Distribution Artifacts Comparison:
1. SOURCE DISTRIBUTION (sdist - .tar.gz):
Contains raw un-compiled source code (.py files, C source, pyproject.toml).
- Cons: User MUST compile C-extensions at install time! Requires C compiler on target machine!
2. BUILT DISTRIBUTION (Wheel - .whl):
Pre-compiled, ready-to-extract zip archive containing python files & compiled binary shared objects (.so / .pyd).
- Pros: Zero compilation on target machine! Installs instantly via simple file extraction!Wheel Naming Convention:
package-1.0.0-py3-none-any.whl (Pure Python package) vs package-1.0.0-cp311-cp311-manylinux_2_17_x86_64.whl (Pre-compiled C-extension wheel for 64-bit Linux).
2. Declarative Packaging Setup (pyproject.toml)
Modern Python packaging uses PEP 518 [build-system] and PEP 621 [project] metadata:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "enterprise-logger"
version = "1.0.0"
description = "High-performance structured logging library"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
authors = [{ name = "Alice Developer", email = "alice@example.com" }]
dependencies = [
"structlog>=24.1.0",
"orjson>=3.9.0",
]
[project.scripts]
# Installs CLI entrypoint binary!
logger-cli = "enterprise_logger.cli:main"3. C-Extension Cross-Platform Wheel Compilation (cibuildwheel)
If your package contains C/Rust extensions, compiling pre-built Wheel binaries for Linux, macOS (x86 & ARM64), and Windows requires cibuildwheel:
# GitHub Actions cibuildwheel workflow
jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: pypa/cibuildwheel@v2.17.0
- uses: actions/upload-artifact@v4
with:
path: ./wheelhouse/*.whl4. Secure PyPI Publishing with Trusted Publishers
Never store static API tokens (pypi-AgEI...) inside CI/CD secrets.
PyPI supports OpenID Connect (OIDC) Trusted Publishing:
- GitHub Actions authenticates directly with PyPI using short-lived OIDC JWT tokens.
- Eliminates hardcoded PyPI passwords and secret rotation hazards!
# Secure OIDC PyPI Publishing Step via uv
- uses: astral-sh/setup-uv@v1
- name: Publish package to PyPI
run: uv publish
env:
# Uses GitHub OIDC ID-Token automatically!
UV_PUBLISH_TOKEN: ${{ steps.auth.outputs.oidc_token }}