Development environment
This page describes what you need to know to work on the Pymetropolis codebase itself (as opposed to using it to run simulations). It assumes you are already familiar with the Pipeline (Steps, MetroFiles, Parameters).
Setting up
Pymetropolis uses uv for dependency management. After cloning the repository and installing uv, install the project and its development dependencies (pytest, ruff, ty) with:
uv sync
Use uv add <package> / uv remove <package> to change dependencies rather than hand-editing
pyproject.toml, and uv run <command> to run any tool inside the project’s virtual environment
(e.g. uv run pymetropolis my-config.toml) rather than manually activating it.
pyproject.toml declares requires-python = ">=3.12"; write code that targets that version, using
modern syntax (X | None rather than Optional[X], builtin generics such as list[int] rather
than typing.List[int]).
Code style
Formatting and linting are handled by ruff:
uv run ruff format
uv run ruff check --fix
The project’s [tool.ruff] settings (in pyproject.toml) set a 100-character line length and
disable the “magic trailing comma” formatting behavior. Two lint rules are intentionally disabled:
C408 (so list()/dict() calls are allowed) and RUF012 (so a Step’s input_files/
output_files class attributes can stay plain mutable dict literals, which is the pattern the
Pipeline relies on).
Type-checking is handled by ty:
uv run ty check
Type-hint every new or modified function signature.
Tests
Tests live under tests/ and run with pytest:
uv run pytest
Existing tests (see tests/pipeline_test.py, tests/population_test.py) follow a consistent,
lightweight style: plain test_* functions (no fixtures or test classes), a handful of small
throwaway Step/MetroFile classes defined at module level for the scenario being tested, and
tempfile.TemporaryDirectory() combined with Config({...}) / MetroPipeline(...) to exercise the
Pipeline without touching real files. Follow this style for new tests rather than introducing a new
testing pattern.
Submitting changes
The contributing guidelines and code of conduct in the repository apply to every contribution. In short:
- Fork the repository and create a feature branch.
- Make your changes, and run
uv run ruff format,uv run ruff check --fix, anduv run ty checkuntil they report no issues. - Run
uv run pytestand make sure every test passes; add tests for new behavior. - Update
CHANGELOG.mdif the change is user-visible. - Open a pull request. The PR template checklist covers code style, documentation, tests, and licensing — fill it in.
Every pull request and every push to main is checked by GitHub Actions (.github/workflows/lint.yml):
ruff format --check, ruff check, ty check, and the full test suite must all pass before a PR
can be merged.
Note
Releasing a new version (tagging, building, and publishing to PyPI) is handled by maintainers via the
publish-to-pypi.ymlworkflow, triggered by pushing av*tag. As a contributor, you do not need to do this yourself.
Regenerating the reference documentation
The Steps, Parameters, and
MetroFiles reference pages in this book are partly generated from the
Pymetropolis codebase, by introspecting every registered Step/Parameter/MetroFile (their
docstrings, Parameter descriptions, Column schemas, etc.). If you add or modify a Step,
Parameter, or MetroFile, an official maintainer will regenerate these pages from the Pymetropolis
repository with:
uv run python -m pymetropolis.bin.generate_doc <path-to-this-book>/src/pymetropolis/references/
This overwrites steps-generated.md, parameters-generated.md, and files-generated.md in the
book’s repository.
Warning
Only what is exported through your package’s
STEPS/FILESlists (and, transitively, aggregated intopymetropolis.schema) is picked up — see the Creating MetroFiles and Creating Steps with Parameters pages for the registration convention.