Coverage for src/keel/classify.py: 100%
13 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-16 18:07 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-16 18:07 +0000
1"""Risk classification — which tier a change is, from the files it touches.
3Pure and deterministic: the tier is a function of the changed paths and the
4project's globs, with no I/O. The tier drives the reviewer count (see
5:func:`keel.ship.reviewer_count`).
6"""
8from __future__ import annotations
10import fnmatch
12#: Default tier when nothing else matches.
13DEFAULT_TIER = 2
16def _matches_any(path: str, globs: tuple[str, ...]) -> bool:
17 return any(fnmatch.fnmatch(path, g) for g in globs)
20def tier_for_files(
21 changed: list[str], *, tier3_globs: tuple[str, ...] = (), docs_globs: tuple[str, ...] = ()
22) -> int:
23 """Classify a change into TIER 1/2/3 from its changed files.
25 * any file matching ``tier3_globs`` (migrations, CI, core code…) ⇒ **TIER-3**;
26 * otherwise, if *every* changed file matches ``docs_globs`` (docs-only) ⇒ **TIER-1**;
27 * otherwise ⇒ **TIER-2** (the default). An empty changeset is TIER-2 (unknown).
28 """
29 if not changed:
30 return DEFAULT_TIER
31 if any(_matches_any(p, tier3_globs) for p in changed):
32 return 3
33 if docs_globs and all(_matches_any(p, docs_globs) for p in changed):
34 return 1
35 return DEFAULT_TIER