Development

Requirements and tooling

The packaging metadata, and the test / tool dependency groups, live in pyproject.toml. uv manages the development environment and its lockfile (uv.lock):

$ uv sync --all-groups

The local gates are driven by tox, configured natively inside pyproject.toml ([tool.tox]), using uv’s venv runner (tox-uv).

Dependency groups

The [dependency-groups] section (PEP 735) of pyproject.toml defines the following groups:

test

pytest, pytest-cov, coverage, wheel: everything needed to run the test suite, including the wheel package, which is used by tests as the subject of installed-wheel introspection.

dev

tox, tox-uv, uv: the tools which drive the gates.

typechecking

mypy, types-setuptools: type-checking the sources and verifying that the in-tree *.pyi stubs do not drift.

docs

sphinx.

lint

ruff, check-manifest, breezy, build, twine: source linting, sdist manifest checking, and release tooling.

The project itself has no runtime dependencies, and none of these groups affect the published distribution metadata: they are development-only concerns, per PEP 735.

Gates

The following gates are runnable via uv run tox -e <env> (or simply tox -e <env>):

py311 … py315, pypy312

The test suite against each supported interpreter, recording coverage data.

cover

Aggregated cross-interpreter coverage report, gated at 100%.

mypy

Static type checking, plus stubtest against each module’s stubs (on Python 3.12; see the note about newer-interpreter dunders in the config).

docs

Building the HTML documentation, and running the narrative doctests.

manifest

Checking that the sdist contents match the version control state: the test fixtures and type stubs must ship inside the sdist, while development files (e.g., uv.lock) must not.

The same checks run in the GitHub Actions workflow (.github/workflows/ci.yml) without involving tox, as an independent check of the toolchain.

Test layout

The test suite lives at tests/unit, outside the pkginfo package: test modules import the APIs under test inside test functions and helpers (see tests/unit/_helpers.py for the shared checkers). Test fixtures which emulate installed distributions (e.g., dodgy, manky, wonky, tricky) are package trees placed on sys.path by the fixtures in tests/unit/conftest.py; their metadata files are hand-maintained data, checked into version control, not generated by builds.

Release process

Releases are tagged with signed tags: the body of the tag carries the release’s changelog entries, rendered as the release notes via the GitHub releases page. For example:

$ git tag -s -F <changelog-body.txt> v1.14.0
$ git push origin v1.14.0

The sdist and wheel are built, checked, and published using the tools in the lint group:

$ uv run build
$ uv run twine check dist/*
$ uv run twine upload dist/*

Creating and publishing the release itself (e.g., via gh release create) is what actually publishes to PyPI: the release workflow (.github/workflows/pypi.yml) reacts to the release published event, verifying that the release’s tag matches the version declared in pyproject.toml, building the sdist and wheel, and publishing them via PyPI’s Trusted Publisher mechanism.

The Changelog project URL points at the releases page, which is the canonical changelog; release 1.13.3 was the last tracked in the repo’s own CHANGES.txt file.