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

1"""Validation gating: determines whether validation should trigger based on changed files. 

2 

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.) 

10 

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

16 

17from __future__ import annotations 

18 

19from pathlib import Path 

20from typing import Protocol 

21 

22from .code_pattern_matcher import filter_matching_files 

23 

24 

25# Config file name that always triggers validation when changed 

26MALA_CONFIG_FILE = "mala.yaml" 

27 

28 

29class ValidationSpecLike(Protocol): 

30 """Protocol for objects with validation spec attributes. 

31 

32 This allows duck-typing for testing without requiring the full ValidationSpec. 

33 """ 

34 

35 code_patterns: list[str] 

36 config_files: list[str] 

37 setup_files: list[str] 

38 

39 

40def should_trigger_validation( 

41 changed_files: list[str], 

42 spec: ValidationSpecLike, 

43) -> bool: 

44 """Determine if validation should run based on changed files. 

45 

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` 

51 

52 Args: 

53 changed_files: List of file paths that were modified. 

54 spec: ValidationSpec containing code_patterns to match against. 

55 

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 

62 

63 # Special case: mala.yaml changes always trigger validation 

64 if any(_is_mala_config(f) for f in changed_files): 

65 return True 

66 

67 # Empty patterns means match all files 

68 if not spec.code_patterns: 

69 return True 

70 

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 

77 

78 

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. 

84 

85 This is useful for understanding which files triggered validation. 

86 

87 Args: 

88 changed_files: List of file paths that were modified. 

89 spec: ValidationSpec containing code_patterns to match against. 

90 

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) 

97 

98 

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. 

104 

105 Cache should be invalidated when: 

106 - mala.yaml is changed (always) 

107 - Any changed file matches `config_files` patterns 

108 

109 Args: 

110 changed_files: List of file paths that were modified. 

111 spec: ValidationSpec containing config_files patterns. 

112 

113 Returns: 

114 True if lint cache should be invalidated. 

115 """ 

116 if not changed_files: 

117 return False 

118 

119 # mala.yaml changes always invalidate lint cache 

120 if any(_is_mala_config(f) for f in changed_files): 

121 return True 

122 

123 # Check config_files patterns (if any defined) 

124 if not spec.config_files: 

125 return False 

126 

127 matching_files = filter_matching_files(changed_files, spec.config_files) 

128 return len(matching_files) > 0 

129 

130 

131def should_invalidate_setup_cache( 

132 changed_files: list[str], 

133 spec: ValidationSpecLike, 

134) -> bool: 

135 """Determine if setup cache should be invalidated. 

136 

137 Setup cache should be invalidated when any changed file matches 

138 `setup_files` patterns (e.g., uv.lock, requirements.txt, etc.). 

139 

140 Args: 

141 changed_files: List of file paths that were modified. 

142 spec: ValidationSpec containing setup_files patterns. 

143 

144 Returns: 

145 True if setup cache should be invalidated. 

146 """ 

147 if not changed_files or not spec.setup_files: 

148 return False 

149 

150 matching_files = filter_matching_files(changed_files, spec.setup_files) 

151 return len(matching_files) > 0 

152 

153 

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). 

159 

160 Args: 

161 changed_files: List of file paths that were modified. 

162 spec: ValidationSpec containing config_files patterns. 

163 

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 

175 

176 

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). 

182 

183 Args: 

184 changed_files: List of file paths that were modified. 

185 spec: ValidationSpec containing setup_files patterns. 

186 

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) 

193 

194 

195def _is_mala_config(file_path: str) -> bool: 

196 """Check if a file path is the mala config file. 

197 

198 Handles both absolute and relative paths by checking the basename. 

199 

200 Args: 

201 file_path: File path to check. 

202 

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