Contributing
This page describes how to set up a local development environment, run tests, and understand the CI gates for Typus.
Environment
- Python 3.10+ (CI runs on 3.10, 3.11, 3.12)
- Use
uvand Makefile shortcuts (no plainpipin scripts/CI): - Preferred bootstrap path for contributors:
./dev/scripts/bootstrap-dev.sh # ensures uv + Python 3.10, syncs the full dev/docs .venv, installs hooks
- Lower-level env sync targets remain available when you only need to refresh part of the setup:
make dev-setup # creates .venv (py310) and installs -e .[dev,sqlite,loader]
make dev-install # installs pre-commit hooks
Formatting & Lint
make format # uv run ruff format .
make lint # uv run ruff check --fix .
make lint-check # uv run ruff format --check . && uv run ruff check .
Type checking
make typecheck # uv run ty check (warnings fail)
Docs
make docs # creates a py310 .venv if missing, ensures docs extras, then builds the MkDocs site
Canonical local quality gate
make check-all # lint-check + typecheck + docs + schemas-check + ci
Tests
Lightweight SQLite-backed tests that match the CI gate:
make ci
Broader default local suite (SQLite + tests that do not require optional Postgres access):
make test
Postgres-backed tests (optional, requires a running DB):
export TYPUS_TEST_DSN=postgresql+asyncpg://<user>:<pass>@<host>:5432/ibrida-v0
export POSTGRES_DSN=$TYPUS_TEST_DSN
export ELEVATION_DSN=$TYPUS_TEST_DSN
export ELEVATION_TABLE=elevation_raster
# Smoke-check DSN / DB / required table before optional PG tests
make test-pg-smoke
# Optional Postgres marker subset (dataset-dependent)
make test-pg
# Elevation smoke tests (guarded; requires raster table)
TYPUS_ELEVATION_TEST=1 uv run pytest -q -k elevation_service
Important: make test and CI exclude pg_optional tests by marker. Run
make test-pg-smoke + make test-pg explicitly when you want live Postgres
coverage. Some tests assert counts that are specific to the SQLite sample
fixture and will not match the full production dataset. The recommended
workflow is:
- Run
make ci/make testto exercise the SQLite fixture path. - Run
make test-pg-smokeand thenmake test-pgfor Postgres-specific coverage.
Notes:
- Ensure the Postgres database contains the required
expanded_taxatable with the recommended indexes. You can bootstrap indexes with:
uv run typus-pg-ensure-indexes
Pre-commit
make dev-install
Hooks run on commit and in CI:
- ruff (lint)
- ruff-format (check-only in CI)
- whitespace/yaml fixers
- pytest-lite (fast subset; mirrors make ci test selection)
JSON Schemas
If you add or change public Pydantic models, append the dotted path to
typus/export_schemas.py and regenerate schemas:
uv run python -m typus.export_schemas
Generated JSON files live under typus/schemas/ and are committed to the repo.
CI and release workflows enforce schema freshness with make schemas-check.
Perf Harness (optional)
make perf
Writes a report to dev/agents/perf_report.md. You can enable TYPUS_PERF_VERIFY=1 for strict checks,
and TYPUS_PERF_EXPLAIN=1 to append EXPLAIN snippets for Postgres.
Service Backends
- SQLite: for offline/dev; loader creates indexes by default.
- Postgres: for production or full datasets;
typus-pg-ensure-indexesadds recommended indexes. - Elevation: Postgres-only; use
ELEVATION_DSNandELEVATION_TABLE(defaultelevation_raster).
CI/CD Overview
- CI workflow (
.github/workflows/ci.yml): - Matrix on Python 3.10/3.11/3.12.
- Syncs dependencies from
uv.lockwithuv sync --locked, then runsmake check-all. make check-allcoversmake lint-check,make typecheck,make docs,make schemas-check, andmake ci.- Triggers on PRs and pushes to
main(to avoid duplicate branch + PR runs for the same commit). - Publish workflow (
.github/workflows/publish.yml): - Blocks build/publish on the same
make check-allgate used locally and in CI. - Uses locked project tooling for
twineand docs builds instead ofuvxor live dependency resolution. - Builds and uploads wheels, then deploys docs on tag.
Supply-chain guardrails:
- Typus keeps normal runtime dependency ranges for downstream consumers, but repo-owned CI, docs, build, and publish environments are lockfile-governed.
pyproject.tomlconfigures a seven-day uvexclude-newercooldown. Lock refreshes should be intentional, reviewed, and committed with the code or workflow change that needs them.make check-allincludesmake lock-age-check, which fails whenuv.lockreferences artifacts uploaded inside the cooldown window.- Release workflows should use
uv sync --locked/uv run --locked; avoiduvxfor tools already present in the project lock.