Code Style#
call-report follows standard Python conventions (PEP 8) enforced by a set of
automated tools, all configured in pyproject.toml.
Formatting, linting, and typing#
ruff – formatting (line length 88, double quotes) and linting. The lint rule set is broad:
E,W,F,I(import sorting),UP(modern syntax),B,C4,SIM,TID,N,A,S(bandit security),T20(no strayprint),PTH(usepathliboveros.path),RUF,D(docstrings), andQ.mypy – strict type-checking (
disallow_untyped_defs) oversrcandtests. Every function and method needs type hints.numpydoc – validates that public docstrings follow the NumPy convention; see Documentation Style.
pre-commit – runs all of the above (plus
codespell,doc8, andsphinx-lint) automatically before each commit. Set it up once withpre-commit install(see Developer Installation).
Python-specific conventions#
Python 3.11+ is required, with support maintained through 3.14. Use modern typing syntax (
X | None,list[str]) rather thantyping.Optional/typing.List.Type hints should be precise and well-defined rather than defaulting to
Any: prefer specific types, generics, protocols, unions, and type variables that capture the real contract. Reach forAnyonly when it is genuinely the right choice (e.g. bridging truly dynamic data), and narrow it as soon as the real type is known. The package ships apy.typedmarker, so its annotations are part of the public contract downstream users type-check against.Fix the underlying issue rather than suppressing a check. Avoid
# noqa,# type: ignore, and# numpydoc ignoreunless a check is genuinely wrong for that specific line (e.g. two hooks make contradictory demands on the same object). When a suppression truly is needed, use the most targeted form available and apply it only to the exact object in conflict.Don’t add speculative abstractions, unused flexibility, or defensive error handling for scenarios that can’t occur. Prefer three similar lines over a premature abstraction.
Testing and coverage#
Tests live in
tests/and run underpytest.pytestalso runs every doctest insrc/call_report/**docstrings (via--doctest-modules); see Documentation Style for what that means for writing anExamplessection.The coverage gate is set to 100% (branch coverage included). This is the starting goal for every change: cover edge cases and error branches, not just the happy path. Add tests alongside every new feature rather than after the fact.
Only fall back from 100% when a line is genuinely not meaningfully testable – and in that case, exclude it explicitly and narrowly (for example,
# pragma: no coveron an@overloadorProtocolstub’s...body) rather than lowering the gate or leaving real code untested.
Dependency management#
Runtime dependencies are kept minimal and deliberate. narwhals is currently the only hard runtime dependency, and the only third-party library that may be hard-imported at module scope anywhere in
src/.Dataframe backends (
pandas,polars,pyarrow) are reached only through narwhals, which imports the configured backend lazily; they stay optional install extras and are only ever test/dev dependencies.Any other optional dependency must be loaded lazily via the helpers in
call_report.core._dependencies(import_optional,_lazy_import,_LazyModule) rather than a bareimport, following the pattern used by polars’ _dependencies.py.Adding any new dependency (runtime, optional, or dev) should be discussed first – see How to Contribute.
Running the checks#
See Developer Installation for the exact commands (pytest, ruff,
mypy, pre-commit) to run locally before opening a pull request.