Development
===========

Requirements and tooling
------------------------

The packaging metadata, and the test / tool dependency groups, live in
``pyproject.toml``.  `uv <https://uv.astral.sh>`_ manages the development
environment and its lockfile (``uv.lock``):

.. code-block:: console

   $ 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 <https://github.com/tseaver/pkginfo/releases>`_, which is
the canonical changelog;  release ``1.13.3`` was the last tracked in the
repo's own ``CHANGES.txt`` file.