Packaging and Publishing
Turn a folder of modules into an installable, versioned, shareable package on PyPI
pyproject.toml. From it a build backend produces two artifacts: a source distribution (sdist) and a built wheel. You upload those to the Python Package Index (PyPI) with twine, and anyone can then run pip install your-package. Nothing here is magic — it is metadata, a build step, and an upload.
- lay out a project with the src layout and understand why it prevents a whole class of bugs,
- write a complete
pyproject.toml:[build-system],[project]metadata, dependencies, optional extras and console entry points, - create and use an isolated virtual environment for development,
- build an
sdistand awheelwithpython -m buildand explain the difference, - choose a version using semantic versioning and keep a single source of truth,
- publish to TestPyPI first and then PyPI with an API token and
twine.
1. The problem packaging solves
You already know how to split code into modules and packages (folders with __init__.py) and import them. That works on your machine because the current directory is on sys.path. The moment someone else needs your code, informal sharing breaks down: which files are needed, what third-party libraries must be installed first, what Python version is required, and how does import yourlib resolve on their machine?
Packaging answers all of that declaratively. You describe the project once, a build tool produces a standard artifact, and pip handles installation into any environment — copying files to site-packages and pulling in declared dependencies. The reader never guesses; the metadata is authoritative.
Historically this metadata lived in an executable setup.py script. Running arbitrary code just to read a project's name was fragile and insecure, so the ecosystem moved to a declarative standard. PEP 518 introduced pyproject.toml and PEP 621 standardised the [project] table. Today pyproject.toml is the one file you need; setup.py and setup.cfg are legacy.