@@ -39,6 +39,7 @@ class CacheStatus(str, Enum):
3939 VERSION_MISMATCH = "version_mismatch"
4040 PYTHON_TAG_MISMATCH = "python_tag_mismatch"
4141 FINGERPRINT_MISMATCH = "mismatch_fingerprint_version"
42+ ANALYSIS_PROFILE_MISMATCH = "analysis_profile_mismatch"
4243 INTEGRITY_FAILED = "integrity_failed"
4344
4445
@@ -84,15 +85,22 @@ class CacheEntry(TypedDict):
8485 segments : list [SegmentDict ]
8586
8687
88+ class AnalysisProfile (TypedDict ):
89+ min_loc : int
90+ min_stmt : int
91+
92+
8793class CacheData (TypedDict ):
8894 version : str
8995 python_tag : str
9096 fingerprint_version : str
97+ analysis_profile : AnalysisProfile
9198 files : dict [str , CacheEntry ]
9299
93100
94101class Cache :
95102 __slots__ = (
103+ "analysis_profile" ,
96104 "cache_schema_version" ,
97105 "data" ,
98106 "fingerprint_version" ,
@@ -112,14 +120,21 @@ def __init__(
112120 * ,
113121 root : str | Path | None = None ,
114122 max_size_bytes : int | None = None ,
123+ min_loc : int = 15 ,
124+ min_stmt : int = 6 ,
115125 ):
116126 self .path = Path (path )
117127 self .root = _resolve_root (root )
118128 self .fingerprint_version = BASELINE_FINGERPRINT_VERSION
129+ self .analysis_profile : AnalysisProfile = {
130+ "min_loc" : min_loc ,
131+ "min_stmt" : min_stmt ,
132+ }
119133 self .data : CacheData = _empty_cache_data (
120134 version = self ._CACHE_VERSION ,
121135 python_tag = current_python_tag (),
122136 fingerprint_version = self .fingerprint_version ,
137+ analysis_profile = self .analysis_profile ,
123138 )
124139 self .legacy_secret_warning = self ._detect_legacy_secret_warning ()
125140 self .cache_schema_version : str | None = None
@@ -164,6 +179,7 @@ def _ignore_cache(
164179 version = self ._CACHE_VERSION ,
165180 python_tag = current_python_tag (),
166181 fingerprint_version = self .fingerprint_version ,
182+ analysis_profile = self .analysis_profile ,
167183 )
168184
169185 def _sign_data (self , data : Mapping [str , object ]) -> str :
@@ -309,6 +325,28 @@ def _parse_cache_document(self, raw_obj: object) -> CacheData | None:
309325 )
310326 return None
311327
328+ analysis_profile = _as_analysis_profile (payload .get ("ap" ))
329+ if analysis_profile is None :
330+ self ._ignore_cache (
331+ "Cache format invalid; ignoring cache." ,
332+ status = CacheStatus .INVALID_TYPE ,
333+ schema_version = version ,
334+ )
335+ return None
336+
337+ if analysis_profile != self .analysis_profile :
338+ self ._ignore_cache (
339+ "Cache analysis profile mismatch "
340+ f"(found min_loc={ analysis_profile ['min_loc' ]} , "
341+ f"min_stmt={ analysis_profile ['min_stmt' ]} ; "
342+ f"expected min_loc={ self .analysis_profile ['min_loc' ]} , "
343+ f"min_stmt={ self .analysis_profile ['min_stmt' ]} ); "
344+ "ignoring cache." ,
345+ status = CacheStatus .ANALYSIS_PROFILE_MISMATCH ,
346+ schema_version = version ,
347+ )
348+ return None
349+
312350 files_obj = payload .get ("files" )
313351 files_dict = _as_str_dict (files_obj )
314352 if files_dict is None :
@@ -337,6 +375,7 @@ def _parse_cache_document(self, raw_obj: object) -> CacheData | None:
337375 "version" : self ._CACHE_VERSION ,
338376 "python_tag" : runtime_tag ,
339377 "fingerprint_version" : self .fingerprint_version ,
378+ "analysis_profile" : self .analysis_profile ,
340379 "files" : parsed_files ,
341380 }
342381
@@ -356,6 +395,7 @@ def save(self) -> None:
356395 payload : dict [str , object ] = {
357396 "py" : current_python_tag (),
358397 "fp" : self .fingerprint_version ,
398+ "ap" : self .analysis_profile ,
359399 "files" : wire_files ,
360400 }
361401 signed_doc = {
@@ -371,6 +411,7 @@ def save(self) -> None:
371411 self .data ["version" ] = self ._CACHE_VERSION
372412 self .data ["python_tag" ] = current_python_tag ()
373413 self .data ["fingerprint_version" ] = self .fingerprint_version
414+ self .data ["analysis_profile" ] = self .analysis_profile
374415
375416 except OSError as e :
376417 raise CacheError (f"Failed to save cache: { e } " ) from e
@@ -508,11 +549,13 @@ def _empty_cache_data(
508549 version : str ,
509550 python_tag : str ,
510551 fingerprint_version : str ,
552+ analysis_profile : AnalysisProfile ,
511553) -> CacheData :
512554 return {
513555 "version" : version ,
514556 "python_tag" : python_tag ,
515557 "fingerprint_version" : fingerprint_version ,
558+ "analysis_profile" : analysis_profile ,
516559 "files" : {},
517560 }
518561
@@ -542,6 +585,22 @@ def _as_str_dict(value: object) -> dict[str, object] | None:
542585 return value
543586
544587
588+ def _as_analysis_profile (value : object ) -> AnalysisProfile | None :
589+ obj = _as_str_dict (value )
590+ if obj is None :
591+ return None
592+
593+ if set (obj .keys ()) != {"min_loc" , "min_stmt" }:
594+ return None
595+
596+ min_loc = _as_int (obj .get ("min_loc" ))
597+ min_stmt = _as_int (obj .get ("min_stmt" ))
598+ if min_loc is None or min_stmt is None :
599+ return None
600+
601+ return {"min_loc" : min_loc , "min_stmt" : min_stmt }
602+
603+
545604def _decode_wire_file_entry (value : object , filepath : str ) -> CacheEntry | None :
546605 obj = _as_str_dict (value )
547606 if obj is None :
0 commit comments