Warming up the neural circuits...
By the end of this chapter you will:
Unpackaged code is difficult to reuse, test across environments, and distribute to teams. Packaging turns local scripts into installable products with versioned release discipline.
If users cannot install your tool reliably, they cannot depend on it.
mytool/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│ └── mytool/
│ ├── __init__.py
│ └── __main__.py
└── tests/The src/ layout prevents accidental imports from the repo root during local development.
[project]
name = "mytool"
version = "0.1.0"
description = "My first installable CLI"
requires-python = ">=3.11"
[project.scripts]
mytool = "mytool.__main__:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"This enables both python -m mytool and installed CLI script execution.
python -m pip install --upgrade build
python -m buildArtifacts in dist/:
.whl: built distribution for install.tar.gz: source distributionInspect artifacts before publishing.
python -m pip install --upgrade twine
twine check dist/*
twine upload --repository testpypi dist/*After on TestPyPI, publish to PyPI using trusted CI publishing or secure token workflows.
Recommended flow:
MAJOR.MINOR.PATCH)Release engineering is part of product quality.
Always publish to TestPyPI first for every significant packaging change. It catches metadata and install failures before they hit users.
| Mistake | Symptom | Fix |
|---|---|---|
Missing build-system section | Build fails immediately | Add valid backend and requirements |
| Hardcoding version in multiple places | Drift and inconsistent artifacts | Single-source version strategy |
| Publishing without artifact checks | Broken install for users | Run twine check and TestPyPI rehearsal |
| Importing from project root in tests | False-local success | Use src/ layout and install package in test env |
| No entrypoint script mapping | CLI not discoverable | Define [project.scripts] |
tool name: datacleanValid package structureentry function dataclean.cli:maindataclean command available after installWorking package projectwheel and sdist created and checkedVersion bump to 0.2.0Repeatable publish workflowNeed simple config, fast buildsReasoned backend choiceBeginner:
"Why use pyproject.toml?"
It standardizes package metadata, build configuration, and tooling configuration in one modern file.
"What is the difference between wheel and sdist?"
Wheel is a built install artifact; sdist is source package that can be built during installation.
Senior:
"How do you prevent broken releases?"
Enforce CI quality gates, artifact validation, TestPyPI rehearsal, and reproducible release runbooks.
"When would you keep setuptools over newer backends?"
When existing plugin ecosystems or advanced packaging hooks depend on setuptools capabilities and migration cost is high.
pyproject.toml is the modern center of package metadata.Need installable tool? -> package with pyproject
Need release artifact? -> python -m build
Need safe publish? -> TestPyPI first
Need repeatability? -> checklist + CI gatesWhat does python -m build produce?