Coverage for src / domain / validation / validation_gating.py: 30%
53 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-01-04 04:43 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-01-04 04:43 +0000
1"""Validation gating: determines whether validation should trigger based on changed files.
3This module provides logic to filter validation triggers based on patterns
4from mala.yaml configuration:
5- `code_patterns`: patterns for files that trigger validation
6- `config_files`: patterns for files that trigger validation and invalidate
7 lint/format/typecheck cache
8- `setup_files`: patterns for files that trigger validation and invalidate
9 setup cache (uv.lock, etc.)
11Special cases:
12- Empty `code_patterns` matches all files (validation always triggers)
13- Changes to `mala.yaml` always trigger validation
14- Changes to `config_files` or `setup_files` trigger cache invalidation
15"""
17from __future__ import annotations
19from pathlib import Path
20from typing import Protocol
22from .code_pattern_matcher import filter_matching_files
25# Config file name that always triggers validation when changed
26MALA_CONFIG_FILE = "mala.yaml"
29class ValidationSpecLike(Protocol):
30 """Protocol for objects with validation spec attributes.
32 This allows duck-typing for testing without requiring the full ValidationSpec.
33 """
35 code_patterns: list[str]
36 config_files: list[str]
37 setup_files: list[str]
40def should_trigger_validation(
41 changed_files: list[str],
42 spec: ValidationSpecLike,
43) -> bool:
44 """Determine if validation should run based on changed files.
46 Validation should trigger when:
47 1. `mala.yaml` is in the changed files (always triggers)
48 2. `code_patterns` is empty (matches all files)
49 3. Any changed file matches a pattern in `code_patterns`, `config_files`,
50 or `setup_files`
52 Args:
53 changed_files: List of file paths that were modified.
54 spec: ValidationSpec containing code_patterns to match against.
56 Returns:
57 True if validation should run, False to skip validation.
58 """
59 if not changed_files:
60 # No changes = no validation needed
61 return False
63 # Special case: mala.yaml changes always trigger validation
64 if any(_is_mala_config(f) for f in changed_files):
65 return True
67 # Empty patterns means match all files
68 if not spec.code_patterns:
69 return True
71 # Check if any changed file matches code/config/setup patterns
72 patterns = [*spec.code_patterns, *spec.config_files, *spec.setup_files]
73 if not patterns:
74 return True
75 matching_files = filter_matching_files(changed_files, patterns)
76 return len(matching_files) > 0
79def get_matching_code_files(
80 changed_files: list[str],
81 spec: ValidationSpecLike,
82) -> list[str]:
83 """Get the subset of changed files that match code patterns.
85 This is useful for understanding which files triggered validation.
87 Args:
88 changed_files: List of file paths that were modified.
89 spec: ValidationSpec containing code_patterns to match against.
91 Returns:
92 List of files that match code patterns (or all files if patterns empty).
93 """
94 if not spec.code_patterns:
95 return list(changed_files)
96 return filter_matching_files(changed_files, spec.code_patterns)
99def should_invalidate_lint_cache(
100 changed_files: list[str],
101 spec: ValidationSpecLike,
102) -> bool:
103 """Determine if lint/format/typecheck cache should be invalidated.
105 Cache should be invalidated when:
106 - mala.yaml is changed (always)
107 - Any changed file matches `config_files` patterns
109 Args:
110 changed_files: List of file paths that were modified.
111 spec: ValidationSpec containing config_files patterns.
113 Returns:
114 True if lint cache should be invalidated.
115 """
116 if not changed_files:
117 return False
119 # mala.yaml changes always invalidate lint cache
120 if any(_is_mala_config(f) for f in changed_files):
121 return True
123 # Check config_files patterns (if any defined)
124 if not spec.config_files:
125 return False
127 matching_files = filter_matching_files(changed_files, spec.config_files)
128 return len(matching_files) > 0
131def should_invalidate_setup_cache(
132 changed_files: list[str],
133 spec: ValidationSpecLike,
134) -> bool:
135 """Determine if setup cache should be invalidated.
137 Setup cache should be invalidated when any changed file matches
138 `setup_files` patterns (e.g., uv.lock, requirements.txt, etc.).
140 Args:
141 changed_files: List of file paths that were modified.
142 spec: ValidationSpec containing setup_files patterns.
144 Returns:
145 True if setup cache should be invalidated.
146 """
147 if not changed_files or not spec.setup_files:
148 return False
150 matching_files = filter_matching_files(changed_files, spec.setup_files)
151 return len(matching_files) > 0
154def get_config_files_changed(
155 changed_files: list[str],
156 spec: ValidationSpecLike,
157) -> list[str]:
158 """Get config files that were changed (for logging/debugging).
160 Args:
161 changed_files: List of file paths that were modified.
162 spec: ValidationSpec containing config_files patterns.
164 Returns:
165 List of changed config files.
166 """
167 if not spec.config_files:
168 return []
169 result = filter_matching_files(changed_files, spec.config_files)
170 # Also include mala.yaml if changed
171 if any(_is_mala_config(f) for f in changed_files):
172 if MALA_CONFIG_FILE not in result:
173 result = [MALA_CONFIG_FILE, *result]
174 return result
177def get_setup_files_changed(
178 changed_files: list[str],
179 spec: ValidationSpecLike,
180) -> list[str]:
181 """Get setup files that were changed (for logging/debugging).
183 Args:
184 changed_files: List of file paths that were modified.
185 spec: ValidationSpec containing setup_files patterns.
187 Returns:
188 List of changed setup files.
189 """
190 if not spec.setup_files:
191 return []
192 return filter_matching_files(changed_files, spec.setup_files)
195def _is_mala_config(file_path: str) -> bool:
196 """Check if a file path is the mala config file.
198 Handles both absolute and relative paths by checking the basename.
200 Args:
201 file_path: File path to check.
203 Returns:
204 True if this is the mala.yaml config file.
205 """
206 # Check basename to handle paths like /repo/mala.yaml or mala.yaml
207 return Path(file_path).name == MALA_CONFIG_FILE