truthound 1.0.8__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 (877) hide show
  1. truthound/__init__.py +162 -0
  2. truthound/adapters.py +100 -0
  3. truthound/api.py +365 -0
  4. truthound/audit/__init__.py +248 -0
  5. truthound/audit/core.py +967 -0
  6. truthound/audit/filters.py +620 -0
  7. truthound/audit/formatters.py +707 -0
  8. truthound/audit/logger.py +902 -0
  9. truthound/audit/middleware.py +571 -0
  10. truthound/audit/storage.py +1083 -0
  11. truthound/benchmark/__init__.py +123 -0
  12. truthound/benchmark/base.py +757 -0
  13. truthound/benchmark/comparison.py +635 -0
  14. truthound/benchmark/generators.py +706 -0
  15. truthound/benchmark/reporters.py +718 -0
  16. truthound/benchmark/runner.py +635 -0
  17. truthound/benchmark/scenarios.py +712 -0
  18. truthound/cache.py +252 -0
  19. truthound/checkpoint/__init__.py +136 -0
  20. truthound/checkpoint/actions/__init__.py +164 -0
  21. truthound/checkpoint/actions/base.py +324 -0
  22. truthound/checkpoint/actions/custom.py +234 -0
  23. truthound/checkpoint/actions/discord_notify.py +290 -0
  24. truthound/checkpoint/actions/email_notify.py +405 -0
  25. truthound/checkpoint/actions/github_action.py +406 -0
  26. truthound/checkpoint/actions/opsgenie.py +1499 -0
  27. truthound/checkpoint/actions/pagerduty.py +226 -0
  28. truthound/checkpoint/actions/slack_notify.py +233 -0
  29. truthound/checkpoint/actions/store_result.py +249 -0
  30. truthound/checkpoint/actions/teams_notify.py +1570 -0
  31. truthound/checkpoint/actions/telegram_notify.py +419 -0
  32. truthound/checkpoint/actions/update_docs.py +552 -0
  33. truthound/checkpoint/actions/webhook.py +293 -0
  34. truthound/checkpoint/analytics/__init__.py +147 -0
  35. truthound/checkpoint/analytics/aggregations/__init__.py +23 -0
  36. truthound/checkpoint/analytics/aggregations/rollup.py +481 -0
  37. truthound/checkpoint/analytics/aggregations/time_bucket.py +306 -0
  38. truthound/checkpoint/analytics/analyzers/__init__.py +17 -0
  39. truthound/checkpoint/analytics/analyzers/anomaly.py +386 -0
  40. truthound/checkpoint/analytics/analyzers/base.py +270 -0
  41. truthound/checkpoint/analytics/analyzers/forecast.py +421 -0
  42. truthound/checkpoint/analytics/analyzers/trend.py +314 -0
  43. truthound/checkpoint/analytics/models.py +292 -0
  44. truthound/checkpoint/analytics/protocols.py +549 -0
  45. truthound/checkpoint/analytics/service.py +718 -0
  46. truthound/checkpoint/analytics/stores/__init__.py +16 -0
  47. truthound/checkpoint/analytics/stores/base.py +306 -0
  48. truthound/checkpoint/analytics/stores/memory_store.py +353 -0
  49. truthound/checkpoint/analytics/stores/sqlite_store.py +557 -0
  50. truthound/checkpoint/analytics/stores/timescale_store.py +501 -0
  51. truthound/checkpoint/async_actions.py +794 -0
  52. truthound/checkpoint/async_base.py +708 -0
  53. truthound/checkpoint/async_checkpoint.py +617 -0
  54. truthound/checkpoint/async_runner.py +639 -0
  55. truthound/checkpoint/checkpoint.py +527 -0
  56. truthound/checkpoint/ci/__init__.py +61 -0
  57. truthound/checkpoint/ci/detector.py +355 -0
  58. truthound/checkpoint/ci/reporter.py +436 -0
  59. truthound/checkpoint/ci/templates.py +454 -0
  60. truthound/checkpoint/circuitbreaker/__init__.py +133 -0
  61. truthound/checkpoint/circuitbreaker/breaker.py +542 -0
  62. truthound/checkpoint/circuitbreaker/core.py +252 -0
  63. truthound/checkpoint/circuitbreaker/detection.py +459 -0
  64. truthound/checkpoint/circuitbreaker/middleware.py +389 -0
  65. truthound/checkpoint/circuitbreaker/registry.py +357 -0
  66. truthound/checkpoint/distributed/__init__.py +139 -0
  67. truthound/checkpoint/distributed/backends/__init__.py +35 -0
  68. truthound/checkpoint/distributed/backends/celery_backend.py +503 -0
  69. truthound/checkpoint/distributed/backends/kubernetes_backend.py +696 -0
  70. truthound/checkpoint/distributed/backends/local_backend.py +397 -0
  71. truthound/checkpoint/distributed/backends/ray_backend.py +625 -0
  72. truthound/checkpoint/distributed/base.py +774 -0
  73. truthound/checkpoint/distributed/orchestrator.py +765 -0
  74. truthound/checkpoint/distributed/protocols.py +842 -0
  75. truthound/checkpoint/distributed/registry.py +449 -0
  76. truthound/checkpoint/idempotency/__init__.py +120 -0
  77. truthound/checkpoint/idempotency/core.py +295 -0
  78. truthound/checkpoint/idempotency/fingerprint.py +454 -0
  79. truthound/checkpoint/idempotency/locking.py +604 -0
  80. truthound/checkpoint/idempotency/service.py +592 -0
  81. truthound/checkpoint/idempotency/stores.py +653 -0
  82. truthound/checkpoint/monitoring/__init__.py +134 -0
  83. truthound/checkpoint/monitoring/aggregators/__init__.py +15 -0
  84. truthound/checkpoint/monitoring/aggregators/base.py +372 -0
  85. truthound/checkpoint/monitoring/aggregators/realtime.py +300 -0
  86. truthound/checkpoint/monitoring/aggregators/window.py +493 -0
  87. truthound/checkpoint/monitoring/collectors/__init__.py +17 -0
  88. truthound/checkpoint/monitoring/collectors/base.py +257 -0
  89. truthound/checkpoint/monitoring/collectors/memory_collector.py +617 -0
  90. truthound/checkpoint/monitoring/collectors/prometheus_collector.py +451 -0
  91. truthound/checkpoint/monitoring/collectors/redis_collector.py +518 -0
  92. truthound/checkpoint/monitoring/events.py +410 -0
  93. truthound/checkpoint/monitoring/protocols.py +636 -0
  94. truthound/checkpoint/monitoring/service.py +578 -0
  95. truthound/checkpoint/monitoring/views/__init__.py +17 -0
  96. truthound/checkpoint/monitoring/views/base.py +172 -0
  97. truthound/checkpoint/monitoring/views/queue_view.py +220 -0
  98. truthound/checkpoint/monitoring/views/task_view.py +240 -0
  99. truthound/checkpoint/monitoring/views/worker_view.py +263 -0
  100. truthound/checkpoint/registry.py +337 -0
  101. truthound/checkpoint/runner.py +356 -0
  102. truthound/checkpoint/transaction/__init__.py +133 -0
  103. truthound/checkpoint/transaction/base.py +389 -0
  104. truthound/checkpoint/transaction/compensatable.py +537 -0
  105. truthound/checkpoint/transaction/coordinator.py +576 -0
  106. truthound/checkpoint/transaction/executor.py +622 -0
  107. truthound/checkpoint/transaction/idempotency.py +534 -0
  108. truthound/checkpoint/transaction/saga/__init__.py +143 -0
  109. truthound/checkpoint/transaction/saga/builder.py +584 -0
  110. truthound/checkpoint/transaction/saga/definition.py +515 -0
  111. truthound/checkpoint/transaction/saga/event_store.py +542 -0
  112. truthound/checkpoint/transaction/saga/patterns.py +833 -0
  113. truthound/checkpoint/transaction/saga/runner.py +718 -0
  114. truthound/checkpoint/transaction/saga/state_machine.py +793 -0
  115. truthound/checkpoint/transaction/saga/strategies.py +780 -0
  116. truthound/checkpoint/transaction/saga/testing.py +886 -0
  117. truthound/checkpoint/triggers/__init__.py +58 -0
  118. truthound/checkpoint/triggers/base.py +237 -0
  119. truthound/checkpoint/triggers/event.py +385 -0
  120. truthound/checkpoint/triggers/schedule.py +355 -0
  121. truthound/cli.py +2358 -0
  122. truthound/cli_modules/__init__.py +124 -0
  123. truthound/cli_modules/advanced/__init__.py +45 -0
  124. truthound/cli_modules/advanced/benchmark.py +343 -0
  125. truthound/cli_modules/advanced/docs.py +225 -0
  126. truthound/cli_modules/advanced/lineage.py +209 -0
  127. truthound/cli_modules/advanced/ml.py +320 -0
  128. truthound/cli_modules/advanced/realtime.py +196 -0
  129. truthound/cli_modules/checkpoint/__init__.py +46 -0
  130. truthound/cli_modules/checkpoint/init.py +114 -0
  131. truthound/cli_modules/checkpoint/list.py +71 -0
  132. truthound/cli_modules/checkpoint/run.py +159 -0
  133. truthound/cli_modules/checkpoint/validate.py +67 -0
  134. truthound/cli_modules/common/__init__.py +71 -0
  135. truthound/cli_modules/common/errors.py +414 -0
  136. truthound/cli_modules/common/options.py +419 -0
  137. truthound/cli_modules/common/output.py +507 -0
  138. truthound/cli_modules/common/protocol.py +552 -0
  139. truthound/cli_modules/core/__init__.py +48 -0
  140. truthound/cli_modules/core/check.py +123 -0
  141. truthound/cli_modules/core/compare.py +104 -0
  142. truthound/cli_modules/core/learn.py +57 -0
  143. truthound/cli_modules/core/mask.py +77 -0
  144. truthound/cli_modules/core/profile.py +65 -0
  145. truthound/cli_modules/core/scan.py +61 -0
  146. truthound/cli_modules/profiler/__init__.py +51 -0
  147. truthound/cli_modules/profiler/auto_profile.py +175 -0
  148. truthound/cli_modules/profiler/metadata.py +107 -0
  149. truthound/cli_modules/profiler/suite.py +283 -0
  150. truthound/cli_modules/registry.py +431 -0
  151. truthound/cli_modules/scaffolding/__init__.py +89 -0
  152. truthound/cli_modules/scaffolding/base.py +631 -0
  153. truthound/cli_modules/scaffolding/commands.py +545 -0
  154. truthound/cli_modules/scaffolding/plugins.py +1072 -0
  155. truthound/cli_modules/scaffolding/reporters.py +594 -0
  156. truthound/cli_modules/scaffolding/validators.py +1127 -0
  157. truthound/common/__init__.py +18 -0
  158. truthound/common/resilience/__init__.py +130 -0
  159. truthound/common/resilience/bulkhead.py +266 -0
  160. truthound/common/resilience/circuit_breaker.py +516 -0
  161. truthound/common/resilience/composite.py +332 -0
  162. truthound/common/resilience/config.py +292 -0
  163. truthound/common/resilience/protocols.py +217 -0
  164. truthound/common/resilience/rate_limiter.py +404 -0
  165. truthound/common/resilience/retry.py +341 -0
  166. truthound/datadocs/__init__.py +260 -0
  167. truthound/datadocs/base.py +571 -0
  168. truthound/datadocs/builder.py +761 -0
  169. truthound/datadocs/charts.py +764 -0
  170. truthound/datadocs/dashboard/__init__.py +63 -0
  171. truthound/datadocs/dashboard/app.py +576 -0
  172. truthound/datadocs/dashboard/components.py +584 -0
  173. truthound/datadocs/dashboard/state.py +240 -0
  174. truthound/datadocs/engine/__init__.py +46 -0
  175. truthound/datadocs/engine/context.py +376 -0
  176. truthound/datadocs/engine/pipeline.py +618 -0
  177. truthound/datadocs/engine/registry.py +469 -0
  178. truthound/datadocs/exporters/__init__.py +49 -0
  179. truthound/datadocs/exporters/base.py +198 -0
  180. truthound/datadocs/exporters/html.py +178 -0
  181. truthound/datadocs/exporters/json_exporter.py +253 -0
  182. truthound/datadocs/exporters/markdown.py +284 -0
  183. truthound/datadocs/exporters/pdf.py +392 -0
  184. truthound/datadocs/i18n/__init__.py +86 -0
  185. truthound/datadocs/i18n/catalog.py +960 -0
  186. truthound/datadocs/i18n/formatting.py +505 -0
  187. truthound/datadocs/i18n/loader.py +256 -0
  188. truthound/datadocs/i18n/plurals.py +378 -0
  189. truthound/datadocs/renderers/__init__.py +42 -0
  190. truthound/datadocs/renderers/base.py +401 -0
  191. truthound/datadocs/renderers/custom.py +342 -0
  192. truthound/datadocs/renderers/jinja.py +697 -0
  193. truthound/datadocs/sections.py +736 -0
  194. truthound/datadocs/styles.py +931 -0
  195. truthound/datadocs/themes/__init__.py +101 -0
  196. truthound/datadocs/themes/base.py +336 -0
  197. truthound/datadocs/themes/default.py +417 -0
  198. truthound/datadocs/themes/enterprise.py +419 -0
  199. truthound/datadocs/themes/loader.py +336 -0
  200. truthound/datadocs/themes.py +301 -0
  201. truthound/datadocs/transformers/__init__.py +57 -0
  202. truthound/datadocs/transformers/base.py +268 -0
  203. truthound/datadocs/transformers/enrichers.py +544 -0
  204. truthound/datadocs/transformers/filters.py +447 -0
  205. truthound/datadocs/transformers/i18n.py +468 -0
  206. truthound/datadocs/versioning/__init__.py +62 -0
  207. truthound/datadocs/versioning/diff.py +639 -0
  208. truthound/datadocs/versioning/storage.py +497 -0
  209. truthound/datadocs/versioning/version.py +358 -0
  210. truthound/datasources/__init__.py +223 -0
  211. truthound/datasources/_async_protocols.py +222 -0
  212. truthound/datasources/_protocols.py +159 -0
  213. truthound/datasources/adapters.py +428 -0
  214. truthound/datasources/async_base.py +599 -0
  215. truthound/datasources/async_factory.py +511 -0
  216. truthound/datasources/base.py +516 -0
  217. truthound/datasources/factory.py +433 -0
  218. truthound/datasources/nosql/__init__.py +47 -0
  219. truthound/datasources/nosql/base.py +487 -0
  220. truthound/datasources/nosql/elasticsearch.py +801 -0
  221. truthound/datasources/nosql/mongodb.py +636 -0
  222. truthound/datasources/pandas_optimized.py +582 -0
  223. truthound/datasources/pandas_source.py +216 -0
  224. truthound/datasources/polars_source.py +395 -0
  225. truthound/datasources/spark_source.py +479 -0
  226. truthound/datasources/sql/__init__.py +154 -0
  227. truthound/datasources/sql/base.py +710 -0
  228. truthound/datasources/sql/bigquery.py +410 -0
  229. truthound/datasources/sql/cloud_base.py +199 -0
  230. truthound/datasources/sql/databricks.py +471 -0
  231. truthound/datasources/sql/mysql.py +316 -0
  232. truthound/datasources/sql/oracle.py +427 -0
  233. truthound/datasources/sql/postgresql.py +321 -0
  234. truthound/datasources/sql/redshift.py +479 -0
  235. truthound/datasources/sql/snowflake.py +439 -0
  236. truthound/datasources/sql/sqlite.py +286 -0
  237. truthound/datasources/sql/sqlserver.py +437 -0
  238. truthound/datasources/streaming/__init__.py +47 -0
  239. truthound/datasources/streaming/base.py +350 -0
  240. truthound/datasources/streaming/kafka.py +670 -0
  241. truthound/decorators.py +98 -0
  242. truthound/docs/__init__.py +69 -0
  243. truthound/docs/extractor.py +971 -0
  244. truthound/docs/generator.py +601 -0
  245. truthound/docs/parser.py +1037 -0
  246. truthound/docs/renderer.py +999 -0
  247. truthound/drift/__init__.py +22 -0
  248. truthound/drift/compare.py +189 -0
  249. truthound/drift/detectors.py +464 -0
  250. truthound/drift/report.py +160 -0
  251. truthound/execution/__init__.py +65 -0
  252. truthound/execution/_protocols.py +324 -0
  253. truthound/execution/base.py +576 -0
  254. truthound/execution/distributed/__init__.py +179 -0
  255. truthound/execution/distributed/aggregations.py +731 -0
  256. truthound/execution/distributed/arrow_bridge.py +817 -0
  257. truthound/execution/distributed/base.py +550 -0
  258. truthound/execution/distributed/dask_engine.py +976 -0
  259. truthound/execution/distributed/mixins.py +766 -0
  260. truthound/execution/distributed/protocols.py +756 -0
  261. truthound/execution/distributed/ray_engine.py +1127 -0
  262. truthound/execution/distributed/registry.py +446 -0
  263. truthound/execution/distributed/spark_engine.py +1011 -0
  264. truthound/execution/distributed/validator_adapter.py +682 -0
  265. truthound/execution/pandas_engine.py +401 -0
  266. truthound/execution/polars_engine.py +497 -0
  267. truthound/execution/pushdown/__init__.py +230 -0
  268. truthound/execution/pushdown/ast.py +1550 -0
  269. truthound/execution/pushdown/builder.py +1550 -0
  270. truthound/execution/pushdown/dialects.py +1072 -0
  271. truthound/execution/pushdown/executor.py +829 -0
  272. truthound/execution/pushdown/optimizer.py +1041 -0
  273. truthound/execution/sql_engine.py +518 -0
  274. truthound/infrastructure/__init__.py +189 -0
  275. truthound/infrastructure/audit.py +1515 -0
  276. truthound/infrastructure/config.py +1133 -0
  277. truthound/infrastructure/encryption.py +1132 -0
  278. truthound/infrastructure/logging.py +1503 -0
  279. truthound/infrastructure/metrics.py +1220 -0
  280. truthound/lineage/__init__.py +89 -0
  281. truthound/lineage/base.py +746 -0
  282. truthound/lineage/impact_analysis.py +474 -0
  283. truthound/lineage/integrations/__init__.py +22 -0
  284. truthound/lineage/integrations/openlineage.py +548 -0
  285. truthound/lineage/tracker.py +512 -0
  286. truthound/lineage/visualization/__init__.py +33 -0
  287. truthound/lineage/visualization/protocols.py +145 -0
  288. truthound/lineage/visualization/renderers/__init__.py +20 -0
  289. truthound/lineage/visualization/renderers/cytoscape.py +329 -0
  290. truthound/lineage/visualization/renderers/d3.py +331 -0
  291. truthound/lineage/visualization/renderers/graphviz.py +276 -0
  292. truthound/lineage/visualization/renderers/mermaid.py +308 -0
  293. truthound/maskers.py +113 -0
  294. truthound/ml/__init__.py +124 -0
  295. truthound/ml/anomaly_models/__init__.py +31 -0
  296. truthound/ml/anomaly_models/ensemble.py +362 -0
  297. truthound/ml/anomaly_models/isolation_forest.py +444 -0
  298. truthound/ml/anomaly_models/statistical.py +392 -0
  299. truthound/ml/base.py +1178 -0
  300. truthound/ml/drift_detection/__init__.py +26 -0
  301. truthound/ml/drift_detection/concept.py +381 -0
  302. truthound/ml/drift_detection/distribution.py +361 -0
  303. truthound/ml/drift_detection/feature.py +442 -0
  304. truthound/ml/drift_detection/multivariate.py +495 -0
  305. truthound/ml/monitoring/__init__.py +88 -0
  306. truthound/ml/monitoring/alerting/__init__.py +33 -0
  307. truthound/ml/monitoring/alerting/handlers.py +427 -0
  308. truthound/ml/monitoring/alerting/rules.py +508 -0
  309. truthound/ml/monitoring/collectors/__init__.py +19 -0
  310. truthound/ml/monitoring/collectors/composite.py +105 -0
  311. truthound/ml/monitoring/collectors/drift.py +324 -0
  312. truthound/ml/monitoring/collectors/performance.py +179 -0
  313. truthound/ml/monitoring/collectors/quality.py +369 -0
  314. truthound/ml/monitoring/monitor.py +536 -0
  315. truthound/ml/monitoring/protocols.py +451 -0
  316. truthound/ml/monitoring/stores/__init__.py +15 -0
  317. truthound/ml/monitoring/stores/memory.py +201 -0
  318. truthound/ml/monitoring/stores/prometheus.py +296 -0
  319. truthound/ml/rule_learning/__init__.py +25 -0
  320. truthound/ml/rule_learning/constraint_miner.py +443 -0
  321. truthound/ml/rule_learning/pattern_learner.py +499 -0
  322. truthound/ml/rule_learning/profile_learner.py +462 -0
  323. truthound/multitenancy/__init__.py +326 -0
  324. truthound/multitenancy/core.py +852 -0
  325. truthound/multitenancy/integration.py +597 -0
  326. truthound/multitenancy/isolation.py +630 -0
  327. truthound/multitenancy/manager.py +770 -0
  328. truthound/multitenancy/middleware.py +765 -0
  329. truthound/multitenancy/quota.py +537 -0
  330. truthound/multitenancy/resolvers.py +603 -0
  331. truthound/multitenancy/storage.py +703 -0
  332. truthound/observability/__init__.py +307 -0
  333. truthound/observability/context.py +531 -0
  334. truthound/observability/instrumentation.py +611 -0
  335. truthound/observability/logging.py +887 -0
  336. truthound/observability/metrics.py +1157 -0
  337. truthound/observability/tracing/__init__.py +178 -0
  338. truthound/observability/tracing/baggage.py +310 -0
  339. truthound/observability/tracing/config.py +426 -0
  340. truthound/observability/tracing/exporter.py +787 -0
  341. truthound/observability/tracing/integration.py +1018 -0
  342. truthound/observability/tracing/otel/__init__.py +146 -0
  343. truthound/observability/tracing/otel/adapter.py +982 -0
  344. truthound/observability/tracing/otel/bridge.py +1177 -0
  345. truthound/observability/tracing/otel/compat.py +681 -0
  346. truthound/observability/tracing/otel/config.py +691 -0
  347. truthound/observability/tracing/otel/detection.py +327 -0
  348. truthound/observability/tracing/otel/protocols.py +426 -0
  349. truthound/observability/tracing/processor.py +561 -0
  350. truthound/observability/tracing/propagator.py +757 -0
  351. truthound/observability/tracing/provider.py +569 -0
  352. truthound/observability/tracing/resource.py +515 -0
  353. truthound/observability/tracing/sampler.py +487 -0
  354. truthound/observability/tracing/span.py +676 -0
  355. truthound/plugins/__init__.py +198 -0
  356. truthound/plugins/base.py +599 -0
  357. truthound/plugins/cli.py +680 -0
  358. truthound/plugins/dependencies/__init__.py +42 -0
  359. truthound/plugins/dependencies/graph.py +422 -0
  360. truthound/plugins/dependencies/resolver.py +417 -0
  361. truthound/plugins/discovery.py +379 -0
  362. truthound/plugins/docs/__init__.py +46 -0
  363. truthound/plugins/docs/extractor.py +444 -0
  364. truthound/plugins/docs/renderer.py +499 -0
  365. truthound/plugins/enterprise_manager.py +877 -0
  366. truthound/plugins/examples/__init__.py +19 -0
  367. truthound/plugins/examples/custom_validators.py +317 -0
  368. truthound/plugins/examples/slack_notifier.py +312 -0
  369. truthound/plugins/examples/xml_reporter.py +254 -0
  370. truthound/plugins/hooks.py +558 -0
  371. truthound/plugins/lifecycle/__init__.py +43 -0
  372. truthound/plugins/lifecycle/hot_reload.py +402 -0
  373. truthound/plugins/lifecycle/manager.py +371 -0
  374. truthound/plugins/manager.py +736 -0
  375. truthound/plugins/registry.py +338 -0
  376. truthound/plugins/security/__init__.py +93 -0
  377. truthound/plugins/security/exceptions.py +332 -0
  378. truthound/plugins/security/policies.py +348 -0
  379. truthound/plugins/security/protocols.py +643 -0
  380. truthound/plugins/security/sandbox/__init__.py +45 -0
  381. truthound/plugins/security/sandbox/context.py +158 -0
  382. truthound/plugins/security/sandbox/engines/__init__.py +19 -0
  383. truthound/plugins/security/sandbox/engines/container.py +379 -0
  384. truthound/plugins/security/sandbox/engines/noop.py +144 -0
  385. truthound/plugins/security/sandbox/engines/process.py +336 -0
  386. truthound/plugins/security/sandbox/factory.py +211 -0
  387. truthound/plugins/security/signing/__init__.py +57 -0
  388. truthound/plugins/security/signing/service.py +330 -0
  389. truthound/plugins/security/signing/trust_store.py +368 -0
  390. truthound/plugins/security/signing/verifier.py +459 -0
  391. truthound/plugins/versioning/__init__.py +41 -0
  392. truthound/plugins/versioning/constraints.py +297 -0
  393. truthound/plugins/versioning/resolver.py +329 -0
  394. truthound/profiler/__init__.py +1729 -0
  395. truthound/profiler/_lazy.py +452 -0
  396. truthound/profiler/ab_testing/__init__.py +80 -0
  397. truthound/profiler/ab_testing/analysis.py +449 -0
  398. truthound/profiler/ab_testing/base.py +257 -0
  399. truthound/profiler/ab_testing/experiment.py +395 -0
  400. truthound/profiler/ab_testing/tracking.py +368 -0
  401. truthound/profiler/auto_threshold.py +1170 -0
  402. truthound/profiler/base.py +579 -0
  403. truthound/profiler/cache_patterns.py +911 -0
  404. truthound/profiler/caching.py +1303 -0
  405. truthound/profiler/column_profiler.py +712 -0
  406. truthound/profiler/comparison.py +1007 -0
  407. truthound/profiler/custom_patterns.py +1170 -0
  408. truthound/profiler/dashboard/__init__.py +50 -0
  409. truthound/profiler/dashboard/app.py +476 -0
  410. truthound/profiler/dashboard/components.py +457 -0
  411. truthound/profiler/dashboard/config.py +72 -0
  412. truthound/profiler/distributed/__init__.py +83 -0
  413. truthound/profiler/distributed/base.py +281 -0
  414. truthound/profiler/distributed/dask_backend.py +498 -0
  415. truthound/profiler/distributed/local_backend.py +293 -0
  416. truthound/profiler/distributed/profiler.py +304 -0
  417. truthound/profiler/distributed/ray_backend.py +374 -0
  418. truthound/profiler/distributed/spark_backend.py +375 -0
  419. truthound/profiler/distributed.py +1366 -0
  420. truthound/profiler/enterprise_sampling.py +1065 -0
  421. truthound/profiler/errors.py +488 -0
  422. truthound/profiler/evolution/__init__.py +91 -0
  423. truthound/profiler/evolution/alerts.py +426 -0
  424. truthound/profiler/evolution/changes.py +206 -0
  425. truthound/profiler/evolution/compatibility.py +365 -0
  426. truthound/profiler/evolution/detector.py +372 -0
  427. truthound/profiler/evolution/protocols.py +121 -0
  428. truthound/profiler/generators/__init__.py +48 -0
  429. truthound/profiler/generators/base.py +384 -0
  430. truthound/profiler/generators/ml_rules.py +375 -0
  431. truthound/profiler/generators/pattern_rules.py +384 -0
  432. truthound/profiler/generators/schema_rules.py +267 -0
  433. truthound/profiler/generators/stats_rules.py +324 -0
  434. truthound/profiler/generators/suite_generator.py +857 -0
  435. truthound/profiler/i18n.py +1542 -0
  436. truthound/profiler/incremental.py +554 -0
  437. truthound/profiler/incremental_validation.py +1710 -0
  438. truthound/profiler/integration/__init__.py +73 -0
  439. truthound/profiler/integration/adapters.py +345 -0
  440. truthound/profiler/integration/context.py +371 -0
  441. truthound/profiler/integration/executor.py +527 -0
  442. truthound/profiler/integration/naming.py +75 -0
  443. truthound/profiler/integration/protocols.py +243 -0
  444. truthound/profiler/memory.py +1185 -0
  445. truthound/profiler/migration/__init__.py +60 -0
  446. truthound/profiler/migration/base.py +345 -0
  447. truthound/profiler/migration/manager.py +444 -0
  448. truthound/profiler/migration/v1_0_to_v1_1.py +484 -0
  449. truthound/profiler/ml/__init__.py +73 -0
  450. truthound/profiler/ml/base.py +244 -0
  451. truthound/profiler/ml/classifier.py +507 -0
  452. truthound/profiler/ml/feature_extraction.py +604 -0
  453. truthound/profiler/ml/pretrained.py +448 -0
  454. truthound/profiler/ml_inference.py +1276 -0
  455. truthound/profiler/native_patterns.py +815 -0
  456. truthound/profiler/observability.py +1184 -0
  457. truthound/profiler/process_timeout.py +1566 -0
  458. truthound/profiler/progress.py +568 -0
  459. truthound/profiler/progress_callbacks.py +1734 -0
  460. truthound/profiler/quality.py +1345 -0
  461. truthound/profiler/resilience.py +1180 -0
  462. truthound/profiler/sampled_matcher.py +794 -0
  463. truthound/profiler/sampling.py +1288 -0
  464. truthound/profiler/scheduling/__init__.py +82 -0
  465. truthound/profiler/scheduling/protocols.py +214 -0
  466. truthound/profiler/scheduling/scheduler.py +474 -0
  467. truthound/profiler/scheduling/storage.py +457 -0
  468. truthound/profiler/scheduling/triggers.py +449 -0
  469. truthound/profiler/schema.py +603 -0
  470. truthound/profiler/streaming.py +685 -0
  471. truthound/profiler/streaming_patterns.py +1354 -0
  472. truthound/profiler/suite_cli.py +625 -0
  473. truthound/profiler/suite_config.py +789 -0
  474. truthound/profiler/suite_export.py +1268 -0
  475. truthound/profiler/table_profiler.py +547 -0
  476. truthound/profiler/timeout.py +565 -0
  477. truthound/profiler/validation.py +1532 -0
  478. truthound/profiler/visualization/__init__.py +118 -0
  479. truthound/profiler/visualization/base.py +346 -0
  480. truthound/profiler/visualization/generator.py +1259 -0
  481. truthound/profiler/visualization/plotly_renderer.py +811 -0
  482. truthound/profiler/visualization/renderers.py +669 -0
  483. truthound/profiler/visualization/sections.py +540 -0
  484. truthound/profiler/visualization.py +2122 -0
  485. truthound/profiler/yaml_validation.py +1151 -0
  486. truthound/py.typed +0 -0
  487. truthound/ratelimit/__init__.py +248 -0
  488. truthound/ratelimit/algorithms.py +1108 -0
  489. truthound/ratelimit/core.py +573 -0
  490. truthound/ratelimit/integration.py +532 -0
  491. truthound/ratelimit/limiter.py +663 -0
  492. truthound/ratelimit/middleware.py +700 -0
  493. truthound/ratelimit/policy.py +792 -0
  494. truthound/ratelimit/storage.py +763 -0
  495. truthound/rbac/__init__.py +340 -0
  496. truthound/rbac/core.py +976 -0
  497. truthound/rbac/integration.py +760 -0
  498. truthound/rbac/manager.py +1052 -0
  499. truthound/rbac/middleware.py +842 -0
  500. truthound/rbac/policy.py +954 -0
  501. truthound/rbac/storage.py +878 -0
  502. truthound/realtime/__init__.py +141 -0
  503. truthound/realtime/adapters/__init__.py +43 -0
  504. truthound/realtime/adapters/base.py +533 -0
  505. truthound/realtime/adapters/kafka.py +487 -0
  506. truthound/realtime/adapters/kinesis.py +479 -0
  507. truthound/realtime/adapters/mock.py +243 -0
  508. truthound/realtime/base.py +553 -0
  509. truthound/realtime/factory.py +382 -0
  510. truthound/realtime/incremental.py +660 -0
  511. truthound/realtime/processing/__init__.py +67 -0
  512. truthound/realtime/processing/exactly_once.py +575 -0
  513. truthound/realtime/processing/state.py +547 -0
  514. truthound/realtime/processing/windows.py +647 -0
  515. truthound/realtime/protocols.py +569 -0
  516. truthound/realtime/streaming.py +605 -0
  517. truthound/realtime/testing/__init__.py +32 -0
  518. truthound/realtime/testing/containers.py +615 -0
  519. truthound/realtime/testing/fixtures.py +484 -0
  520. truthound/report.py +280 -0
  521. truthound/reporters/__init__.py +46 -0
  522. truthound/reporters/_protocols.py +30 -0
  523. truthound/reporters/base.py +324 -0
  524. truthound/reporters/ci/__init__.py +66 -0
  525. truthound/reporters/ci/azure.py +436 -0
  526. truthound/reporters/ci/base.py +509 -0
  527. truthound/reporters/ci/bitbucket.py +567 -0
  528. truthound/reporters/ci/circleci.py +547 -0
  529. truthound/reporters/ci/detection.py +364 -0
  530. truthound/reporters/ci/factory.py +182 -0
  531. truthound/reporters/ci/github.py +388 -0
  532. truthound/reporters/ci/gitlab.py +471 -0
  533. truthound/reporters/ci/jenkins.py +525 -0
  534. truthound/reporters/console_reporter.py +299 -0
  535. truthound/reporters/factory.py +211 -0
  536. truthound/reporters/html_reporter.py +524 -0
  537. truthound/reporters/json_reporter.py +256 -0
  538. truthound/reporters/markdown_reporter.py +280 -0
  539. truthound/reporters/sdk/__init__.py +174 -0
  540. truthound/reporters/sdk/builder.py +558 -0
  541. truthound/reporters/sdk/mixins.py +1150 -0
  542. truthound/reporters/sdk/schema.py +1493 -0
  543. truthound/reporters/sdk/templates.py +666 -0
  544. truthound/reporters/sdk/testing.py +968 -0
  545. truthound/scanners.py +170 -0
  546. truthound/scheduling/__init__.py +122 -0
  547. truthound/scheduling/cron.py +1136 -0
  548. truthound/scheduling/presets.py +212 -0
  549. truthound/schema.py +275 -0
  550. truthound/secrets/__init__.py +173 -0
  551. truthound/secrets/base.py +618 -0
  552. truthound/secrets/cloud.py +682 -0
  553. truthound/secrets/integration.py +507 -0
  554. truthound/secrets/manager.py +633 -0
  555. truthound/secrets/oidc/__init__.py +172 -0
  556. truthound/secrets/oidc/base.py +902 -0
  557. truthound/secrets/oidc/credential_provider.py +623 -0
  558. truthound/secrets/oidc/exchangers.py +1001 -0
  559. truthound/secrets/oidc/github/__init__.py +110 -0
  560. truthound/secrets/oidc/github/claims.py +718 -0
  561. truthound/secrets/oidc/github/enhanced_provider.py +693 -0
  562. truthound/secrets/oidc/github/trust_policy.py +742 -0
  563. truthound/secrets/oidc/github/verification.py +723 -0
  564. truthound/secrets/oidc/github/workflow.py +691 -0
  565. truthound/secrets/oidc/providers.py +825 -0
  566. truthound/secrets/providers.py +506 -0
  567. truthound/secrets/resolver.py +495 -0
  568. truthound/stores/__init__.py +177 -0
  569. truthound/stores/backends/__init__.py +18 -0
  570. truthound/stores/backends/_protocols.py +340 -0
  571. truthound/stores/backends/azure_blob.py +530 -0
  572. truthound/stores/backends/concurrent_filesystem.py +915 -0
  573. truthound/stores/backends/connection_pool.py +1365 -0
  574. truthound/stores/backends/database.py +743 -0
  575. truthound/stores/backends/filesystem.py +538 -0
  576. truthound/stores/backends/gcs.py +399 -0
  577. truthound/stores/backends/memory.py +354 -0
  578. truthound/stores/backends/s3.py +434 -0
  579. truthound/stores/backpressure/__init__.py +84 -0
  580. truthound/stores/backpressure/base.py +375 -0
  581. truthound/stores/backpressure/circuit_breaker.py +434 -0
  582. truthound/stores/backpressure/monitor.py +376 -0
  583. truthound/stores/backpressure/strategies.py +677 -0
  584. truthound/stores/base.py +551 -0
  585. truthound/stores/batching/__init__.py +65 -0
  586. truthound/stores/batching/base.py +305 -0
  587. truthound/stores/batching/buffer.py +370 -0
  588. truthound/stores/batching/store.py +248 -0
  589. truthound/stores/batching/writer.py +521 -0
  590. truthound/stores/caching/__init__.py +60 -0
  591. truthound/stores/caching/backends.py +684 -0
  592. truthound/stores/caching/base.py +356 -0
  593. truthound/stores/caching/store.py +305 -0
  594. truthound/stores/compression/__init__.py +193 -0
  595. truthound/stores/compression/adaptive.py +694 -0
  596. truthound/stores/compression/base.py +514 -0
  597. truthound/stores/compression/pipeline.py +868 -0
  598. truthound/stores/compression/providers.py +672 -0
  599. truthound/stores/compression/streaming.py +832 -0
  600. truthound/stores/concurrency/__init__.py +81 -0
  601. truthound/stores/concurrency/atomic.py +556 -0
  602. truthound/stores/concurrency/index.py +775 -0
  603. truthound/stores/concurrency/locks.py +576 -0
  604. truthound/stores/concurrency/manager.py +482 -0
  605. truthound/stores/encryption/__init__.py +297 -0
  606. truthound/stores/encryption/base.py +952 -0
  607. truthound/stores/encryption/keys.py +1191 -0
  608. truthound/stores/encryption/pipeline.py +903 -0
  609. truthound/stores/encryption/providers.py +953 -0
  610. truthound/stores/encryption/streaming.py +950 -0
  611. truthound/stores/expectations.py +227 -0
  612. truthound/stores/factory.py +246 -0
  613. truthound/stores/migration/__init__.py +75 -0
  614. truthound/stores/migration/base.py +480 -0
  615. truthound/stores/migration/manager.py +347 -0
  616. truthound/stores/migration/registry.py +382 -0
  617. truthound/stores/migration/store.py +559 -0
  618. truthound/stores/observability/__init__.py +106 -0
  619. truthound/stores/observability/audit.py +718 -0
  620. truthound/stores/observability/config.py +270 -0
  621. truthound/stores/observability/factory.py +208 -0
  622. truthound/stores/observability/metrics.py +636 -0
  623. truthound/stores/observability/protocols.py +410 -0
  624. truthound/stores/observability/store.py +570 -0
  625. truthound/stores/observability/tracing.py +784 -0
  626. truthound/stores/replication/__init__.py +76 -0
  627. truthound/stores/replication/base.py +260 -0
  628. truthound/stores/replication/monitor.py +269 -0
  629. truthound/stores/replication/store.py +439 -0
  630. truthound/stores/replication/syncer.py +391 -0
  631. truthound/stores/results.py +359 -0
  632. truthound/stores/retention/__init__.py +77 -0
  633. truthound/stores/retention/base.py +378 -0
  634. truthound/stores/retention/policies.py +621 -0
  635. truthound/stores/retention/scheduler.py +279 -0
  636. truthound/stores/retention/store.py +526 -0
  637. truthound/stores/streaming/__init__.py +138 -0
  638. truthound/stores/streaming/base.py +801 -0
  639. truthound/stores/streaming/database.py +984 -0
  640. truthound/stores/streaming/filesystem.py +719 -0
  641. truthound/stores/streaming/reader.py +629 -0
  642. truthound/stores/streaming/s3.py +843 -0
  643. truthound/stores/streaming/writer.py +790 -0
  644. truthound/stores/tiering/__init__.py +108 -0
  645. truthound/stores/tiering/base.py +462 -0
  646. truthound/stores/tiering/manager.py +249 -0
  647. truthound/stores/tiering/policies.py +692 -0
  648. truthound/stores/tiering/store.py +526 -0
  649. truthound/stores/versioning/__init__.py +56 -0
  650. truthound/stores/versioning/base.py +376 -0
  651. truthound/stores/versioning/store.py +660 -0
  652. truthound/stores/versioning/strategies.py +353 -0
  653. truthound/types.py +56 -0
  654. truthound/validators/__init__.py +774 -0
  655. truthound/validators/aggregate/__init__.py +27 -0
  656. truthound/validators/aggregate/central.py +116 -0
  657. truthound/validators/aggregate/extremes.py +116 -0
  658. truthound/validators/aggregate/spread.py +118 -0
  659. truthound/validators/aggregate/sum.py +64 -0
  660. truthound/validators/aggregate/type.py +78 -0
  661. truthound/validators/anomaly/__init__.py +93 -0
  662. truthound/validators/anomaly/base.py +431 -0
  663. truthound/validators/anomaly/ml_based.py +1190 -0
  664. truthound/validators/anomaly/multivariate.py +647 -0
  665. truthound/validators/anomaly/statistical.py +599 -0
  666. truthound/validators/base.py +1089 -0
  667. truthound/validators/business_rule/__init__.py +46 -0
  668. truthound/validators/business_rule/base.py +147 -0
  669. truthound/validators/business_rule/checksum.py +509 -0
  670. truthound/validators/business_rule/financial.py +526 -0
  671. truthound/validators/cache.py +733 -0
  672. truthound/validators/completeness/__init__.py +39 -0
  673. truthound/validators/completeness/conditional.py +73 -0
  674. truthound/validators/completeness/default.py +98 -0
  675. truthound/validators/completeness/empty.py +103 -0
  676. truthound/validators/completeness/nan.py +337 -0
  677. truthound/validators/completeness/null.py +152 -0
  678. truthound/validators/cross_table/__init__.py +17 -0
  679. truthound/validators/cross_table/aggregate.py +333 -0
  680. truthound/validators/cross_table/row_count.py +122 -0
  681. truthound/validators/datetime/__init__.py +29 -0
  682. truthound/validators/datetime/format.py +78 -0
  683. truthound/validators/datetime/freshness.py +269 -0
  684. truthound/validators/datetime/order.py +73 -0
  685. truthound/validators/datetime/parseable.py +185 -0
  686. truthound/validators/datetime/range.py +202 -0
  687. truthound/validators/datetime/timezone.py +69 -0
  688. truthound/validators/distribution/__init__.py +49 -0
  689. truthound/validators/distribution/distribution.py +128 -0
  690. truthound/validators/distribution/monotonic.py +119 -0
  691. truthound/validators/distribution/outlier.py +178 -0
  692. truthound/validators/distribution/quantile.py +80 -0
  693. truthound/validators/distribution/range.py +254 -0
  694. truthound/validators/distribution/set.py +125 -0
  695. truthound/validators/distribution/statistical.py +459 -0
  696. truthound/validators/drift/__init__.py +79 -0
  697. truthound/validators/drift/base.py +427 -0
  698. truthound/validators/drift/multi_feature.py +401 -0
  699. truthound/validators/drift/numeric.py +395 -0
  700. truthound/validators/drift/psi.py +446 -0
  701. truthound/validators/drift/statistical.py +510 -0
  702. truthound/validators/enterprise.py +1658 -0
  703. truthound/validators/geospatial/__init__.py +80 -0
  704. truthound/validators/geospatial/base.py +97 -0
  705. truthound/validators/geospatial/boundary.py +238 -0
  706. truthound/validators/geospatial/coordinate.py +351 -0
  707. truthound/validators/geospatial/distance.py +399 -0
  708. truthound/validators/geospatial/polygon.py +665 -0
  709. truthound/validators/i18n/__init__.py +308 -0
  710. truthound/validators/i18n/bidi.py +571 -0
  711. truthound/validators/i18n/catalogs.py +570 -0
  712. truthound/validators/i18n/dialects.py +763 -0
  713. truthound/validators/i18n/extended_catalogs.py +549 -0
  714. truthound/validators/i18n/formatting.py +1434 -0
  715. truthound/validators/i18n/loader.py +1020 -0
  716. truthound/validators/i18n/messages.py +521 -0
  717. truthound/validators/i18n/plural.py +683 -0
  718. truthound/validators/i18n/protocols.py +855 -0
  719. truthound/validators/i18n/tms.py +1162 -0
  720. truthound/validators/localization/__init__.py +53 -0
  721. truthound/validators/localization/base.py +122 -0
  722. truthound/validators/localization/chinese.py +362 -0
  723. truthound/validators/localization/japanese.py +275 -0
  724. truthound/validators/localization/korean.py +524 -0
  725. truthound/validators/memory/__init__.py +94 -0
  726. truthound/validators/memory/approximate_knn.py +506 -0
  727. truthound/validators/memory/base.py +547 -0
  728. truthound/validators/memory/sgd_online.py +719 -0
  729. truthound/validators/memory/streaming_ecdf.py +753 -0
  730. truthound/validators/ml_feature/__init__.py +54 -0
  731. truthound/validators/ml_feature/base.py +249 -0
  732. truthound/validators/ml_feature/correlation.py +299 -0
  733. truthound/validators/ml_feature/leakage.py +344 -0
  734. truthound/validators/ml_feature/null_impact.py +270 -0
  735. truthound/validators/ml_feature/scale.py +264 -0
  736. truthound/validators/multi_column/__init__.py +89 -0
  737. truthound/validators/multi_column/arithmetic.py +284 -0
  738. truthound/validators/multi_column/base.py +231 -0
  739. truthound/validators/multi_column/comparison.py +273 -0
  740. truthound/validators/multi_column/consistency.py +312 -0
  741. truthound/validators/multi_column/statistical.py +299 -0
  742. truthound/validators/optimization/__init__.py +164 -0
  743. truthound/validators/optimization/aggregation.py +563 -0
  744. truthound/validators/optimization/covariance.py +556 -0
  745. truthound/validators/optimization/geo.py +626 -0
  746. truthound/validators/optimization/graph.py +587 -0
  747. truthound/validators/optimization/orchestrator.py +970 -0
  748. truthound/validators/optimization/profiling.py +1312 -0
  749. truthound/validators/privacy/__init__.py +223 -0
  750. truthound/validators/privacy/base.py +635 -0
  751. truthound/validators/privacy/ccpa.py +670 -0
  752. truthound/validators/privacy/gdpr.py +728 -0
  753. truthound/validators/privacy/global_patterns.py +604 -0
  754. truthound/validators/privacy/plugins.py +867 -0
  755. truthound/validators/profiling/__init__.py +52 -0
  756. truthound/validators/profiling/base.py +175 -0
  757. truthound/validators/profiling/cardinality.py +312 -0
  758. truthound/validators/profiling/entropy.py +391 -0
  759. truthound/validators/profiling/frequency.py +455 -0
  760. truthound/validators/pushdown_support.py +660 -0
  761. truthound/validators/query/__init__.py +91 -0
  762. truthound/validators/query/aggregate.py +346 -0
  763. truthound/validators/query/base.py +246 -0
  764. truthound/validators/query/column.py +249 -0
  765. truthound/validators/query/expression.py +274 -0
  766. truthound/validators/query/result.py +323 -0
  767. truthound/validators/query/row_count.py +264 -0
  768. truthound/validators/referential/__init__.py +80 -0
  769. truthound/validators/referential/base.py +395 -0
  770. truthound/validators/referential/cascade.py +391 -0
  771. truthound/validators/referential/circular.py +563 -0
  772. truthound/validators/referential/foreign_key.py +624 -0
  773. truthound/validators/referential/orphan.py +485 -0
  774. truthound/validators/registry.py +112 -0
  775. truthound/validators/schema/__init__.py +41 -0
  776. truthound/validators/schema/column_count.py +142 -0
  777. truthound/validators/schema/column_exists.py +80 -0
  778. truthound/validators/schema/column_order.py +82 -0
  779. truthound/validators/schema/column_pair.py +85 -0
  780. truthound/validators/schema/column_pair_set.py +195 -0
  781. truthound/validators/schema/column_type.py +94 -0
  782. truthound/validators/schema/multi_column.py +53 -0
  783. truthound/validators/schema/multi_column_aggregate.py +175 -0
  784. truthound/validators/schema/referential.py +274 -0
  785. truthound/validators/schema/table_schema.py +91 -0
  786. truthound/validators/schema_validator.py +219 -0
  787. truthound/validators/sdk/__init__.py +250 -0
  788. truthound/validators/sdk/builder.py +680 -0
  789. truthound/validators/sdk/decorators.py +474 -0
  790. truthound/validators/sdk/enterprise/__init__.py +211 -0
  791. truthound/validators/sdk/enterprise/docs.py +725 -0
  792. truthound/validators/sdk/enterprise/fuzzing.py +659 -0
  793. truthound/validators/sdk/enterprise/licensing.py +709 -0
  794. truthound/validators/sdk/enterprise/manager.py +543 -0
  795. truthound/validators/sdk/enterprise/resources.py +628 -0
  796. truthound/validators/sdk/enterprise/sandbox.py +766 -0
  797. truthound/validators/sdk/enterprise/signing.py +603 -0
  798. truthound/validators/sdk/enterprise/templates.py +865 -0
  799. truthound/validators/sdk/enterprise/versioning.py +659 -0
  800. truthound/validators/sdk/templates.py +757 -0
  801. truthound/validators/sdk/testing.py +807 -0
  802. truthound/validators/security/__init__.py +181 -0
  803. truthound/validators/security/redos/__init__.py +182 -0
  804. truthound/validators/security/redos/core.py +861 -0
  805. truthound/validators/security/redos/cpu_monitor.py +593 -0
  806. truthound/validators/security/redos/cve_database.py +791 -0
  807. truthound/validators/security/redos/ml/__init__.py +155 -0
  808. truthound/validators/security/redos/ml/base.py +785 -0
  809. truthound/validators/security/redos/ml/datasets.py +618 -0
  810. truthound/validators/security/redos/ml/features.py +359 -0
  811. truthound/validators/security/redos/ml/models.py +1000 -0
  812. truthound/validators/security/redos/ml/predictor.py +507 -0
  813. truthound/validators/security/redos/ml/storage.py +632 -0
  814. truthound/validators/security/redos/ml/training.py +571 -0
  815. truthound/validators/security/redos/ml_analyzer.py +937 -0
  816. truthound/validators/security/redos/optimizer.py +674 -0
  817. truthound/validators/security/redos/profiler.py +682 -0
  818. truthound/validators/security/redos/re2_engine.py +709 -0
  819. truthound/validators/security/redos.py +886 -0
  820. truthound/validators/security/sql_security.py +1247 -0
  821. truthound/validators/streaming/__init__.py +126 -0
  822. truthound/validators/streaming/base.py +292 -0
  823. truthound/validators/streaming/completeness.py +210 -0
  824. truthound/validators/streaming/mixin.py +575 -0
  825. truthound/validators/streaming/range.py +308 -0
  826. truthound/validators/streaming/sources.py +846 -0
  827. truthound/validators/string/__init__.py +57 -0
  828. truthound/validators/string/casing.py +158 -0
  829. truthound/validators/string/charset.py +96 -0
  830. truthound/validators/string/format.py +501 -0
  831. truthound/validators/string/json.py +77 -0
  832. truthound/validators/string/json_schema.py +184 -0
  833. truthound/validators/string/length.py +104 -0
  834. truthound/validators/string/like_pattern.py +237 -0
  835. truthound/validators/string/regex.py +202 -0
  836. truthound/validators/string/regex_extended.py +435 -0
  837. truthound/validators/table/__init__.py +88 -0
  838. truthound/validators/table/base.py +78 -0
  839. truthound/validators/table/column_count.py +198 -0
  840. truthound/validators/table/freshness.py +362 -0
  841. truthound/validators/table/row_count.py +251 -0
  842. truthound/validators/table/schema.py +333 -0
  843. truthound/validators/table/size.py +285 -0
  844. truthound/validators/timeout/__init__.py +102 -0
  845. truthound/validators/timeout/advanced/__init__.py +247 -0
  846. truthound/validators/timeout/advanced/circuit_breaker.py +675 -0
  847. truthound/validators/timeout/advanced/prediction.py +773 -0
  848. truthound/validators/timeout/advanced/priority.py +618 -0
  849. truthound/validators/timeout/advanced/redis_backend.py +770 -0
  850. truthound/validators/timeout/advanced/retry.py +721 -0
  851. truthound/validators/timeout/advanced/sampling.py +788 -0
  852. truthound/validators/timeout/advanced/sla.py +661 -0
  853. truthound/validators/timeout/advanced/telemetry.py +804 -0
  854. truthound/validators/timeout/cascade.py +477 -0
  855. truthound/validators/timeout/deadline.py +657 -0
  856. truthound/validators/timeout/degradation.py +525 -0
  857. truthound/validators/timeout/distributed.py +597 -0
  858. truthound/validators/timeseries/__init__.py +89 -0
  859. truthound/validators/timeseries/base.py +326 -0
  860. truthound/validators/timeseries/completeness.py +617 -0
  861. truthound/validators/timeseries/gap.py +485 -0
  862. truthound/validators/timeseries/monotonic.py +310 -0
  863. truthound/validators/timeseries/seasonality.py +422 -0
  864. truthound/validators/timeseries/trend.py +510 -0
  865. truthound/validators/uniqueness/__init__.py +59 -0
  866. truthound/validators/uniqueness/approximate.py +475 -0
  867. truthound/validators/uniqueness/distinct_values.py +253 -0
  868. truthound/validators/uniqueness/duplicate.py +118 -0
  869. truthound/validators/uniqueness/primary_key.py +140 -0
  870. truthound/validators/uniqueness/unique.py +191 -0
  871. truthound/validators/uniqueness/within_record.py +599 -0
  872. truthound/validators/utils.py +756 -0
  873. truthound-1.0.8.dist-info/METADATA +474 -0
  874. truthound-1.0.8.dist-info/RECORD +877 -0
  875. truthound-1.0.8.dist-info/WHEEL +4 -0
  876. truthound-1.0.8.dist-info/entry_points.txt +2 -0
  877. truthound-1.0.8.dist-info/licenses/LICENSE +190 -0
