Contributing and testing

Development setup

The source repository is private (maintainers only); the public repository humansys-lab/ecg-records holds the documentation, schemas and examples.

git clone git@github.com:humansys-lab/ECG-feature-extraction-.git   # private
cd ECG-feature-extraction-
python -m venv .venv && source .venv/bin/activate
python -m pip install -r requirements-ci.txt
python -m pip install -e "./feature_extraction[dev]"
python -m pip install "mkdocs>=1.6,<2"

requirements-ci.txt lists the test dependencies (pytest, Hypothesis, jsonschema, Matplotlib, wfdb). The dev extra adds build, twine and import-linter. The package itself only needs NumPy and SciPy.

Running the checks

Check Command
test suite python -m pytest -q tests
import-layer contracts lint-imports --config feature_extraction/.importlinter
validation registry python tools/check_validation_registry.py
changelog has the current version python tools/check_changelog.py
performance budget python tools/check_performance.py reference --repeats 5
generated reference pages are current python tools/build_docs.py --check
documentation examples run python tools/check_doc_examples.py
documentation site builds mkdocs build --strict

CI (.github/workflows/test.yml) runs all of them on every pull request.

Optional dependencies in tests

  • Tests that need Matplotlib (tests/viz/) are skipped when it is not installed; the import-isolation tests always run.
  • The legacy ECGFeatureExtractor.extract() path and the legacy JSON export include the interpretation, which lives in the separate ecginterpret distribution. Their tests are marked @needs_interpretation (tests/optional.py) and skip unless ecginterpret is installed. The record API is tested unconditionally.
  • Tests that read public datasets skip unless the data is present (for example LUDB under data/).

Test layout

Kind Examples
record contract and schema test_ecg_record_contract.py, test_library_design_review.py, test_record_sidecar.py
property tests (Hypothesis) test_record_properties.py
pinned reference record test_golden_reference.py, fixture tests/fixtures/golden/reference_10s_12lead/
pipeline, stages, policies test_pipeline_decomposition.py, test_pacing_policy.py, test_limited_leads.py
engine behaviour test_atrial_events.py, test_af_afl_features.py, test_quality_contract.py, …
public facade and deprecations test_public_facade.py, test_engine_relocation.py
plotting tests/viz/
tools test_performance_budget.py, test_validation_registry.py

The reference fixture

tests/fixtures/golden/reference_10s_12lead/ holds a synthetic 10 s, 12-lead, 500 Hz signal (signal.npz, generated by make_signal.py) and its expected records in all three profiles. test_golden_reference.py requires the current code to reproduce them exactly. When a reviewed change alters the records, regenerate them with python tests/fixtures/golden/update_fixture.py and explain the difference in the pull request.

Documentation

The site lives in docs/site/ and is built with MkDocs (mkdocs.yml).

  • docs/site/reference/ is generated from the installed package and the shipped schema files by python tools/build_docs.py. Do not edit it by hand; CI fails when it is stale.
  • Every ```python block in the guide is executed by tools/check_doc_examples.py, in a scratch directory that contains ecg.npy and ecg.npz (the reference signal) and ecg.record.json (its record). Blocks on one page share a namespace, in order. A block that needs files the checker cannot provide is preceded by <!-- doc-check: skip (...) -->.
  • The full example record in guide/record-format.md is validated against the JSON Schema and the strict reader by test_library_design_review.py.
  • Preview locally with mkdocs serve. Every push to main that touches the docs, the package or the schemas rebuilds the site with the same checks and deploys it to GitHub Pages (.github/workflows/docs.yml), at https://humansys-lab.github.io/ECG-feature-extraction-/.

Style

  • Public functions take keyword-only options and validate their inputs.
  • Errors are ECGInputError subclasses with a stable code.
  • Records must stay deterministic: no randomness, no timestamps, sorted keys.
  • Keep the layer rules; lint-imports enforces them.