Packaging Basics
9 examples to get you started with Packaging and Publishing - 6 basic and 3 intermediate.
Search across all documentation pages
9 examples to get you started with Packaging and Publishing - 6 basic and 3 intermediate.
uv 0.6+ or pippyproject.toml and src/ layoutA wheel (.whl) is the standard binary distribution format.
uv build
ls dist/
# myapp-0.1.0-py3-none-any.whl
# myapp-0.1.0.tar.gzuv build creates wheel and sdist in dist/py3-none-any means pure Python, any platformdist/ - build in CIRelated: Build Backends - hatchling vs setuptools
Package metadata lives in [project].
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = ["httpx>=0.28"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"name is the PyPI package name (lowercase, hyphens)version follows SemVer[build-system] declares the build backendmy_app vs my-app)Related: pyproject.toml Explained
Keep source in src/ for reliable packaging and testing.
myapp/
├── pyproject.toml
├── src/myapp/
│ ├── __init__.py
│ └── core.py
└── tests/
src/ prevents accidental imports from repo rootTest your package before publishing.
uv build
uv pip install dist/myapp-0.1.0-py3-none-any.whl
python -c "import myapp; print(myapp.__version__)"pip install . (sdist path)Expose version in __init__.py or use dynamic versioning.
[project]
version = "0.1.0"# src/myapp/__init__.py
__version__ = "0.1.0"pyproject.toml preferred0.2.0b1Related: Versioning & Changelogs
Ship a CLI command with your package.
[project.scripts]
myapp = "myapp.cli:main"# src/myapp/cli.py
def main() -> None:
print("Hello from myapp CLI").venv/bin/myapp on pip installproject.scripts in pyproject.tomluv pip install -e . then myappRelated: Entry Points & Console Scripts
Dry-run publishing before production PyPI.
uv build
uv publish --index testpypi
# Test install:
uv pip install --index-url https://test.pypi.org/simple/ myappRelated: Publishing to PyPI
Ship non-Python files (templates, config defaults).
[tool.hatch.build.targets.wheel]
packages = ["src/myapp"]
[tool.hatch.build.targets.wheel.force-include]
"src/myapp/templates" = "myapp/templates"force-include maps source to install pathimportlib.resources to access at runtimepip install and inspect site-packagesAutomate wheel building on every release tag.
- run: uv build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/Related: Packaging Best Practices
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026