Packaging & Distribution

Python Advanced — Packaging & Distribution
Python Advanced
Course 3 · Chapter 6 · Packaging & Distribution

📦 Packaging & Distribution

Course 1, Chapter 9 covered consuming packages — pip install, venv. This chapter covers the other side: turning your own code into a real, installable, distributable Python package, from project structure through pyproject.toml to actually publishing on PyPI.

🗂️ Project Structure

my_package/ ├── pyproject.toml ├── README.md ├── src/ │ └── my_package/ │ ├── __init__.py │ └── core.py └── tests/ └── test_core.py

The src/ layout (package code inside a src/ folder, separate from tests and config) is the modern recommended structure — it prevents accidentally importing your package from the current working directory instead of the actually-installed version, a subtle bug the older "flat" layout could hide. __init__.py is what makes a plain folder importable as a package at all, a detail Course 1, Chapter 9's import coverage didn't need to mention since it only covered using existing packages.

⚙️ pyproject.toml

# pyproject.toml [build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "my_package" version = "0.1.0" description = "A small example package" dependencies = [ "requests>=2.31.0", ]

pyproject.toml is the modern, standardized project config file — it replaced the older setup.py approach, and plays the same role as Node's package.json or Rust's Cargo.toml. The [project] table declares your package's metadata and dependencies in the exact package==version style from Course 1, Chapter 9's own requirements.txt coverage, just declared alongside the package itself rather than in a separate file.

🔨 Build Tools: setuptools vs Poetry

setuptools vs Poetry

setuptoolsPoetry
RoleThe traditional, most widely used build backend — does one job, building the packageAn all-in-one tool: dependency management, virtual environments, building, and publishing together
Dependency lockingNo built-in lock file — needs a separate tool (like pip-tools) for reproducible installsBuilt-in poetry.lock, similar to package-lock.json or Cargo.lock
Best forSimple packages, or when you want minimal tooling and full controlLarger projects wanting one consistent tool for the whole dependency/build/publish workflow

Both read from pyproject.toml — they differ in scope, not in the config file format itself. setuptools is the safer default to learn first since it's what most existing open-source packages already use; Poetry is worth reaching for once a project's dependency management gets complex enough to want a lock file.

💻 Building and Installing Locally

# from inside the project's root folder, with a venv active (Course 1, Chapter 9): pip install -e .

pip install -e . is an editable install — it installs your package by linking directly to your source folder, rather than copying it. Changes to your code take effect immediately, with no reinstall needed, making it the standard way to develop and test a package locally before publishing it.

🚀 Publishing to PyPI

# build the distributable files (a wheel + a source distribution): python -m build # upload to TestPyPI first — a safe practice environment, separate from the real registry: twine upload --repository testpypi dist/* # once verified, upload to the real PyPI: twine upload dist/*

python -m build produces the actual distributable files in a dist/ folder; twine uploads them. TestPyPI is a full separate instance of the registry meant exactly for practice runs — publishing there costs nothing and can't be undone by mistake on the real thing.

⚠ Package Names on PyPI Are Global and Effectively Permanent

Once a package name is taken on PyPI, it's taken — there's no reserving a name in advance, and abandoned/typo'd releases are very rarely removed. Check pypi.org/project/<name> before committing to a name. Also double-check what actually gets included in your built package before publishing — a stray .env file or credentials accidentally bundled into a public release is effectively permanent too, since older versions typically stay downloadable even after a fix.

Package Publishing: Go vs Kotlin vs Python

LanguageApproach
GoNo central registry or "publish" step at all — a module is just a public Git repository; go get fetches directly from the repo URL and its tags.
KotlinMaven Central (or an equivalent) — a central registry model much like PyPI, with a similar build-then-upload workflow via Gradle/Maven.
PythonPyPI — a central registry, built and uploaded via pyproject.toml + build + twine.

Go's repository-is-the-package model is a genuinely different philosophy from the central-registry approach both Kotlin and Python share — no separate publishing step exists in Go at all, just tagging a commit in a public repo.

pyproject.toml

The modern standard config file — metadata, dependencies, and build backend in one place.

setuptools / Poetry

Two build-tool options — a minimal backend vs. an all-in-one workflow tool.

Editable install

pip install -e . — develop locally with changes taking effect immediately.

build + twine

python -m build creates distributables; twine upload publishes them to PyPI.

💻 Coding Challenges

Challenge 1: Write a Minimal pyproject.toml

Write a complete pyproject.toml for a package called text_utils, version 0.1.0, that depends on requests>=2.28.0, using setuptools as the build backend.

Goal: Practice writing the [build-system] and [project] tables correctly from scratch.

→ Solution

Challenge 2: Design a src/ Layout

Sketch out (as plain text, a folder tree — no code needed to run) the src/ layout for a package called calculator, containing two modules, basic.py and advanced.py, plus one test file for each.

Goal: Get comfortable with the standard src/ project structure this chapter recommends.

→ Solution

Challenge 3: Trace the Publishing Steps

List, in the correct order, every command-line step needed to go from a finished local package to it being live and installable from the real PyPI — including the safe practice step this chapter recommends before the real upload.

Goal: Solidify the full build-and-publish workflow as a sequence, not just isolated commands.

→ Solution

🎯 What's Next

Next chapter: Performance — profiling with cProfile, a Cython/PyPy overview, and common optimization patterns.