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

1"""Risk classification — which tier a change is, from the files it touches. 

2 

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""" 

7 

8from __future__ import annotations 

9 

10import fnmatch 

11 

12#: Default tier when nothing else matches. 

13DEFAULT_TIER = 2 

14 

15 

16def _matches_any(path: str, globs: tuple[str, ...]) -> bool: 

17 return any(fnmatch.fnmatch(path, g) for g in globs) 

18 

19 

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. 

24 

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