Contributing
============
Setting up
----------
Clone the repository and install zabgrab in a virtual environment, in editable mode
with the development tools:
.. code-block:: bash
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:
.. code-block:: bash
.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
--------------------------
.. code-block:: bash
.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 :doc:`releasing`).
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 :mod:`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 :doc:`/guide/automation`).
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 :doc:`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.