11from __future__ import annotations
22
3- from dataclasses import dataclass
3+ from dataclasses import dataclass , field
44
55
66@dataclass
@@ -10,6 +10,7 @@ class ParseWarningCollector:
1010 composers_skipped : int = 0
1111 bubbles_skipped : int = 0
1212 composers_processing_failed : int = 0
13+ source_failures : list [dict ] = field (default_factory = list )
1314
1415 def record_composer_skipped (self , count : int = 1 ) -> None :
1516 if count > 0 :
@@ -24,12 +25,22 @@ def record_composer_processing_failure(self, count: int = 1) -> None:
2425 if count > 0 :
2526 self .composers_processing_failed += count
2627
28+ def record_source_failure (self , exc : BaseException , source : str ) -> None :
29+ """Record a whole-source failure (e.g. the global storage DB is unreadable).
30+
31+ Distinct from per-item parse skips: signals that an entire data source
32+ could not be searched so the API can warn callers that results may be
33+ incomplete.
34+ """
35+ self .source_failures .append ({"source" : source , "detail" : str (exc )})
36+
2737 @property
2838 def has_warnings (self ) -> bool :
2939 return (
3040 self .composers_skipped > 0
3141 or self .bubbles_skipped > 0
3242 or self .composers_processing_failed > 0
43+ or bool (self .source_failures )
3344 )
3445
3546 def to_api_list (self ) -> list [dict ]:
@@ -65,6 +76,12 @@ def to_api_list(self) -> list[dict]:
6576 f"{ n } { noun } could not be fully assembled after parsing"
6677 ),
6778 })
79+ for sf in self .source_failures :
80+ warnings .append ({
81+ "type" : "source_failure" ,
82+ "source" : sf ["source" ],
83+ "detail" : sf ["detail" ],
84+ })
6885 return warnings
6986
7087 def attach_to (self , payload : dict ) -> dict :
0 commit comments