P35 · Packaging, Tooling & Automation
project layout, pyproject, builds & automation
Turn a script into a shippable package: declare it in pyproject.toml with a src/ layout, pin its dependencies for reproducible builds, produce the wheel a registry expects, and automate the whole dev loop so quality is built in - with this repo as the worked example throughout.
The gap between a script and something another person can install is smaller than it looks and full of decisions that only bite later. Where does the code live so that your tests exercise the installed package rather than the directory you happen to be standing in? What does a version range actually promise? Why does the build work today and fail in three weeks with no change to your code? This pillar closes that gap deliberately.
It starts with declaration. A real pyproject.toml, and the src/ layout —
which is not a style preference but the arrangement that makes it impossible to
accidentally import your uninstalled source during a test run, so you test what
you ship. An entry point then turns a library into a command a user can type,
which is where most internal tools should have started. Dependencies follow, and
they are worked by hand: evaluate SemVer constraints yourself to see what a
range admits, then prove that a lockfile is what makes a build reproducible
rather than merely repeatable, and read the wheel and sdist a registry actually
expects. Real upload stays prose, since the point is understanding the artefact.
The last track is automation, and it goes deeper than writing a Makefile. You build the task-graph engine underneath one — topological ordering, cycle detection, and a per-target plan — so the thing that runs your checks is not a mystery either. Then the checks are codified where they get run: pre-commit hooks for the fast ones, a CI gate for all of them, over a single source of truth so the command you run locally and the command CI runs cannot drift apart. This repository is the worked example throughout.