Packaging Code

November 30 · Give the receiver an installable command

MaDS Databases & SQL

One question will organize today

Can a clean environment install and run the inherited workflow without changing sys.path or guessing which file starts it?

Course source and adaptation

Today’s sequence follows Alex Reinhart’s Packaging Code, updated to a pyproject.toml and src/ layout following current Python Packaging Authority guidance.

Current reference: PyPA packaging projects and command-line tools.

By the end of class

You should be able to:

  • separate reusable modules from entry points,
  • explain imports without sys.path tricks,
  • create a minimal pyproject.toml,
  • expose, build, and install a command,
  • test the artifact in a clean environment.

A folder of scripts has hidden contracts

Questions a receiver should not have to ask:

  • Which file runs first?
  • From which directory?
  • Which dependencies and versions?
  • Which settings are required?
  • What is safe to import?
  • What output proves success?

Packaging makes these contracts explicit.

Modules are importable units

# src/client_pipeline/validation.py
def require_nonempty(rows: list[tuple]) -> None:
    if not rows:
        raise ValueError("No rows received")

Keep useful logic out of top-level side effects.

Guard direct execution

def main() -> int:
    ...
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Importing the module should not refresh production data.

Checkpoint 1 · Untangle one script

Take one inherited script and mark configuration, I/O, transformation, validation, and CLI behavior. Propose two or three modules and one entry point.

Use imports, not working-directory luck

Inside the package:

from .config import Settings
from .db import connect
from .validation import require_nonempty

Do not append project paths to sys.path in production code.

pyproject.toml names the build contract

[build-system]
requires = ["hatchling>=1.26"]
build-backend = "hatchling.build"

[project]
name = "client-pipeline"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["psycopg[binary]>=3.2,<4"]

Commit dependency metadata—not a virtual environment.

Expose a command

[project.scripts]
client-pipeline = "client_pipeline.cli:main"

After installation:

client-pipeline --help

The command is the receiver’s stable entry point.

Configuration stays outside the package

package: validates setting names and types
environment: supplies deployment-specific values
runbook: documents how the receiver obtains them

Do not package .env, credentials, raw client data, or local caches.

Checkpoint 2 · Write the minimal metadata

Create a project name, supported Python version, runtime dependencies, and CLI entry point for the inherited workflow.

Run the metadata through one teammate: can they identify the command and configuration without opening the source?

Build artifacts are installable evidence

python -m build

Produces a source distribution and wheel in dist/.

The wheel is the installable artifact; the repository remains the collaboration source.

Clean-environment test

python -m venv .venv-test
source .venv-test/bin/activate
python -m pip install dist/*.whl
client-pipeline --help

Then run the smallest-success command with test-safe configuration.

Editable installs are for development

python -m pip install -e .

Useful while editing, but not proof that the built wheel contains everything required.

Tests should import the installed package

At minimum:

  • configuration validation,
  • pure transformation/validation function,
  • CLI --help and invalid input,
  • smallest database query against a test schema,
  • clean failure with missing DATABASE_URL.

Checkpoint 3 · Break the clean install

Build and install the inherited package in a clean environment. Record the first failure, classify its hidden dependency, fix it, and repeat.

Version behavior, not filenames

A receiver needs to know:

  • artifact version,
  • schema compatibility,
  • source/API assumptions,
  • behavior changes,
  • rollback artifact.

final_v2_really_final.py is not release management.

Project transfer

Package one inherited refresh or report path and add the wheel-install test to the receiver evidence pack.

Small and reliable beats packaging every experiment.

Homework starts here

Homework 3 may use packaging as its tested extension. Link to pyproject.toml, source layout, built artifact, clean-install log, and smallest-success output.

The pattern to keep

separate reusable logic
  → define the package boundary
  → declare build and dependencies
  → expose one stable command
  → build a wheel
  → install it clean
  → test smallest success and failure

Next: run the receiver acceptance test and shape the final client readout.