Contributing and rules

Commands

uv sync                                   # environment incl. dev tools
uv run python tools/build_dtc_db.py       # trouble code catalog
uv run ruff format . && uv run ruff check . && uv run mypy   # mypy strict
uv run pytest                             # about 1800 tests, ~4.5 min

Check the exit code of pytest itself, do not hide it behind | tail.

Rules

  • Layers: transport → protocol → services → ui/cli, no imports upwards; higher layers only know the Transport protocol (Architecture).

  • Read-only: mode 04 only via services/clear.py, clearing stays locked until reading has been verified at the car. No coding, no flashing, no manufacturer commands (Safety concept).

  • New commands are deliberately enabled in the allowlist (protocol/elm327.py) and entered in tests/fixtures/command_spec.yaml with datasheet page and quote. Otherwise the tests fail, and the Command reference shows them automatically.

  • Standards compliance: parsers stick to SAE J1979, ISO 15765 and the ELM327 datasheet. Deviations of the emulator are corrected in the tests, not in the product code.

  • Offline: trouble code texts from SQLite. Online only after opt-in: NHTSA vPIC (VIN) and short explanations for codes without catalog text (ADR 0004: Online explanations for trouble codes without catalog text).

  • Language: German in comments, docstrings and commit messages. User interface, output and documentation exist in German and English (below). ruff flags en dashes in Python strings; use : or - there.

Translations

Texts in the code are German and go through tr("…") (obd_diag.i18n); placeholders in curly braces, filled after translating: tr("Verbinde mit {port} …").format(port=port), never an f-string inside tr. Constants fixed at import time are marked with N_("…") and translated with tr on output; singular/plural with trn. In QML qsTr("…") with .arg(). The English texts are in src/obd_diag/locale/en.po:

uv run python tools/translations.py           # add new texts, remove old ones
uv run python tools/translations.py --todo    # missing ones as JSON {"number": "text"}
uv run python tools/translations.py --fill translations.json

tests/unit/test_i18n.py fails if the catalog is not up to date, a translation is missing, placeholders do not match or a German text is output without tr/qsTr. Log messages (log.…) stay German.

Documentation

The documentation is in docs/ (Markdown with MyST, Sphinx, Furo theme), the English version with the same file names in docs/en/. CI builds both for every PR and push to main and stores the HTML as the artifact “dokumentation” on the run (.github/workflows/docs.yml). Every push to main publishes it at https://phprell.github.io/obd-diag/ (German) and https://phprell.github.io/obd-diag/en/ (English). Building locally:

uv run --group docs sphinx-build -W docs docs/_build
uv run --group docs sphinx-build -W docs/en docs/_build/en
python -m http.server -d docs/_build      # then http://localhost:8000

Every page exists in both languages; the link at the top of the sidebar switches to the same page in the other language. tests/unit/test_docs_i18n.py checks that both trees have the same pages with the same number of headings, code blocks and images. Whoever changes a page changes the other language in the same PR.

-W turns warnings into errors, e.g. broken references. What can be generated from the code is generated, not copied:

Page

Source

Generated by

Command reference

tests/fixtures/command_spec.yaml

tools/docs_tables.py

PID formulas

PIDS in protocol/pids.py

tools/docs_tables.py

Command line (options)

build_parser in cli.py

sphinx-argparse

API reference

docstrings

autodoc

pictures in User interface

user interface against the emulator

uv run python tools/docs_screenshots.py (English: --lang en)

New functions bring the matching page under “Usage” in the same PR. After changes to the user interface, generate the pictures again and check them in. Byte examples on the technical pages are checked against the parsers by tests/unit/test_docs_examples.py; whoever changes an example changes the test too.

Release

Set the version in pyproject.toml and src/obd_diag/__init__.py, write a section ## X.Y.Z – date in CHANGELOG.md and add the version to the AppStream file src/obd_diag/share/io.github.phprell.obd_diag.metainfo.xml as well. After the merge, push the tag vX.Y.Z on main. .github/workflows/release.yml then checks tag and versions, builds catalog, sdist, wheel, AUR package and Flatpak (.github/workflows/packaging.yml, also on every PR), creates the GitHub release with the changelog section and uploads to PyPI. Packaging templates: packaging/aur/PKGBUILD, packaging/flatpak/.