@@ -0,0 +1,252 @@
1
+ """Core types and configuration for Circuit Breaker pattern.
2
+
3
+ This module defines:
4
+ - Circuit states (CLOSED, OPEN, HALF_OPEN)
5
+ - Configuration dataclasses
6
+ - Exception types
7
+ - Metrics and event types
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+ from datetime import datetime
14
+ from enum import Enum
15
+ from typing import Any, Callable
16
+
17
+
18
+ class CircuitState(str, Enum):
19
+ """Circuit breaker states.
20
+
21
+ State transitions:
22
+ CLOSED -> OPEN: When failure threshold is exceeded
23
+ OPEN -> HALF_OPEN: After recovery timeout expires
24
+ HALF_OPEN -> CLOSED: When test calls succeed
25
+ HALF_OPEN -> OPEN: When test calls fail
26
+ """
27
+
28
+ CLOSED = "closed"
29
+ """Normal operation - requests pass through."""
30
+
31
+ OPEN = "open"
32
+ """Circuit tripped - requests fail immediately."""
33
+
34
+ HALF_OPEN = "half_open"
35
+ """Testing recovery - limited requests allowed."""
36
+
37
+
38
+ class FailureDetectionStrategy(str, Enum):
39
+ """Strategy for detecting failures."""
40
+
41
+ CONSECUTIVE = "consecutive"
42
+ """Trip after N consecutive failures."""
43
+
44
+ PERCENTAGE = "percentage"
45
+ """Trip when failure percentage exceeds threshold."""
46
+
47
+ TIME_WINDOW = "time_window"
48
+ """Trip based on failures within a time window."""
49
+
50
+ COMPOSITE = "composite"
51
+ """Combine multiple detection strategies."""
52
+
53
+
54
+ @dataclass
55
+ class CircuitBreakerConfig:
56
+ """Configuration for circuit breaker behavior.
57
+
58
+ Attributes:
59
+ failure_threshold: Number of failures before opening circuit
60
+ recovery_timeout: Seconds to wait before attempting recovery
61
+ half_open_max_calls: Max calls allowed in half-open state
62
+ success_threshold: Successes needed to close from half-open
63
+ detection_strategy: Strategy for detecting failures
64
+ failure_rate_threshold: Failure rate to trip (for percentage strategy)
65
+ time_window_seconds: Window size for time-based detection
66
+ min_calls_in_window: Minimum calls before percentage applies
67
+ excluded_exceptions: Exception types that don't count as failures
68
+ included_exceptions: Only these exceptions count as failures (if set)
69
+ fallback: Optional fallback function when circuit is open
70
+ on_state_change: Callback for state changes
71
+ on_failure: Callback for failures
72
+ on_success: Callback for successes
73
+ """
74
+
75
+ # Thresholds
76
+ failure_threshold: int = 5
77
+ recovery_timeout: float = 30.0
78
+ half_open_max_calls: int = 3
79
+ success_threshold: int = 2
80
+
81
+ # Detection strategy
82
+ detection_strategy: FailureDetectionStrategy = FailureDetectionStrategy.CONSECUTIVE
83
+ failure_rate_threshold: float = 0.5
84
+ time_window_seconds: float = 60.0
85
+ min_calls_in_window: int = 10
86
+
87
+ # Exception filtering
88
+ excluded_exceptions: tuple[type[Exception], ...] = ()
89
+ included_exceptions: tuple[type[Exception], ...] | None = None
90
+
91
+ # Callbacks
92
+ fallback: Callable[..., Any] | None = None
93
+ on_state_change: Callable[[StateChangeEvent], None] | None = None
94
+ on_failure: Callable[[Exception, CircuitBreakerMetrics], None] | None = None
95
+ on_success: Callable[[Any, CircuitBreakerMetrics], None] | None = None
96
+
97
+ def should_count_exception(self, exc: Exception) -> bool:
98
+ """Determine if an exception should count as a failure."""
99
+ if self.excluded_exceptions and isinstance(exc, self.excluded_exceptions):
100
+ return False
101
+ if self.included_exceptions is not None:
102
+ return isinstance(exc, self.included_exceptions)
103
+ return True
104
+
105
+
106
+ class CircuitBreakerError(Exception):
107
+ """Base exception for circuit breaker errors."""
108
+
109
+ def __init__(
110
+ self,
111
+ message: str,
112
+ breaker_name: str | None = None,
113
+ state: CircuitState | None = None,
114
+ ):
115
+ super().__init__(message)
116
+ self.breaker_name = breaker_name
117
+ self.state = state
118
+
119
+
120
+ class CircuitOpenError(CircuitBreakerError):
121
+ """Raised when attempting to call through an open circuit."""
122
+
123
+ def __init__(
124
+ self,
125
+ breaker_name: str,
126
+ remaining_time: float | None = None,
127
+ ):
128
+ message = f"Circuit '{breaker_name}' is open"
129
+ if remaining_time is not None:
130
+ message += f", recovery in {remaining_time:.1f}s"
131
+ super().__init__(message, breaker_name, CircuitState.OPEN)
132
+ self.remaining_time = remaining_time
133
+
134
+
135
+ class CircuitHalfOpenError(CircuitBreakerError):
136
+ """Raised when half-open circuit has reached max test calls."""
137
+
138
+ def __init__(self, breaker_name: str, max_calls: int):
139
+ message = f"Circuit '{breaker_name}' is half-open and at max test calls ({max_calls})"
140
+ super().__init__(message, breaker_name, CircuitState.HALF_OPEN)
141
+ self.max_calls = max_calls
142
+
143
+
144
+ @dataclass
145
+ class CallResult:
146
+ """Result of a call through the circuit breaker.
147
+
148
+ Attributes:
149
+ success: Whether the call succeeded
150
+ result: The return value if successful
151
+ exception: The exception if failed
152
+ duration_ms: Call duration in milliseconds
153
+ timestamp: When the call was made
154
+ from_fallback: Whether result came from fallback
155
+ """
156
+
157
+ success: bool
158
+ result: Any = None
159
+ exception: Exception | None = None
160
+ duration_ms: float = 0.0
161
+ timestamp: datetime = field(default_factory=datetime.now)
162
+ from_fallback: bool = False
163
+
164
+
165
+ @dataclass
166
+ class CircuitBreakerMetrics:
167
+ """Metrics for circuit breaker monitoring.
168
+
169
+ Attributes:
170
+ name: Breaker name
171
+ state: Current state
172
+ total_calls: Total number of calls
173
+ successful_calls: Number of successful calls
174
+ failed_calls: Number of failed calls
175
+ rejected_calls: Calls rejected due to open circuit
176
+ consecutive_failures: Current consecutive failure count
177
+ consecutive_successes: Current consecutive success count
178
+ failure_rate: Current failure rate (0.0 - 1.0)
179
+ last_failure_time: Time of last failure
180
+ last_success_time: Time of last success
181
+ last_state_change_time: Time of last state change
182
+ time_in_current_state_ms: Time spent in current state
183
+ average_response_time_ms: Average call duration
184
+ """
185
+
186
+ name: str
187
+ state: CircuitState
188
+ total_calls: int = 0
189
+ successful_calls: int = 0
190
+ failed_calls: int = 0
191
+ rejected_calls: int = 0
192
+ consecutive_failures: int = 0
193
+ consecutive_successes: int = 0
194
+ failure_rate: float = 0.0
195
+ last_failure_time: datetime | None = None
196
+ last_success_time: datetime | None = None
197
+ last_state_change_time: datetime | None = None
198
+ time_in_current_state_ms: float = 0.0
199
+ average_response_time_ms: float = 0.0
200
+
201
+ @property
202
+ def is_healthy(self) -> bool:
203
+ """Check if circuit is in healthy state."""
204
+ return self.state == CircuitState.CLOSED
205
+
206
+ def to_dict(self) -> dict[str, Any]:
207
+ """Convert to dictionary for serialization."""
208
+ return {
209
+ "name": self.name,
210
+ "state": self.state.value,
211
+ "total_calls": self.total_calls,
212
+ "successful_calls": self.successful_calls,
213
+ "failed_calls": self.failed_calls,
214
+ "rejected_calls": self.rejected_calls,
215
+ "consecutive_failures": self.consecutive_failures,
216
+ "consecutive_successes": self.consecutive_successes,
217
+ "failure_rate": self.failure_rate,
218
+ "last_failure_time": self.last_failure_time.isoformat() if self.last_failure_time else None,
219
+ "last_success_time": self.last_success_time.isoformat() if self.last_success_time else None,
220
+ "last_state_change_time": self.last_state_change_time.isoformat() if self.last_state_change_time else None,
221
+ "time_in_current_state_ms": self.time_in_current_state_ms,
222
+ "average_response_time_ms": self.average_response_time_ms,
223
+ "is_healthy": self.is_healthy,
224
+ }
225
+
226
+
227
+ @dataclass
228
+ class StateChangeEvent:
229
+ """Event emitted when circuit state changes.
230
+
231
+ Attributes:
232
+ breaker_name: Name of the circuit breaker
233
+ from_state: Previous state
234
+ to_state: New state
235
+ timestamp: When the change occurred
236
+ reason: Reason for state change
237
+ metrics: Metrics at time of change
238
+ """
239
+
240
+ breaker_name: str
241
+ from_state: CircuitState
242
+ to_state: CircuitState
243
+ timestamp: datetime = field(default_factory=datetime.now)
244
+ reason: str = ""
245
+ metrics: CircuitBreakerMetrics | None = None
246
+
247
+ def __str__(self) -> str:
248
+ return (
249
+ f"CircuitBreaker '{self.breaker_name}' state change: "
250
+ f"{self.from_state.value} -> {self.to_state.value}"
251
+ f"{f' ({self.reason})' if self.reason else ''}"
252
+ )
@@ -0,0 +1,459 @@
1
+ """Failure detection strategies for Circuit Breaker.
2
+
3
+ This module provides different strategies for determining when
4
+ a circuit should trip (open):
5
+
6
+ - ConsecutiveFailureDetector: Trip after N consecutive failures
7
+ - PercentageFailureDetector: Trip when failure rate exceeds threshold
8
+ - TimeWindowFailureDetector: Trip based on failures within time window
9
+ - CompositeFailureDetector: Combine multiple strategies
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from abc import ABC, abstractmethod
15
+ from collections import deque
16
+ from dataclasses import dataclass, field
17
+ from datetime import datetime, timedelta
18
+ from threading import RLock
19
+ from typing import Protocol, runtime_checkable
20
+
21
+ from truthound.checkpoint.circuitbreaker.core import (
22
+ CircuitBreakerConfig,
23
+ FailureDetectionStrategy,
24
+ )
25
+
26
+
27
+ @runtime_checkable
28
+ class FailureDetector(Protocol):
29
+ """Protocol for failure detection strategies."""
30
+
31
+ def record_success(self) -> None:
32
+ """Record a successful call."""
33
+ ...
34
+
35
+ def record_failure(self, exception: Exception | None = None) -> None:
36
+ """Record a failed call."""
37
+ ...
38
+
39
+ def should_trip(self) -> bool:
40
+ """Determine if circuit should trip (open)."""
41
+ ...
42
+
43
+ def reset(self) -> None:
44
+ """Reset detector state."""
45
+ ...
46
+
47
+ @property
48
+ def failure_count(self) -> int:
49
+ """Current failure count."""
50
+ ...
51
+
52
+ @property
53
+ def success_count(self) -> int:
54
+ """Current success count."""
55
+ ...
56
+
57
+
58
+ class BaseFailureDetector(ABC):
59
+ """Base class for failure detectors with common functionality."""
60
+
61
+ def __init__(self):
62
+ self._lock = RLock()
63
+ self._failures = 0
64
+ self._successes = 0
65
+
66
+ @abstractmethod
67
+ def record_success(self) -> None:
68
+ """Record a successful call."""
69
+ pass
70
+
71
+ @abstractmethod
72
+ def record_failure(self, exception: Exception | None = None) -> None:
73
+ """Record a failed call."""
74
+ pass
75
+
76
+ @abstractmethod
77
+ def should_trip(self) -> bool:
78
+ """Determine if circuit should trip."""
79
+ pass
80
+
81
+ def reset(self) -> None:
82
+ """Reset detector state."""
83
+ with self._lock:
84
+ self._failures = 0
85
+ self._successes = 0
86
+
87
+ @property
88
+ def failure_count(self) -> int:
89
+ """Current failure count."""
90
+ return self._failures
91
+
92
+ @property
93
+ def success_count(self) -> int:
94
+ """Current success count."""
95
+ return self._successes
96
+
97
+
98
+ class ConsecutiveFailureDetector(BaseFailureDetector):
99
+ """Trips after N consecutive failures.
100
+
101
+ This is the simplest detection strategy. The circuit trips when
102
+ a specified number of consecutive failures occur. A single success
103
+ resets the counter.
104
+
105
+ Example:
106
+ >>> detector = ConsecutiveFailureDetector(threshold=3)
107
+ >>> detector.record_failure() # count: 1
108
+ >>> detector.record_failure() # count: 2
109
+ >>> detector.should_trip() # False
110
+ >>> detector.record_failure() # count: 3
111
+ >>> detector.should_trip() # True
112
+ """
113
+
114
+ def __init__(self, threshold: int = 5):
115
+ """Initialize detector.
116
+
117
+ Args:
118
+ threshold: Number of consecutive failures to trip
119
+ """
120
+ super().__init__()
121
+ self._threshold = threshold
122
+ self._consecutive_failures = 0
123
+
124
+ def record_success(self) -> None:
125
+ """Record success and reset consecutive failures."""
126
+ with self._lock:
127
+ self._successes += 1
128
+ self._consecutive_failures = 0
129
+
130
+ def record_failure(self, exception: Exception | None = None) -> None:
131
+ """Record failure and increment consecutive counter."""
132
+ with self._lock:
133
+ self._failures += 1
134
+ self._consecutive_failures += 1
135
+
136
+ def should_trip(self) -> bool:
137
+ """Trip if consecutive failures >= threshold."""
138
+ with self._lock:
139
+ return self._consecutive_failures >= self._threshold
140
+
141
+ def reset(self) -> None:
142
+ """Reset all counters."""
143
+ with self._lock:
144
+ super().reset()
145
+ self._consecutive_failures = 0
146
+
147
+ @property
148
+ def consecutive_failures(self) -> int:
149
+ """Current consecutive failure count."""
150
+ return self._consecutive_failures
151
+
152
+
153
+ class PercentageFailureDetector(BaseFailureDetector):
154
+ """Trips when failure percentage exceeds threshold.
155
+
156
+ This strategy considers the overall failure rate rather than
157
+ consecutive failures. Useful for services with occasional
158
+ transient errors.
159
+
160
+ Example:
161
+ >>> detector = PercentageFailureDetector(
162
+ ... threshold=0.5, # 50%
163
+ ... min_calls=10,
164
+ ... )
165
+ >>> for _ in range(6):
166
+ ... detector.record_failure()
167
+ >>> for _ in range(4):
168
+ ... detector.record_success()
169
+ >>> detector.should_trip() # True (60% failure rate)
170
+ """
171
+
172
+ def __init__(
173
+ self,
174
+ threshold: float = 0.5,
175
+ min_calls: int = 10,
176
+ window_size: int | None = None,
177
+ ):
178
+ """Initialize detector.
179
+
180
+ Args:
181
+ threshold: Failure rate (0.0-1.0) to trip
182
+ min_calls: Minimum calls before percentage applies
183
+ window_size: If set, only consider last N calls
184
+ """
185
+ super().__init__()
186
+ self._threshold = threshold
187
+ self._min_calls = min_calls
188
+ self._window_size = window_size
189
+ self._calls: deque[bool] = deque(maxlen=window_size)
190
+
191
+ def record_success(self) -> None:
192
+ """Record success."""
193
+ with self._lock:
194
+ self._successes += 1
195
+ self._calls.append(True)
196
+
197
+ def record_failure(self, exception: Exception | None = None) -> None:
198
+ """Record failure."""
199
+ with self._lock:
200
+ self._failures += 1
201
+ self._calls.append(False)
202
+
203
+ def should_trip(self) -> bool:
204
+ """Trip if failure rate >= threshold and min calls met."""
205
+ with self._lock:
206
+ total = len(self._calls) if self._window_size else (self._successes + self._failures)
207
+ if total < self._min_calls:
208
+ return False
209
+
210
+ if self._window_size:
211
+ failures = sum(1 for success in self._calls if not success)
212
+ rate = failures / total
213
+ else:
214
+ rate = self._failures / total if total > 0 else 0.0
215
+
216
+ return rate >= self._threshold
217
+
218
+ def reset(self) -> None:
219
+ """Reset all counters."""
220
+ with self._lock:
221
+ super().reset()
222
+ self._calls.clear()
223
+
224
+ @property
225
+ def failure_rate(self) -> float:
226
+ """Current failure rate."""
227
+ with self._lock:
228
+ if self._window_size:
229
+ total = len(self._calls)
230
+ if total == 0:
231
+ return 0.0
232
+ failures = sum(1 for success in self._calls if not success)
233
+ return failures / total
234
+ else:
235
+ total = self._successes + self._failures
236
+ return self._failures / total if total > 0 else 0.0
237
+
238
+
239
+ @dataclass
240
+ class TimestampedCall:
241
+ """A call with timestamp for time-window tracking."""
242
+
243
+ success: bool
244
+ timestamp: datetime = field(default_factory=datetime.now)
245
+ exception_type: str | None = None
246
+
247
+
248
+ class TimeWindowFailureDetector(BaseFailureDetector):
249
+ """Trips based on failures within a sliding time window.
250
+
251
+ This strategy only considers recent calls, allowing the circuit
252
+ to recover naturally as old failures age out of the window.
253
+
254
+ Example:
255
+ >>> detector = TimeWindowFailureDetector(
256
+ ... threshold=5,
257
+ ... window_seconds=60.0,
258
+ ... )
259
+ >>> # 5 failures within 60 seconds will trip
260
+ """
261
+
262
+ def __init__(
263
+ self,
264
+ threshold: int = 5,
265
+ window_seconds: float = 60.0,
266
+ use_percentage: bool = False,
267
+ percentage_threshold: float = 0.5,
268
+ min_calls: int = 10,
269
+ ):
270
+ """Initialize detector.
271
+
272
+ Args:
273
+ threshold: Number of failures to trip (absolute mode)
274
+ window_seconds: Time window size in seconds
275
+ use_percentage: Use percentage mode instead of absolute
276
+ percentage_threshold: Failure rate to trip (percentage mode)
277
+ min_calls: Minimum calls for percentage mode
278
+ """
279
+ super().__init__()
280
+ self._threshold = threshold
281
+ self._window_seconds = window_seconds
282
+ self._use_percentage = use_percentage
283
+ self._percentage_threshold = percentage_threshold
284
+ self._min_calls = min_calls
285
+ self._calls: list[TimestampedCall] = []
286
+
287
+ def _cleanup_old_calls(self) -> None:
288
+ """Remove calls outside the time window."""
289
+ cutoff = datetime.now() - timedelta(seconds=self._window_seconds)
290
+ self._calls = [c for c in self._calls if c.timestamp >= cutoff]
291
+
292
+ def record_success(self) -> None:
293
+ """Record success."""
294
+ with self._lock:
295
+ self._successes += 1
296
+ self._calls.append(TimestampedCall(success=True))
297
+ self._cleanup_old_calls()
298
+
299
+ def record_failure(self, exception: Exception | None = None) -> None:
300
+ """Record failure."""
301
+ with self._lock:
302
+ self._failures += 1
303
+ exc_type = type(exception).__name__ if exception else None
304
+ self._calls.append(TimestampedCall(success=False, exception_type=exc_type))
305
+ self._cleanup_old_calls()
306
+
307
+ def should_trip(self) -> bool:
308
+ """Trip based on failures in time window."""
309
+ with self._lock:
310
+ self._cleanup_old_calls()
311
+
312
+ failures_in_window = sum(1 for c in self._calls if not c.success)
313
+ total_in_window = len(self._calls)
314
+
315
+ if self._use_percentage:
316
+ if total_in_window < self._min_calls:
317
+ return False
318
+ rate = failures_in_window / total_in_window
319
+ return rate >= self._percentage_threshold
320
+ else:
321
+ return failures_in_window >= self._threshold
322
+
323
+ def reset(self) -> None:
324
+ """Reset all counters."""
325
+ with self._lock:
326
+ super().reset()
327
+ self._calls.clear()
328
+
329
+ @property
330
+ def calls_in_window(self) -> int:
331
+ """Number of calls in current window."""
332
+ with self._lock:
333
+ self._cleanup_old_calls()
334
+ return len(self._calls)
335
+
336
+ @property
337
+ def failures_in_window(self) -> int:
338
+ """Number of failures in current window."""
339
+ with self._lock:
340
+ self._cleanup_old_calls()
341
+ return sum(1 for c in self._calls if not c.success)
342
+
343
+
344
+ class CompositeFailureDetector(BaseFailureDetector):
345
+ """Combines multiple detection strategies.
346
+
347
+ This detector can use either AND or OR logic to combine
348
+ multiple detection strategies.
349
+
350
+ Example:
351
+ >>> detector = CompositeFailureDetector(
352
+ ... detectors=[
353
+ ... ConsecutiveFailureDetector(threshold=3),
354
+ ... PercentageFailureDetector(threshold=0.5),
355
+ ... ],
356
+ ... require_all=False, # OR logic
357
+ ... )
358
+ """
359
+
360
+ def __init__(
361
+ self,
362
+ detectors: list[FailureDetector],
363
+ require_all: bool = False,
364
+ ):
365
+ """Initialize composite detector.
366
+
367
+ Args:
368
+ detectors: List of detectors to combine
369
+ require_all: If True, all must trip (AND). If False, any trips (OR)
370
+ """
371
+ super().__init__()
372
+ self._detectors = detectors
373
+ self._require_all = require_all
374
+
375
+ def record_success(self) -> None:
376
+ """Record success on all detectors."""
377
+ with self._lock:
378
+ self._successes += 1
379
+ for detector in self._detectors:
380
+ detector.record_success()
381
+
382
+ def record_failure(self, exception: Exception | None = None) -> None:
383
+ """Record failure on all detectors."""
384
+ with self._lock:
385
+ self._failures += 1
386
+ for detector in self._detectors:
387
+ detector.record_failure(exception)
388
+
389
+ def should_trip(self) -> bool:
390
+ """Trip based on combined detector results."""
391
+ with self._lock:
392
+ if self._require_all:
393
+ return all(d.should_trip() for d in self._detectors)
394
+ else:
395
+ return any(d.should_trip() for d in self._detectors)
396
+
397
+ def reset(self) -> None:
398
+ """Reset all detectors."""
399
+ with self._lock:
400
+ super().reset()
401
+ for detector in self._detectors:
402
+ detector.reset()
403
+
404
+ @property
405
+ def detector_states(self) -> list[dict]:
406
+ """Get state of all detectors."""
407
+ return [
408
+ {
409
+ "type": type(d).__name__,
410
+ "should_trip": d.should_trip(),
411
+ "failure_count": d.failure_count,
412
+ "success_count": d.success_count,
413
+ }
414
+ for d in self._detectors
415
+ ]
416
+
417
+
418
+ def create_detector(config: CircuitBreakerConfig) -> FailureDetector:
419
+ """Factory function to create appropriate detector from config.
420
+
421
+ Args:
422
+ config: Circuit breaker configuration
423
+
424
+ Returns:
425
+ Appropriate failure detector based on config.detection_strategy
426
+ """
427
+ strategy = config.detection_strategy
428
+
429
+ if strategy == FailureDetectionStrategy.CONSECUTIVE:
430
+ return ConsecutiveFailureDetector(threshold=config.failure_threshold)
431
+
432
+ elif strategy == FailureDetectionStrategy.PERCENTAGE:
433
+ return PercentageFailureDetector(
434
+ threshold=config.failure_rate_threshold,
435
+ min_calls=config.min_calls_in_window,
436
+ )
437
+
438
+ elif strategy == FailureDetectionStrategy.TIME_WINDOW:
439
+ return TimeWindowFailureDetector(
440
+ threshold=config.failure_threshold,
441
+ window_seconds=config.time_window_seconds,
442
+ )
443
+
444
+ elif strategy == FailureDetectionStrategy.COMPOSITE:
445
+ # Create a composite with both consecutive and percentage
446
+ return CompositeFailureDetector(
447
+ detectors=[
448
+ ConsecutiveFailureDetector(threshold=config.failure_threshold),
449
+ PercentageFailureDetector(
450
+ threshold=config.failure_rate_threshold,
451
+ min_calls=config.min_calls_in_window,
452
+ ),
453
+ ],
454
+ require_all=False,
455
+ )
456
+
457
+ else:
458
+ # Default to consecutive
459
+ return ConsecutiveFailureDetector(threshold=config.failure_threshold)