Contributing

Setting up

Clone the repository and install zabgrab in a virtual environment, in editable mode with the development tools:

git clone https://github.com/SeanDS/zabgrab.git
cd zabgrab
python -m venv .venv
.venv/bin/pip install -e ".[dev,plot,docs]"
.venv/bin/pre-commit install

The pre-commit hooks check formatting and linting on each commit.

Checks

Run these before pushing; CI runs the same checks on Linux (Python 3.11 to 3.14), macOS and Windows:

.venv/bin/ruff check .
.venv/bin/ruff format --check .
.venv/bin/mypy
.venv/bin/pytest --cov=zabgrab

The tests need 100% coverage (branches included) to pass. Use # pragma: no cover only for lines that genuinely can’t be tested.

CI’s last job, CI passed, passes only if every other job did; it’s the one check main’s branch rules require.

The tests run in UK time, which their expected local times are written in. On Linux and macOS they set it themselves; Windows can’t change a running process’s time zone, so there the system time zone must be UK time (tzutil /s "GMT Standard Time").

Building the documentation

.venv/bin/sphinx-build -b html docs docs/_build/html

Then open docs/_build/html/index.html. CI builds the documentation too, and fails on any warning (add --fail-on-warning to see the same). Each CI run keeps the built documentation as an artifact named docs, and once changes reach main they’re published as the development documentation (see Making a release).

Code layout

The code is split into layers, and dependencies only point inwards:

zabgrab.model

Data classes only: config, item specs, Zabbix objects, queries and fetched data.

zabgrab.services

The logic: resolving settings, choosing servers, logging in, finding items, downloading, naming files, updating and merging. Services get everything they need (the Zabbix client, the UI, the clock, …) as arguments, through the protocols in zabgrab.services.ports, and never print or prompt directly.

zabgrab.adapters

The outside world: the Zabbix API, config files, file formats and the keyring.

zabgrab.ui

Terminal output (rich) and plots (matplotlib).

zabgrab.cli

The click commands, which only wire the rest together.

Data goes to stdout; messages, warnings, questions and progress go to stderr. Every question carries a fix: how to give the answer without being asked, shown when zabgrab can’t ask (see Scripts and scheduled jobs).

Conventions

  • Comment code wherever it isn’t trivial, explaining why rather than restating what.

  • Every module, class, function and method has a NumPy-style docstring, which this documentation’s API reference is built from. ruff’s docstring rules check the basics, and examples in docstrings run as tests.

  • Add tests covering every branch of new code. Tests use an in-memory fake Zabbix server (tests/fakezabbix.py) and fake UI, clock and keyring (tests/fakes.py); command line tests use tests/cli_support.py.

  • Record design decisions in PLAN.md, and user-visible changes in CHANGELOG.rst under the next version’s heading ((unreleased)).

MATLAB files

The layout of .mat and .h5 files is pinned by files verified in MATLAB, in tests/matlab/reference/. If a change breaks the tests comparing against them, the new layout must be checked in MATLAB before the reference files are replaced: tests/test_matlab.py runs tests/matlab/check_files.m in MATLAB when it’s installed (or listed in $ZABGRAB_MATLAB), and is skipped otherwise.

Every group records the version of the layout it was written with (FORMAT_VERSION in zabgrab.model.series, and supported_format in matlab/zabgrab_load.m). Readers refuse newer versions, so increase both whenever the layout changes in a way an older zabgrab or zabgrab_load would misread: removing, renaming or changing the meaning of a group, dataset, attribute or metadata key. Purely additive changes that older readers safely ignore don’t need a new version. Readers must keep reading every older version.