truthound-dashboard 1.4.4__py3-none-any.whl → 1.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. truthound_dashboard/api/alerts.py +75 -86
  2. truthound_dashboard/api/anomaly.py +7 -13
  3. truthound_dashboard/api/cross_alerts.py +38 -52
  4. truthound_dashboard/api/drift.py +49 -59
  5. truthound_dashboard/api/drift_monitor.py +234 -79
  6. truthound_dashboard/api/enterprise_sampling.py +498 -0
  7. truthound_dashboard/api/history.py +57 -5
  8. truthound_dashboard/api/lineage.py +3 -48
  9. truthound_dashboard/api/maintenance.py +104 -49
  10. truthound_dashboard/api/mask.py +1 -2
  11. truthound_dashboard/api/middleware.py +2 -1
  12. truthound_dashboard/api/model_monitoring.py +435 -311
  13. truthound_dashboard/api/notifications.py +227 -191
  14. truthound_dashboard/api/notifications_advanced.py +21 -20
  15. truthound_dashboard/api/observability.py +586 -0
  16. truthound_dashboard/api/plugins.py +2 -433
  17. truthound_dashboard/api/profile.py +199 -37
  18. truthound_dashboard/api/quality_reporter.py +701 -0
  19. truthound_dashboard/api/reports.py +7 -16
  20. truthound_dashboard/api/router.py +66 -0
  21. truthound_dashboard/api/rule_suggestions.py +5 -5
  22. truthound_dashboard/api/scan.py +17 -19
  23. truthound_dashboard/api/schedules.py +85 -50
  24. truthound_dashboard/api/schema_evolution.py +6 -6
  25. truthound_dashboard/api/schema_watcher.py +667 -0
  26. truthound_dashboard/api/sources.py +98 -27
  27. truthound_dashboard/api/tiering.py +1323 -0
  28. truthound_dashboard/api/triggers.py +14 -11
  29. truthound_dashboard/api/validations.py +12 -11
  30. truthound_dashboard/api/versioning.py +1 -6
  31. truthound_dashboard/core/__init__.py +129 -3
  32. truthound_dashboard/core/actions/__init__.py +62 -0
  33. truthound_dashboard/core/actions/custom.py +426 -0
  34. truthound_dashboard/core/actions/notifications.py +910 -0
  35. truthound_dashboard/core/actions/storage.py +472 -0
  36. truthound_dashboard/core/actions/webhook.py +281 -0
  37. truthound_dashboard/core/anomaly.py +262 -67
  38. truthound_dashboard/core/anomaly_explainer.py +4 -3
  39. truthound_dashboard/core/backends/__init__.py +67 -0
  40. truthound_dashboard/core/backends/base.py +299 -0
  41. truthound_dashboard/core/backends/errors.py +191 -0
  42. truthound_dashboard/core/backends/factory.py +423 -0
  43. truthound_dashboard/core/backends/mock_backend.py +451 -0
  44. truthound_dashboard/core/backends/truthound_backend.py +718 -0
  45. truthound_dashboard/core/checkpoint/__init__.py +87 -0
  46. truthound_dashboard/core/checkpoint/adapters.py +814 -0
  47. truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
  48. truthound_dashboard/core/checkpoint/runner.py +270 -0
  49. truthound_dashboard/core/connections.py +645 -23
  50. truthound_dashboard/core/converters/__init__.py +14 -0
  51. truthound_dashboard/core/converters/truthound.py +620 -0
  52. truthound_dashboard/core/cross_alerts.py +540 -320
  53. truthound_dashboard/core/datasource_factory.py +1672 -0
  54. truthound_dashboard/core/drift_monitor.py +216 -20
  55. truthound_dashboard/core/enterprise_sampling.py +1291 -0
  56. truthound_dashboard/core/interfaces/__init__.py +225 -0
  57. truthound_dashboard/core/interfaces/actions.py +652 -0
  58. truthound_dashboard/core/interfaces/base.py +247 -0
  59. truthound_dashboard/core/interfaces/checkpoint.py +676 -0
  60. truthound_dashboard/core/interfaces/protocols.py +664 -0
  61. truthound_dashboard/core/interfaces/reporters.py +650 -0
  62. truthound_dashboard/core/interfaces/routing.py +646 -0
  63. truthound_dashboard/core/interfaces/triggers.py +619 -0
  64. truthound_dashboard/core/lineage.py +407 -71
  65. truthound_dashboard/core/model_monitoring.py +431 -3
  66. truthound_dashboard/core/notifications/base.py +4 -0
  67. truthound_dashboard/core/notifications/channels.py +501 -1203
  68. truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
  69. truthound_dashboard/core/notifications/deduplication/service.py +131 -348
  70. truthound_dashboard/core/notifications/dispatcher.py +202 -11
  71. truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
  72. truthound_dashboard/core/notifications/escalation/engine.py +168 -358
  73. truthound_dashboard/core/notifications/routing/__init__.py +88 -128
  74. truthound_dashboard/core/notifications/routing/engine.py +90 -317
  75. truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
  76. truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
  77. truthound_dashboard/core/notifications/throttling/builder.py +117 -255
  78. truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
  79. truthound_dashboard/core/phase5/collaboration.py +1 -1
  80. truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
  81. truthound_dashboard/core/quality_reporter.py +1359 -0
  82. truthound_dashboard/core/report_history.py +0 -6
  83. truthound_dashboard/core/reporters/__init__.py +175 -14
  84. truthound_dashboard/core/reporters/adapters.py +943 -0
  85. truthound_dashboard/core/reporters/base.py +0 -3
  86. truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
  87. truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
  88. truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
  89. truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
  90. truthound_dashboard/core/reporters/compat.py +266 -0
  91. truthound_dashboard/core/reporters/csv_reporter.py +2 -35
  92. truthound_dashboard/core/reporters/factory.py +526 -0
  93. truthound_dashboard/core/reporters/interfaces.py +745 -0
  94. truthound_dashboard/core/reporters/registry.py +1 -10
  95. truthound_dashboard/core/scheduler.py +165 -0
  96. truthound_dashboard/core/schema_evolution.py +3 -3
  97. truthound_dashboard/core/schema_watcher.py +1528 -0
  98. truthound_dashboard/core/services.py +595 -76
  99. truthound_dashboard/core/store_manager.py +810 -0
  100. truthound_dashboard/core/streaming_anomaly.py +169 -4
  101. truthound_dashboard/core/tiering.py +1309 -0
  102. truthound_dashboard/core/triggers/evaluators.py +178 -8
  103. truthound_dashboard/core/truthound_adapter.py +2620 -197
  104. truthound_dashboard/core/unified_alerts.py +23 -20
  105. truthound_dashboard/db/__init__.py +8 -0
  106. truthound_dashboard/db/database.py +8 -2
  107. truthound_dashboard/db/models.py +944 -25
  108. truthound_dashboard/db/repository.py +2 -0
  109. truthound_dashboard/main.py +15 -0
  110. truthound_dashboard/schemas/__init__.py +177 -16
  111. truthound_dashboard/schemas/base.py +44 -23
  112. truthound_dashboard/schemas/collaboration.py +19 -6
  113. truthound_dashboard/schemas/cross_alerts.py +19 -3
  114. truthound_dashboard/schemas/drift.py +61 -55
  115. truthound_dashboard/schemas/drift_monitor.py +67 -23
  116. truthound_dashboard/schemas/enterprise_sampling.py +653 -0
  117. truthound_dashboard/schemas/lineage.py +0 -33
  118. truthound_dashboard/schemas/mask.py +10 -8
  119. truthound_dashboard/schemas/model_monitoring.py +89 -10
  120. truthound_dashboard/schemas/notifications_advanced.py +13 -0
  121. truthound_dashboard/schemas/observability.py +453 -0
  122. truthound_dashboard/schemas/plugins.py +0 -280
  123. truthound_dashboard/schemas/profile.py +154 -247
  124. truthound_dashboard/schemas/quality_reporter.py +403 -0
  125. truthound_dashboard/schemas/reports.py +2 -2
  126. truthound_dashboard/schemas/rule_suggestion.py +8 -1
  127. truthound_dashboard/schemas/scan.py +4 -24
  128. truthound_dashboard/schemas/schedule.py +11 -3
  129. truthound_dashboard/schemas/schema_watcher.py +727 -0
  130. truthound_dashboard/schemas/source.py +17 -2
  131. truthound_dashboard/schemas/tiering.py +822 -0
  132. truthound_dashboard/schemas/triggers.py +16 -0
  133. truthound_dashboard/schemas/unified_alerts.py +7 -0
  134. truthound_dashboard/schemas/validation.py +0 -13
  135. truthound_dashboard/schemas/validators/base.py +41 -21
  136. truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
  137. truthound_dashboard/schemas/validators/localization_validators.py +273 -0
  138. truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
  139. truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
  140. truthound_dashboard/schemas/validators/referential_validators.py +312 -0
  141. truthound_dashboard/schemas/validators/registry.py +93 -8
  142. truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
  143. truthound_dashboard/schemas/versioning.py +1 -6
  144. truthound_dashboard/static/index.html +2 -2
  145. truthound_dashboard-1.5.1.dist-info/METADATA +312 -0
  146. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.dist-info}/RECORD +149 -148
  147. truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
  148. truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
  149. truthound_dashboard/core/plugins/hooks/manager.py +0 -403
  150. truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
  151. truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
  152. truthound_dashboard/core/reporters/junit_reporter.py +0 -233
  153. truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
  154. truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
  155. truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
  156. truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
  157. truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
  158. truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
  159. truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
  160. truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
  161. truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
  162. truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
  163. truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
  164. truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
  165. truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
  166. truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
  167. truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
  168. truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
  169. truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
  170. truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
  171. truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
  172. truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
  173. truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
  174. truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
  175. truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
  176. truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
  177. truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
  178. truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
  179. truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
  180. truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
  181. truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
  182. truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
  183. truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
  184. truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
  185. truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
  186. truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
  187. truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
  188. truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
  189. truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
  190. truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
  191. truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
  192. truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
  193. truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
  194. truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
  195. truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
  196. truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
  197. truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
  198. truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
  199. truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
  200. truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
  201. truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
  202. truthound_dashboard-1.4.4.dist-info/METADATA +0 -507
  203. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.dist-info}/WHEEL +0 -0
  204. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.dist-info}/entry_points.txt +0 -0
  205. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,367 +0,0 @@
