Packaging for Distribution¶
Once your code is organized and tested, the next step is packaging it so others can install it via pip. In 2026, we have moved away from legacy files like setup.py and setup.cfg in favor of the standardized pyproject.toml.
1. The Anatomy of pyproject.toml¶
The pyproject.toml file contains everything a build tool needs to know about your project. It is divided into sections (tables).
View Example pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my_project"
version = "0.1.0"
description = "A short description of my project"
readme = "README.md"
requires-python = ">=3.9"
license = {text = "MIT"}
authors = [
{name = "Ravi", email = "ravi@example.com"},
]
dependencies = [
"requests>=2.31.0",
"pydantic>=2.0.0",
]
[project.urls]
Homepage = "[https://github.com/user/my_project](https://github.com/user/my_project)"
Documentation = "[https://user.github.io/my_project](https://user.github.io/my_project)"
[project.scripts]
my-cli = "my_project.main:app"
2. Choosing a Build Backend¶
The [build-system] section tells the installer which tool to use to package your code.
| Backend | Best For |
|---|---|
| Hatchling | High-performance, feature-rich, and works great with the src layout. |
| Flit | Minimalist; best for simple libraries that don't need complex build steps. |
| Setuptools | The legacy standard; use only if you have custom C extensions or complex requirements. |
| PDM/Poetry | All-in-one dependency managers that have their own backends. |
3. Building the Distribution¶
To package your repo, you need to generate two files:
- Source Distribution (sdist): A
.tar.gzarchive containing your source code. - Built Distribution (wheel): A
.whlfile that is pre-compiled and faster to install.
Using the build tool¶
The standard, backend-agnostic way to build is using the build package:
This will create a dist/ directory in your root:
4. Publishing to PyPI¶
To make your package available via pip install my_project, you must upload these artifacts to the Python Package Index (PyPI).
Step A: Create an Account¶
Register at PyPI.org and enable 2FA. Generate an API Token for secure uploads.
Step B: Upload with Twine¶
twine is the standard tool for securely uploading packages.
python -m pip install twine
# Upload to TestPyPI first (to check for errors)
python -m twine upload --repository testpypi dist/*
# Upload to the real PyPI
python -m twine upload dist/*
5. Automation with CI/CD¶
You shouldn't have to run these commands manually every time. Most modern projects use GitHub Actions to automate the release process whenever a new tag is pushed.
View GitHub Release Workflow (.yml)
name: Publish to PyPI
on:
release:
types: [published]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
- name: Install dependencies
run: pip install build twine
- name: Build and Publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python -m build
twine upload dist/*