Python
Intermediate 13 min read Python

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.

pyproject.tomlpackagingentry pointswheeldistributionversioning
Prerequisites: Python Environments
Before we start

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
Introduction

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.

Motivation

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.
Think about it

Mental model

Imagine

A manufacturer's shipping crate

Think of

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.

Mechanics

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.

pyproject.toml
Build
Test install
Publish
Consumers
Real world

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.
Guidelines

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.
Watch out

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.
Root cause analysis

Common issues

Error
Entry point script runs but can't find its data files
Root cause

Package data wasn't declared in the build config

Fix

Declare include-package-data and site-packages data globs in pyproject.toml, then rebuild.

Error
Import works locally but fails from the wheel
Root cause

Editable install masked a missing package in the built artifact

Fix

Install the wheel into a fresh venv and run the entry point before shipping.

Error
pip installs a very old version
Root cause

Index cache or an unpinned dependency range resolved older

Fix

Pin the exact version in the consumer and clear caches during audits.

When things break

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.
Interviews

Interview checkpoint

Test yourself

Quick quiz

0/2
1

What is a wheel?

2

Entry points only matter for command-line tools.

Go deeper

Official documentation