1
- """Hook Decorators.
2
-
3
- This module provides decorators for easily registering
4
- hook handlers from plugin code.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- from functools import wraps
10
- from typing import Any, Callable, TypeVar
11
-
12
- from .protocols import HookContext, HookPriority, HookResult, HookType
13
- from .manager import hook_manager
14
-
15
- F = TypeVar("F", bound=Callable[..., Any])
16
-
17
-
18
- def hook(
19
- hook_type: HookType | str,
20
- priority: HookPriority | int = HookPriority.NORMAL,
21
- conditions: dict[str, Any] | None = None,
22
- plugin_id: str | None = None,
23
- auto_register: bool = True,
24
- ) -> Callable[[F], F]:
25
- """Decorator to register a function as a hook handler.
26
-
27
- Args:
28
- hook_type: Type of hook to register for.
29
- priority: Execution priority.
30
- conditions: Conditions for executing.
31
- plugin_id: ID of the registering plugin.
32
- auto_register: Whether to auto-register with global manager.
33
-
34
- Returns:
35
- Decorator function.
36
-
37
- Example:
38
- @hook(HookType.BEFORE_VALIDATION)
39
- def my_handler(context: HookContext) -> HookResult | None:
40
- # Process the hook
41
- return HookResult(success=True)
42
- """
43
- if isinstance(hook_type, str):
44
- hook_type = HookType(hook_type)
45
-
46
- def decorator(func: F) -> F:
47
- # Store hook metadata on the function
48
- func._hook_type = hook_type # type: ignore
49
- func._hook_priority = priority # type: ignore
50
- func._hook_conditions = conditions or {} # type: ignore
51
- func._hook_plugin_id = plugin_id # type: ignore
52
-
53
- if auto_register:
54
- hook_manager.register(
55
- hook_type=hook_type,
56
- handler=func,
57
- plugin_id=plugin_id,
58
- priority=priority,
59
- conditions=conditions,
60
- handler_name=func.__name__,
61
- )
62
-
63
- return func
64
-
65
- return decorator
66
-
67
-
68
- def before_validation(
69
- priority: HookPriority | int = HookPriority.NORMAL,
70
- conditions: dict[str, Any] | None = None,
71
- plugin_id: str | None = None,
72
- ) -> Callable[[F], F]:
73
- """Decorator for before_validation hooks.
74
-
75
- The handler receives:
76
- - context.data["source_id"]: Data source ID
77
- - context.data["validators"]: List of validators to run
78
- - context.data["config"]: Validation configuration
79
-
80
- The handler can:
81
- - Modify validators list via context.modify("validators", [...])
82
- - Cancel validation via context.cancel()
83
-
84
- Example:
85
- @before_validation()
86
- def add_custom_validator(context: HookContext) -> HookResult | None:
87
- validators = context.get("validators", [])
88
- validators.append("my_custom_validator")
89
- context.modify("validators", validators)
90
- return HookResult(success=True, data={"added": "my_custom_validator"})
91
- """
92
- return hook(
93
- HookType.BEFORE_VALIDATION,
94
- priority=priority,
95
- conditions=conditions,
96
- plugin_id=plugin_id,
97
- )
98
-
99
-
100
- def after_validation(
101
- priority: HookPriority | int = HookPriority.NORMAL,
102
- conditions: dict[str, Any] | None = None,
103
- plugin_id: str | None = None,
104
- ) -> Callable[[F], F]:
105
- """Decorator for after_validation hooks.
106
-
107
- The handler receives:
108
- - context.data["source_id"]: Data source ID
109
- - context.data["result"]: Validation result
110
- - context.data["issues"]: List of validation issues
111
- - context.data["execution_time_ms"]: Execution time
112
-
113
- Example:
114
- @after_validation()
115
- def log_validation_result(context: HookContext) -> HookResult | None:
116
- issues = context.get("issues", [])
117
- if issues:
118
- print(f"Found {len(issues)} issues")
119
- return HookResult(success=True)
120
- """
121
- return hook(
122
- HookType.AFTER_VALIDATION,
123
- priority=priority,
124
- conditions=conditions,
125
- plugin_id=plugin_id,
126
- )
127
-
128
-
129
- def on_issue_found(
130
- priority: HookPriority | int = HookPriority.NORMAL,
131
- conditions: dict[str, Any] | None = None,
132
- plugin_id: str | None = None,
133
- ) -> Callable[[F], F]:
134
- """Decorator for on_issue_found hooks.
135
-
136
- The handler receives:
137
- - context.data["issue"]: The validation issue
138
- - context.data["validator"]: The validator that found it
139
- - context.data["column"]: Column name (if applicable)
140
- - context.data["row_index"]: Row index (if applicable)
141
-
142
- The handler can:
143
- - Modify issue severity via context.modify("issue", {...})
144
- - Suppress issue via context.modify("suppress", True)
145
-
146
- Example:
147
- @on_issue_found()
148
- def filter_known_issues(context: HookContext) -> HookResult | None:
149
- issue = context.get("issue", {})
150
- if "known_pattern" in issue.get("message", ""):
151
- context.modify("suppress", True)
152
- return HookResult(success=True)
153
- """
154
- return hook(
155
- HookType.ON_ISSUE_FOUND,
156
- priority=priority,
157
- conditions=conditions,
158
- plugin_id=plugin_id,
159
- )
160
-
161
-
162
- def before_profile(
163
- priority: HookPriority | int = HookPriority.NORMAL,
164
- conditions: dict[str, Any] | None = None,
165
- plugin_id: str | None = None,
166
- ) -> Callable[[F], F]:
167
- """Decorator for before_profile hooks.
168
-
169
- The handler receives:
170
- - context.data["source_id"]: Data source ID
171
- - context.data["config"]: Profiling configuration
172
- - context.data["columns"]: Columns to profile
173
-
174
- Example:
175
- @before_profile()
176
- def configure_profiling(context: HookContext) -> HookResult | None:
177
- config = context.get("config", {})
178
- config["sample_size"] = 10000
179
- context.modify("config", config)
180
- return HookResult(success=True)
181
- """
182
- return hook(
183
- HookType.BEFORE_PROFILE,
184
- priority=priority,
185
- conditions=conditions,
186
- plugin_id=plugin_id,
187
- )
188
-
189
-
190
- def after_profile(
191
- priority: HookPriority | int = HookPriority.NORMAL,
192
- conditions: dict[str, Any] | None = None,
193
- plugin_id: str | None = None,
194
- ) -> Callable[[F], F]:
195
- """Decorator for after_profile hooks.
196
-
197
- The handler receives:
198
- - context.data["source_id"]: Data source ID
199
- - context.data["profile"]: Profile result
200
- - context.data["execution_time_ms"]: Execution time
201
-
202
- Example:
203
- @after_profile()
204
- def analyze_profile(context: HookContext) -> HookResult | None:
205
- profile = context.get("profile", {})
206
- # Perform additional analysis
207
- return HookResult(success=True, data={"analysis": {...}})
208
- """
209
- return hook(
210
- HookType.AFTER_PROFILE,
211
- priority=priority,
212
- conditions=conditions,
213
- plugin_id=plugin_id,
214
- )
215
-
216
-
217
- def on_report_generate(
218
- priority: HookPriority | int = HookPriority.NORMAL,
219
- conditions: dict[str, Any] | None = None,
220
- plugin_id: str | None = None,
221
- ) -> Callable[[F], F]:
222
- """Decorator for on_report_generate hooks.
223
-
224
- The handler receives:
225
- - context.data["report"]: Report data
226
- - context.data["format"]: Output format (html, pdf, json, etc.)
227
- - context.data["config"]: Report configuration
228
-
229
- The handler can:
230
- - Add sections via context.modify("sections", [...])
231
- - Modify report data via context.modify("report", {...})
232
-
233
- Example:
234
- @on_report_generate()
235
- def add_summary(context: HookContext) -> HookResult | None:
236
- report = context.get("report", {})
237
- report["custom_summary"] = "Generated by plugin"
238
- context.modify("report", report)
239
- return HookResult(success=True)
240
- """
241
- return hook(
242
- HookType.ON_REPORT_GENERATE,
243
- priority=priority,
244
- conditions=conditions,
245
- plugin_id=plugin_id,
246
- )
247
-
248
-
249
- def on_error(
250
- priority: HookPriority | int = HookPriority.NORMAL,
251
- conditions: dict[str, Any] | None = None,
252
- plugin_id: str | None = None,
253
- ) -> Callable[[F], F]:
254
- """Decorator for on_error hooks.
255
-
256
- The handler receives:
257
- - context.data["error"]: The exception
258
- - context.data["error_type"]: Exception type name
259
- - context.data["context"]: Error context (operation, source, etc.)
260
- - context.data["traceback"]: Traceback string
261
-
262
- The handler can:
263
- - Suppress error via context.modify("suppress", True)
264
- - Provide fallback via context.modify("fallback", value)
265
- - Request retry via context.modify("retry", True)
266
-
267
- Example:
268
- @on_error()
269
- def handle_connection_error(context: HookContext) -> HookResult | None:
270
- error_type = context.get("error_type")
271
- if error_type == "ConnectionError":
272
- context.modify("retry", True)
273
- return HookResult(success=True, data={"action": "retry"})
274
- return None
275
- """
276
- return hook(
277
- HookType.ON_ERROR,
278
- priority=priority,
279
- conditions=conditions,
280
- plugin_id=plugin_id,
281
- )
282
-
283
-
284
- class HookRegistrar:
285
- """Helper class for registering multiple hooks from a plugin.
286
-
287
- Example:
288
- class MyPlugin:
289
- def __init__(self):
290
- self.hooks = HookRegistrar("my-plugin")
291
-
292
- def register_hooks(self):
293
- self.hooks.register(
294
- HookType.BEFORE_VALIDATION,
295
- self.before_validation,
296
- )
297
- self.hooks.register(
298
- HookType.AFTER_VALIDATION,
299
- self.after_validation,
300
- )
301
-
302
- def unregister_hooks(self):
303
- self.hooks.unregister_all()
304
- """
305
-
306
- def __init__(self, plugin_id: str) -> None:
307
- """Initialize the registrar.
308
-
309
- Args:
310
- plugin_id: Plugin ID for all registrations.
311
- """
312
- self.plugin_id = plugin_id
313
- self._registration_ids: list[str] = []
314
-
315
- def register(
316
- self,
317
- hook_type: HookType | str,
318
- handler: Callable[[HookContext], HookResult | None],
319
- priority: HookPriority | int = HookPriority.NORMAL,
320
- conditions: dict[str, Any] | None = None,
321
- ) -> str:
322
- """Register a hook handler.
323
-
324
- Args:
325
- hook_type: Type of hook.
326
- handler: Handler function.
327
- priority: Execution priority.
328
- conditions: Conditions for executing.
329
-
330
- Returns:
331
- Registration ID.
332
- """
333
- reg_id = hook_manager.register(
334
- hook_type=hook_type,
335
- handler=handler,
336
- plugin_id=self.plugin_id,
337
- priority=priority,
338
- conditions=conditions,
339
- )
340
- self._registration_ids.append(reg_id)
341
- return reg_id
342
-
343
- def unregister(self, registration_id: str) -> bool:
344
- """Unregister a specific handler.
345
-
346
- Args:
347
- registration_id: Registration ID.
348
-
349
- Returns:
350
- True if successful.
351
- """
352
- if registration_id in self._registration_ids:
353
- self._registration_ids.remove(registration_id)
354
- return hook_manager.unregister(registration_id)
355
-
356
- def unregister_all(self) -> int:
357
- """Unregister all handlers for this plugin.
358
-
359
- Returns:
360
- Number of handlers unregistered.
361
- """
362
- count = 0
363
- for reg_id in list(self._registration_ids):
364
- if hook_manager.unregister(reg_id):
365
- count += 1
366
- self._registration_ids.clear()
367
- return count