Skip to content

ADR-008: Packaging and Distribution

Status

Accepted Date: 2026-02-14

Context

cosalette is a reusable Python framework consumed by 8+ independent IoT-to-MQTT bridge projects, each in its own repository. The framework needs a packaging and distribution strategy that supports clean dependency management across multiple repos, works in CI/CD pipelines (GitHub Actions) and Docker builds, and follows modern Python packaging standards.

The project is open source (MIT license). The author wants to learn the PyPI publication workflow and is willing to maintain the package, though the primary audience is personal projects.

Decision

Use PyPI publication with the package name cosalette, hatchling as the build backend, setuptools-scm for version management, src layout, and a py.typed marker (PEP 561) for type stub support, because this is the cleanest, most standard distribution mechanism for Python packages.

Package structure

cosalette/
├── pyproject.toml
├── src/
│   └── cosalette/
│       ├── __init__.py
│       ├── py.typed          # PEP 561 marker
│       ├── _version.py       # Generated by setuptools-scm
│       └── ...

Build configuration

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

[tool.hatch.build.targets.wheel]
packages = ["src/cosalette"]

[tool.setuptools_scm]
version_scheme = "guess-next-dev"
write_to = "src/cosalette/_version.py"
fallback_version = "0.0.0"

Versioning strategy

  • cosalette uses independent semver versioning
  • Consumer projects pin with compatible ranges: cosalette>=0.5,<1.0
  • Deprecation cycles can be short (one minor version) since all consumers are maintained by the same author

Multi-repo layout

github.com/ff-fab/
├── cosalette/           # Framework repository
├── velux2mqtt/          # Depends on cosalette
├── gas2mqtt/            # Depends on cosalette
├── wiz2mqtt/            # Depends on cosalette
└── ...                  # 5 more project repos

Decision Drivers

  • pip install cosalette must work everywhere (CI, Docker, devcontainers)
  • Proper version resolution (pip/uv can resolve conflicts)
  • Independent semver for framework and projects
  • PEP 561 type stub support for type-checker compatibility
  • Modern Python packaging standards (PEP 517/518/621)
  • Professional practice and learning opportunity for PyPI publishing

Considered Options

Option 1: Poetry

Use Poetry for project management and build backend.

  • Advantages: All-in-one tool (dependency management + build + publish). Lock file for reproducible installs. Large community.
  • Disadvantages: Poetry's custom dependency resolver can conflict with pip/uv. Non-standard lock file format. The project already uses hatchling — switching adds no value. Poetry's pyproject.toml extensions are not PEP 621 compliant.

Option 2: Flit

Use Flit for simple, minimal packaging.

  • Advantages: Extremely simple for pure-Python packages. Minimal configuration.
  • Disadvantages: Does not support dynamic version management (setuptools-scm). Limited build customisation. Less flexibility for future needs (C extensions, data files).

Option 3: setuptools (legacy)

Use traditional setuptools with setup.py or setup.cfg.

  • Advantages: Most widely used build backend. Maximum compatibility.
  • Disadvantages: setup.py is legacy; setup.cfg is being superseded by pyproject.toml. Verbose configuration compared to hatchling. Does not support PEP 621 natively in older versions.

Option 4: hatchling with PyPI publication (chosen)

Use hatchling as the PEP 517 build backend with PyPI as the distribution channel.

  • Advantages: Modern PEP 517/518/621 compliant. Clean pyproject.toml configuration. Works with setuptools-scm for git-tag-based versioning. pip install cosalette works everywhere without git access. Hashes and integrity checks. Professional practice even for personal projects.
  • Disadvantages: Requires PyPI account setup and release automation. Public package on PyPI (acceptable for open source project).

Decision Matrix

Criterion Poetry Flit setuptools (Legacy) hatchling + PyPI
PEP compliance 3 4 3 5
Version management 4 2 3 5
Install simplicity 4 5 3 5
Build flexibility 3 2 5 4
Community adoption 5 3 4 4

Scale: 1 (poor) to 5 (excellent)

Consequences

Positive

  • pip install cosalette works in any environment — CI, Docker, devcontainers, bare metal
  • Version resolution by pip/uv handles dependency conflicts automatically
  • py.typed marker enables type-checking consumers to verify against cosalette's type annotations
  • setuptools-scm eliminates manual version management — git tags drive version numbers
  • src layout prevents accidental imports of the development source

Negative

  • PyPI publication requires release automation (GitHub Actions workflow) and account management
  • The package is public on PyPI — though this is acceptable for an open-source project
  • setuptools-scm requires git tags to be managed carefully — missing tags produce dev versions

2026-02-14