# Python Packaging 101 tutorial Welcome! This is a hands-on introduction to packaging Python projects using modern tooling. ## Learning outcomes By the end of this tutorial, you will: - Understand the structure of a modern Python package. - Build source and wheel distributions with `python -m build`. - Upload to TestPyPI using twine and an API token. - Install and verify your package from TestPyPI. ## Setup and requirements - Computer connected to the Internet - Access to your personal GitHub account  - Python 3.9+, `pip`, `venv` or `virtualenv` installed - Access to your personal [TestPyPI](https://test.pypi.org) account (optional, we will have time to do it in class) ## Prerequisites To get the most out of this tutorial, you should be comfortable with: - Writing basic Python code  - Using virtual environments  - Using terminal # Overview ## Why package? - Make your code reusable and shareable. - Control versions and dependencies. - Enable reproducible installs. # 1. Structure of an Installable Module [acpackage](https://github.com/albuscode/acpackage) is a demo package we will use for this tutorial. Let's look at the file directory: ``` acpackage/ src/ acpackage/ __init__.py example.py docs/ tests/ pyproject.toml README.md LICENSE .gitignore ``` A file directory is a hierarchical structure, like a physical filing cabinet, that organizes files into folders. `.gitignore`, `docs/`, `tests/` are optional, but highly recommended **src/acpackage/example.py** This module contains a function that adds two integers and returns the result. It includes a detailed docstring explaining parameters and examples. ```python def add_num(a: int, b: int) -> int: """ Add two numbers. Parameters ---------- a : int The first number to be added. b : int The second number to be added. Returns ------- int The sum of the two input numbers (a + b). Examples -------- >>> add_num(2, 4) 6 >>> add_num(-2, 4) 2 """ return a + b ``` **src/acpackage/`__init__.py`** `__init__.py` acts as the "constructor" for a Python package, enabling its proper recognition, initialization, and controlled exposure of its contents. ```python from .example import add_num __all__ = ["add_num"] ``` # 2. Anatomy of `pyproject.toml` Every modern Python package should include a pyproject.toml file. This file is the foundation and primary source of truth for package configuration. If it’s correct, your package will install, build, and work as expected with modern tools. - You **must** include `[project]`, `[build-system]`, and the `name`, `version`, and `build-backend`. - It's **highly recommended** to include `requires-python`. - pyproject.toml is not just for builds, it’s a central configuration file for tools and environments. ```toml [build-system] requires = ["uv_build >= 0.8.20, <0.9.0"] build-backend = "uv_build" [project] name = "acpackage" description = "A package for performing basic addition" readme = "README.md" version = "0.0.1" requires-python = ">=3.10" license = { file = "LICENSE" } keywords = [] authors = [ { name = "Inessa Pawson", email = "inessa@albuscode.org" }, ] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python :: 3", "Operating System :: OS Independent" ] dependencies = [] [project.urls] Homepage = "https://github.com/albuscode/acpackage" Documentation = "" Issues = "https://github.com/albuscode/acpackage/issues" Source = "https://github.com/albuscode/acpackage" ``` # 3. Build & Install Locally ### Download `acpackage` as .zip file: https://github.com/albuscode/acpackage/archive/refs/heads/main.zip *Note:* Package names on Test PyPI must be unique. Rename the downloaded package to `acpackageYOURNAME`. ### Move to the package directory ```bash cd acpackageYOURNAME ``` ### Install build tools: ```bash python -m pip install --upgrade build ``` ### Build distributions: ```bash python -m build ls -lah dist ``` This produces both a source archive (*.tar.gz) and a wheel (*.whl) in dist/. ### Install the wheel to test: ```bash pip install dist/acpackageYOURNAME-0.0.1-py3-none-any.whl python -c "import acpackageYOURNAME; print(acpackageYOURNAME.add_num(2, 3))" ``` ### Try **editable installs** (PEP 660): ```bash pip install -e . ``` Editable mode is useful during development as you can change code without reinstalling. Not always supported by all build backends, but `uv_build` supports it. # 4. Upload to TestPyPI Create an account and API token at [TestPyPI](https://test.pypi.org/) , then upload with Twine. ```bash pip install --upgrade twine twine upload --repository testpypi dist/* ``` # 5. Install from TestPyPI & Verify ```bash pip install --index-url https://test.pypi.org/simple/ \ --extra-index-url https://pypi.org/simple \ acpackage-YOURNAME python -c "import acpackageYOURNAME; print(acpackageYOURNAME.add_num(4, 5))" ``` If you exposed a console script: ```bash acpackage-add 7 8 ``` # 6. Metadata, Dependencies, & Extras ```toml [project] dependencies = ["simplejson>=3.19"] [project.optional-dependencies] dev = ["pytest", "ruff"] cli = ["click>=8"] ``` Example CLI using Click: ```python import sys try: import click except Exception: print("Install optional extra: pip install 'acpackage-yourname[cli]' ") raise @click.command() @click.argument("a", type=int) @click.argument("b", type=int) def main(a, b): from acpackage import add print(add(a, b)) if __name__ == "__main__": main() ``` # 7. Advanced Topics ## Namespace packages - Implicit namespace packages (PEP 420). ## Binary extensions - C/C++/Cython → platform wheels. ## Plugin entry points - Use `project.entry-points` for discoverable plugins. <!-- 08-workflows.md --> # 8. Workflows & Best Practices Example GitHub Actions release workflow: ```yaml name: Release on: push: tags: - 'v*' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.x' - run: python -m pip install --upgrade pip build twine - run: python -m build - run: twine check dist/* - env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: twine upload dist/* ``` # 9. Resources - Packaging Python Projects by PyPA: packaging.python.org - Python Packaging 101 by PyOpenSci: https://www.pyopensci.org/python-package-guide/tutorials/intro.html