Dependencies¶
Ownership¶
Four native pip-compile inputs own the exact direct tool dependencies.
Root pyproject.toml owns empty runtime metadata and the isolated resolver
bootstrap pins:
| Audience | Direct input | Generated lock | Purpose |
|---|---|---|---|
| Quality | requirements-dev.in |
requirements-dev.txt |
Ruff, mypy, Bandit, and dependency audit |
| Tests | requirements-test.in |
requirements-test.txt |
pytest, asyncio support, and branch coverage |
| Packaging | requirements-package.in |
requirements-package.txt |
standard wheel and sdist construction only |
| Documentation | requirements-docs.in |
requirements-docs.txt |
strict MkDocs site build |
Every lock is generated by pip-compile, carries exact versions and SHA-256
hashes, and installs with wheel-only hash enforcement followed by pip check.
Direct dependencies do not overlap between audiences. Installed PEP 561 smoke
uses mypy from the quality environment; the packaging environment therefore
contains no type checker or standalone executable builder.
The maintainer audiences are not published as package extras. There is no
runtime input or lock because the library has no runtime dependencies.
The build-system setuptools range describes backend capability; the exact
backend used by project builds is owned by requirements-package.in.
CI's distribution job combines only the packaging tools needed to construct
archives with the quality audience's mypy for an installed-package typing check; publication
downloads the already gated archives and installs no build tools.
There is no third-party product-license inventory because the runtime dependency set is empty and none of the maintainer tool audiences is redistributed in the wheel or sdist. Distribution verification instead checks that the project's MIT license is present and exact. Adding any runtime or redistributed third-party dependency also requires adding an owned license inventory before release.
The test-only SSH server image is separate from Python tool audiences and is
never published or included in the wheel or sdist. Its owning
tests_acceptance/server/Containerfile pins the base image by digest and Debian
packages through a dated signed archive snapshot. Update those pins deliberately
and run make test-acceptance and make ci; do not install or version-match an
SSH server on the client host. make acceptance-image rebuilds from that narrow
context and keeps its image ID under the selected machine's environment root.
This includes the image-owned tini init process; engine injection of a host
catatonit or docker-init binary is disabled, not added as a host prerequisite.
For live tests, Podman runs rootless with its host pasta helper from the passt
OS package, using a direct per-container network rather than a shared bridge.
Docker remains supported through its native network stack. This host engine
prerequisite adds no dependency to the library or its Python locks.
Reviewed update loop¶
- Change exact direct pins in the appropriate
requirements-*.infile. Update overlapping resolver bootstrap pins inpyproject.tomlin the same change. - Run
make lock, ormake refresh-dependenciesfor a deliberate graph-wide refresh. - Review every transitive pin and hash change.
- Run
make freeze-check,make lock-platform-check,make dependency-snapshot, andmake audit. - Review
make audit-rawandmake outdated.
Generated lock files are not edited manually. The isolated resolver bootstrap
is exactly pinned in the standard [dependency-groups].resolver-bootstrap
group, which is excluded from built package metadata. The Makefile asks the
dependency-policy helper for the complete closure instead of duplicating it,
installs that closure with --no-deps, and then runs pip check. Every
bootstrap package that also occurs in an audience lock has the same version.
Bootstrap export validates the source pins without consulting existing locks;
otherwise an overlapping update such as build would prevent creating the
resolver needed to regenerate those locks. Both lock-generation Make targets
finish by validating the complete new graph, including bootstrap overlaps.
Do not bypass Make, temporarily revert pins, or hand-edit locks to perform an update.
The current resolver pair, pip==26.2.1 and pip-tools==7.6.1, matches
Dependabot's Python helper pins reviewed on 2026-09-08 at
dependabot/dependabot-core revision bddccfc0419e89a6ca0d33f80a1fb0b2882ef467.
Future resolver migrations require reviewing that pair and reproducing all four locks.
Freeze checking seeds temporary outputs with the committed locks so available
transitive updates do not turn validation into a graph refresh. It compares
regenerated content while excluding only pip-compile's interpreter-specific
header.
Dependabot discovers each .in/.txt pair as a native pip-compile manifest and
groups Python updates into one PR. pyproject.toml is excluded from its pip
manifests so the plain requirements updater cannot own generated locks. The
isolated resolver group remains manually reviewed, validated, and audited.
The audit covers all four locks and the exact resolver bootstrap. Findings are
accepted only when they exactly equal
.github/dependency-audit-exceptions.json; both new findings and stale
exceptions fail. Dependency submission derives all four persistent manifests
offline from validated locks; the transient resolver group is validated and
audited but is not submitted as a fifth lock. The
dependency contract contains the evidence
mapping.