Python Packaging & Distribution
From a folder of scripts to a releasable artifact
Packaging decides how your AI service, eval harness or guardrail tool is built, distributed and versioned. A pyproject.toml with metadata, entry points and CI releases makes your code deployable and importable.
What you will learn
- Structure a project with pyproject.toml metadata
- Build and install a wheel in a clean environment
- Publish versioned releases with entry points for CLI tools
What is this?
Python packaging is the process of making your code installable — declaring what it needs, how it is built and what it exposes. A `pyproject.toml` describes name, version, dependencies and entry points. Building a wheel produces a file that reproducibly installs anywhere, which is how AI services, eval harnesses and guardrail libraries ship to production and to other teams.
Why do you need it?
- An importable package can be shared across a mono-repo service and its eval harness instead of copied.
- Entry points turn an internal helper into a `myevals run` CLI every engineer can call.
- Versioned wheels make rollbacks trivial — you reinstall the previous tag.
- CI-driven builds prevent the classic 'works from my checkout' problem.
Mental model
A manufacturer's shipping crate
A built wheel
The factory doesn't ship loose parts and instructions; it packs a crate with a barcode and inventory label. A wheel is the same idea: a self-contained, versioned, universally installable crate for a Python service.
How it works
- `pyproject.toml` declares project metadata: name, version, dependencies, entry points.
- A build backend (hatchling, setuptools, poetry-core) reads it and assembles a wheel.
- The wheel bundles compiled metadata and code so `pip install dist/*.whl` works in any venv.
- Entry points expose console scripts: `[project.scripts] myeval = 'ai.evals.cli:main'`.
- A CI job builds, tests and, on tag, uploads to a package index.
The packaging pipeline.
In production
- An eval harness ships as a versioned package so any engineer can `pip install evaltool==2.3.1` and run the same golden test suite.
- A guardrail library exposes a `guardrail-serve` console script from its package entry point.
- Teams tag v1.4.0, CI builds and uploads the wheel, and prod pins exactly that version for audits.
Best practices
- Keep package name, version and dependencies in pyproject.toml — drop setup.py and requirements.txt for it.
- Add entry points so tools are invocable, not just importable.
- Build in CI with `python -m build` and test-install the artifact before publishing.
- Use semantic versioning and publish only from tagged commits.
Common mistakes
- Version in pyproject.toml and setup.py drifting out of sync.
- Shipping a wheel built on one platform with C extensions undeclared as platform-tagged dependencies.
- Forgetting `__init__.py` (or explicit namespace packages), so imports silently miss.
- Publishing without running the eval/CLI from the installed wheel, not the source checkout.
Common issues
Entry point script runs but can't find its data filesPackage data wasn't declared in the build config
Declare include-package-data and site-packages data globs in pyproject.toml, then rebuild.
Import works locally but fails from the wheelEditable install masked a missing package in the built artifact
Install the wheel into a fresh venv and run the entry point before shipping.
pip installs a very old versionIndex cache or an unpinned dependency range resolved older
Pin the exact version in the consumer and clear caches during audits.
Debugging tips
- If an installed tool won't import, install in editable mode and compare `pip list` paths.
- Check `pip show <pkg>` to confirm the version prod actually resolved.
- A broken CLI usually means the entry point module path or function name is wrong; run `python -c 'import <entry>; <entry>.main()'` to isolate.
- Wheels with missing data files need `include`/package-data declarations, not luck.
Interview checkpoint
Quick quiz
What is a wheel?
Entry points only matter for command-line tools.