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,765 @@
1
+ """Distributed Checkpoint Orchestrator.
2
+
3
+ This module provides the main orchestrator class for distributed
4
+ checkpoint execution with advanced features like scheduling,
5
+ rate limiting, and circuit breaker support.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import logging
12
+ import threading
13
+ import time
14
+ from collections import defaultdict
15
+ from dataclasses import dataclass, field
16
+ from datetime import datetime, timedelta
17
+ from typing import TYPE_CHECKING, Any, Callable, Iterator
18
+ from queue import PriorityQueue, Empty
19
+ from uuid import uuid4
20
+
21
+ from truthound.checkpoint.distributed.base import (
22
+ BaseDistributedBackend,
23
+ BaseDistributedOrchestrator,
24
+ TaskMetrics,
25
+ )
26
+ from truthound.checkpoint.distributed.protocols import (
27
+ BackendCapability,
28
+ ClusterState,
29
+ DistributedConfig,
30
+ DistributedError,
31
+ DistributedTask,
32
+ DistributedTaskProtocol,
33
+ DistributedTaskResult,
34
+ TaskCancelledError,
35
+ TaskPriority,
36
+ TaskState,
37
+ TaskSubmissionError,
38
+ TaskTimeoutError,
39
+ WorkerInfo,
40
+ WorkerNotAvailableError,
41
+ )
42
+
43
+ if TYPE_CHECKING:
44
+ from truthound.checkpoint.checkpoint import Checkpoint, CheckpointResult
45
+
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+
50
+ # =============================================================================
51
+ # Rate Limiting
52
+ # =============================================================================
53
+
54
+
55
+ @dataclass
56
+ class RateLimitConfig:
57
+ """Configuration for rate limiting.
58
+
59
+ Attributes:
60
+ max_tasks_per_second: Maximum tasks per second.
61
+ max_tasks_per_minute: Maximum tasks per minute.
62
+ max_concurrent_tasks: Maximum concurrent tasks.
63
+ burst_limit: Allow burst up to this limit.
64
+ """
65
+
66
+ max_tasks_per_second: float = 10.0
67
+ max_tasks_per_minute: float = 100.0
68
+ max_concurrent_tasks: int = 50
69
+ burst_limit: int = 20
70
+
71
+
72
+ class RateLimiter:
73
+ """Token bucket rate limiter for task submission."""
74
+
75
+ def __init__(self, config: RateLimitConfig) -> None:
76
+ self._config = config
77
+ self._lock = threading.RLock()
78
+ self._tokens = config.burst_limit
79
+ self._last_update = time.time()
80
+ self._concurrent_count = 0
81
+ self._minute_count = 0
82
+ self._minute_start = time.time()
83
+
84
+ def acquire(self, timeout: float = 10.0) -> bool:
85
+ """Acquire permission to submit a task.
86
+
87
+ Args:
88
+ timeout: Maximum time to wait.
89
+
90
+ Returns:
91
+ True if acquired, False if timeout.
92
+ """
93
+ deadline = time.time() + timeout
94
+
95
+ while time.time() < deadline:
96
+ with self._lock:
97
+ self._refill_tokens()
98
+ self._reset_minute_if_needed()
99
+
100
+ if (
101
+ self._tokens >= 1
102
+ and self._concurrent_count < self._config.max_concurrent_tasks
103
+ and self._minute_count < self._config.max_tasks_per_minute
104
+ ):
105
+ self._tokens -= 1
106
+ self._concurrent_count += 1
107
+ self._minute_count += 1
108
+ return True
109
+
110
+ time.sleep(0.1)
111
+
112
+ return False
113
+
114
+ def release(self) -> None:
115
+ """Release a concurrent task slot."""
116
+ with self._lock:
117
+ self._concurrent_count = max(0, self._concurrent_count - 1)
118
+
119
+ def _refill_tokens(self) -> None:
120
+ """Refill tokens based on elapsed time."""
121
+ now = time.time()
122
+ elapsed = now - self._last_update
123
+ refill = elapsed * self._config.max_tasks_per_second
124
+ self._tokens = min(self._config.burst_limit, self._tokens + refill)
125
+ self._last_update = now
126
+
127
+ def _reset_minute_if_needed(self) -> None:
128
+ """Reset minute counter if a minute has passed."""
129
+ now = time.time()
130
+ if now - self._minute_start >= 60:
131
+ self._minute_count = 0
132
+ self._minute_start = now
133
+
134
+
135
+ # =============================================================================
136
+ # Circuit Breaker
137
+ # =============================================================================
138
+
139
+
140
+ class CircuitBreakerState:
141
+ """Circuit breaker states."""
142
+
143
+ CLOSED = "closed"
144
+ OPEN = "open"
145
+ HALF_OPEN = "half_open"
146
+
147
+
148
+ @dataclass
149
+ class CircuitBreakerConfig:
150
+ """Configuration for circuit breaker.
151
+
152
+ Attributes:
153
+ failure_threshold: Number of failures to open circuit.
154
+ success_threshold: Number of successes to close circuit.
155
+ timeout_seconds: Time before half-open from open.
156
+ half_open_max_calls: Max calls in half-open state.
157
+ """
158
+
159
+ failure_threshold: int = 5
160
+ success_threshold: int = 3
161
+ timeout_seconds: float = 60.0
162
+ half_open_max_calls: int = 3
163
+
164
+
165
+ class CircuitBreaker:
166
+ """Circuit breaker for distributed task submission."""
167
+
168
+ def __init__(self, config: CircuitBreakerConfig | None = None) -> None:
169
+ self._config = config or CircuitBreakerConfig()
170
+ self._lock = threading.RLock()
171
+ self._state = CircuitBreakerState.CLOSED
172
+ self._failure_count = 0
173
+ self._success_count = 0
174
+ self._last_failure_time: datetime | None = None
175
+ self._half_open_calls = 0
176
+
177
+ @property
178
+ def state(self) -> str:
179
+ """Get current state."""
180
+ with self._lock:
181
+ return self._state
182
+
183
+ @property
184
+ def is_closed(self) -> bool:
185
+ """Check if circuit is closed (allowing requests)."""
186
+ return self.state == CircuitBreakerState.CLOSED
187
+
188
+ def can_execute(self) -> bool:
189
+ """Check if execution is allowed."""
190
+ with self._lock:
191
+ if self._state == CircuitBreakerState.CLOSED:
192
+ return True
193
+
194
+ if self._state == CircuitBreakerState.OPEN:
195
+ # Check if timeout has passed
196
+ if self._last_failure_time:
197
+ elapsed = (datetime.now() - self._last_failure_time).total_seconds()
198
+ if elapsed >= self._config.timeout_seconds:
199
+ self._state = CircuitBreakerState.HALF_OPEN
200
+ self._half_open_calls = 0
201
+ logger.info("Circuit breaker transitioning to HALF_OPEN")
202
+ return True
203
+ return False
204
+
205
+ if self._state == CircuitBreakerState.HALF_OPEN:
206
+ if self._half_open_calls < self._config.half_open_max_calls:
207
+ self._half_open_calls += 1
208
+ return True
209
+ return False
210
+
211
+ return False
212
+
213
+ def record_success(self) -> None:
214
+ """Record a successful execution."""
215
+ with self._lock:
216
+ if self._state == CircuitBreakerState.HALF_OPEN:
217
+ self._success_count += 1
218
+ if self._success_count >= self._config.success_threshold:
219
+ self._state = CircuitBreakerState.CLOSED
220
+ self._failure_count = 0
221
+ self._success_count = 0
222
+ logger.info("Circuit breaker CLOSED after successful recovery")
223
+ elif self._state == CircuitBreakerState.CLOSED:
224
+ # Reset failure count on success
225
+ self._failure_count = 0
226
+
227
+ def record_failure(self) -> None:
228
+ """Record a failed execution."""
229
+ with self._lock:
230
+ self._failure_count += 1
231
+ self._last_failure_time = datetime.now()
232
+
233
+ if self._state == CircuitBreakerState.HALF_OPEN:
234
+ # Any failure in half-open goes back to open
235
+ self._state = CircuitBreakerState.OPEN
236
+ self._success_count = 0
237
+ logger.warning("Circuit breaker OPEN after failure in HALF_OPEN")
238
+
239
+ elif self._state == CircuitBreakerState.CLOSED:
240
+ if self._failure_count >= self._config.failure_threshold:
241
+ self._state = CircuitBreakerState.OPEN
242
+ logger.warning(
243
+ f"Circuit breaker OPEN after {self._failure_count} failures"
244
+ )
245
+
246
+ def reset(self) -> None:
247
+ """Reset the circuit breaker."""
248
+ with self._lock:
249
+ self._state = CircuitBreakerState.CLOSED
250
+ self._failure_count = 0
251
+ self._success_count = 0
252
+ self._last_failure_time = None
253
+ self._half_open_calls = 0
254
+
255
+
256
+ # =============================================================================
257
+ # Scheduled Task
258
+ # =============================================================================
259
+
260
+
261
+ @dataclass
262
+ class ScheduledTask:
263
+ """A task scheduled for future execution.
264
+
265
+ Attributes:
266
+ task_id: Unique task identifier.
267
+ checkpoint: The checkpoint to execute.
268
+ scheduled_time: When to execute.
269
+ priority: Task priority.
270
+ context: Execution context.
271
+ recurring: If True, reschedule after execution.
272
+ interval_seconds: Interval for recurring tasks.
273
+ """
274
+
275
+ task_id: str
276
+ checkpoint: "Checkpoint"
277
+ scheduled_time: datetime
278
+ priority: TaskPriority = TaskPriority.NORMAL
279
+ context: dict[str, Any] = field(default_factory=dict)
280
+ recurring: bool = False
281
+ interval_seconds: float = 0.0
282
+
283
+ def __lt__(self, other: "ScheduledTask") -> bool:
284
+ """Compare by scheduled time for priority queue."""
285
+ return self.scheduled_time < other.scheduled_time
286
+
287
+
288
+ # =============================================================================
289
+ # Main Orchestrator
290
+ # =============================================================================
291
+
292
+
293
+ class DistributedCheckpointOrchestrator(BaseDistributedOrchestrator):
294
+ """Advanced orchestrator for distributed checkpoint execution.
295
+
296
+ This orchestrator provides additional features beyond the base class:
297
+ - Rate limiting for task submission
298
+ - Circuit breaker for failure protection
299
+ - Task scheduling for future execution
300
+ - Checkpoint groups for logical organization
301
+ - Result aggregation and reporting
302
+
303
+ Example:
304
+ >>> from truthound.checkpoint.distributed import (
305
+ ... DistributedCheckpointOrchestrator,
306
+ ... get_backend,
307
+ ... )
308
+ >>>
309
+ >>> # Create orchestrator with backend
310
+ >>> backend = get_backend("celery", broker_url="redis://localhost:6379")
311
+ >>> orchestrator = DistributedCheckpointOrchestrator(backend)
312
+ >>>
313
+ >>> with orchestrator:
314
+ ... # Submit tasks with rate limiting
315
+ ... task = orchestrator.submit(checkpoint, priority=7)
316
+ ...
317
+ ... # Schedule for future execution
318
+ ... scheduled = orchestrator.schedule(
319
+ ... checkpoint,
320
+ ... delay_seconds=300,
321
+ ... recurring=True,
322
+ ... interval_seconds=3600,
323
+ ... )
324
+ ...
325
+ ... # Submit a group of checkpoints
326
+ ... group_tasks = orchestrator.submit_group(
327
+ ... "daily_validations",
328
+ ... [cp1, cp2, cp3],
329
+ ... )
330
+ ...
331
+ ... # Wait for results
332
+ ... results = orchestrator.gather(group_tasks)
333
+ """
334
+
335
+ def __init__(
336
+ self,
337
+ backend: BaseDistributedBackend,
338
+ config: DistributedConfig | None = None,
339
+ rate_limit_config: RateLimitConfig | None = None,
340
+ circuit_breaker_config: CircuitBreakerConfig | None = None,
341
+ ) -> None:
342
+ """Initialize the orchestrator.
343
+
344
+ Args:
345
+ backend: The distributed backend to use.
346
+ config: Distributed configuration.
347
+ rate_limit_config: Rate limiting configuration.
348
+ circuit_breaker_config: Circuit breaker configuration.
349
+ """
350
+ super().__init__(backend, config)
351
+
352
+ self._rate_limiter = RateLimiter(rate_limit_config or RateLimitConfig())
353
+ self._circuit_breaker = CircuitBreaker(circuit_breaker_config)
354
+
355
+ # Scheduling
356
+ self._scheduled_tasks: PriorityQueue[ScheduledTask] = PriorityQueue()
357
+ self._scheduler_thread: threading.Thread | None = None
358
+ self._scheduler_running = False
359
+
360
+ # Groups
361
+ self._groups: dict[str, list[str]] = defaultdict(list) # group -> task_ids
362
+
363
+ # Callbacks
364
+ self._on_rate_limited: list[Callable[[str], None]] = []
365
+ self._on_circuit_open: list[Callable[[], None]] = []
366
+
367
+ # -------------------------------------------------------------------------
368
+ # Enhanced Task Submission
369
+ # -------------------------------------------------------------------------
370
+
371
+ def submit(
372
+ self,
373
+ checkpoint: "Checkpoint",
374
+ priority: int | TaskPriority = TaskPriority.NORMAL,
375
+ timeout: float | None = None,
376
+ context: dict[str, Any] | None = None,
377
+ wait_for_rate_limit: bool = True,
378
+ rate_limit_timeout: float = 30.0,
379
+ **kwargs: Any,
380
+ ) -> DistributedTaskProtocol["CheckpointResult"]:
381
+ """Submit a checkpoint with rate limiting and circuit breaker.
382
+
383
+ Args:
384
+ checkpoint: Checkpoint to execute.
385
+ priority: Task priority (0-10 or TaskPriority).
386
+ timeout: Task timeout in seconds.
387
+ context: Additional context for the run.
388
+ wait_for_rate_limit: Wait for rate limit slot.
389
+ rate_limit_timeout: Timeout for rate limit wait.
390
+ **kwargs: Backend-specific options.
391
+
392
+ Returns:
393
+ Task handle for tracking execution.
394
+
395
+ Raises:
396
+ DistributedError: If circuit breaker is open.
397
+ TaskSubmissionError: If rate limit timeout exceeded.
398
+ """
399
+ # Check circuit breaker
400
+ if not self._circuit_breaker.can_execute():
401
+ raise DistributedError(
402
+ "Circuit breaker is open",
403
+ {"state": self._circuit_breaker.state},
404
+ )
405
+
406
+ # Rate limiting
407
+ if wait_for_rate_limit:
408
+ if not self._rate_limiter.acquire(timeout=rate_limit_timeout):
409
+ for callback in self._on_rate_limited:
410
+ callback(checkpoint.name)
411
+ raise TaskSubmissionError(
412
+ "Rate limit timeout exceeded",
413
+ checkpoint_name=checkpoint.name,
414
+ reason="Could not acquire rate limit slot",
415
+ )
416
+
417
+ try:
418
+ task = super().submit(checkpoint, priority, timeout, context, **kwargs)
419
+
420
+ # Add completion callback for circuit breaker
421
+ if hasattr(task, "add_callback"):
422
+ task.add_callback(self._handle_task_completion)
423
+
424
+ return task
425
+
426
+ except Exception as e:
427
+ self._circuit_breaker.record_failure()
428
+ self._rate_limiter.release()
429
+ raise
430
+
431
+ def _handle_task_completion(
432
+ self,
433
+ result: "CheckpointResult | None",
434
+ exception: Exception | None,
435
+ ) -> None:
436
+ """Handle task completion for circuit breaker."""
437
+ if exception:
438
+ self._circuit_breaker.record_failure()
439
+ else:
440
+ self._circuit_breaker.record_success()
441
+ self._rate_limiter.release()
442
+
443
+ # -------------------------------------------------------------------------
444
+ # Group Submission
445
+ # -------------------------------------------------------------------------
446
+
447
+ def submit_group(
448
+ self,
449
+ group_name: str,
450
+ checkpoints: list["Checkpoint"],
451
+ priority: int | TaskPriority = TaskPriority.NORMAL,
452
+ timeout: float | None = None,
453
+ context: dict[str, Any] | None = None,
454
+ **kwargs: Any,
455
+ ) -> list[DistributedTaskProtocol["CheckpointResult"]]:
456
+ """Submit a group of checkpoints.
457
+
458
+ Groups allow logical organization of related checkpoints
459
+ and aggregate result tracking.
460
+
461
+ Args:
462
+ group_name: Name for the group.
463
+ checkpoints: List of checkpoints to execute.
464
+ priority: Task priority.
465
+ timeout: Task timeout in seconds.
466
+ context: Additional context for all runs.
467
+ **kwargs: Backend-specific options.
468
+
469
+ Returns:
470
+ List of task handles.
471
+ """
472
+ tasks = []
473
+ group_context = {
474
+ **(context or {}),
475
+ "group_name": group_name,
476
+ "group_size": len(checkpoints),
477
+ }
478
+
479
+ for i, checkpoint in enumerate(checkpoints):
480
+ cp_context = {
481
+ **group_context,
482
+ "group_index": i,
483
+ }
484
+ try:
485
+ task = self.submit(checkpoint, priority, timeout, cp_context, **kwargs)
486
+ tasks.append(task)
487
+ self._groups[group_name].append(task.task_id)
488
+ except Exception as e:
489
+ logger.error(f"Failed to submit {checkpoint.name} in group {group_name}: {e}")
490
+
491
+ return tasks
492
+
493
+ def get_group_tasks(
494
+ self,
495
+ group_name: str,
496
+ ) -> list[DistributedTaskProtocol["CheckpointResult"]]:
497
+ """Get all tasks in a group.
498
+
499
+ Args:
500
+ group_name: Name of the group.
501
+
502
+ Returns:
503
+ List of task handles.
504
+ """
505
+ tasks = []
506
+ for task_id in self._groups.get(group_name, []):
507
+ task = self._backend.get_task(task_id)
508
+ if task:
509
+ tasks.append(task)
510
+ return tasks
511
+
512
+ def get_group_status(self, group_name: str) -> dict[str, Any]:
513
+ """Get status summary for a group.
514
+
515
+ Args:
516
+ group_name: Name of the group.
517
+
518
+ Returns:
519
+ Status summary dictionary.
520
+ """
521
+ tasks = self.get_group_tasks(group_name)
522
+
523
+ states = defaultdict(int)
524
+ for task in tasks:
525
+ states[task.state.value] += 1
526
+
527
+ return {
528
+ "group_name": group_name,
529
+ "total_tasks": len(tasks),
530
+ "states": dict(states),
531
+ "completed": sum(1 for t in tasks if t.is_ready()),
532
+ "successful": sum(1 for t in tasks if t.is_successful()),
533
+ }
534
+
535
+ # -------------------------------------------------------------------------
536
+ # Scheduling
537
+ # -------------------------------------------------------------------------
538
+
539
+ def schedule(
540
+ self,
541
+ checkpoint: "Checkpoint",
542
+ delay_seconds: float | None = None,
543
+ scheduled_time: datetime | None = None,
544
+ priority: TaskPriority = TaskPriority.NORMAL,
545
+ context: dict[str, Any] | None = None,
546
+ recurring: bool = False,
547
+ interval_seconds: float = 0.0,
548
+ ) -> str:
549
+ """Schedule a checkpoint for future execution.
550
+
551
+ Args:
552
+ checkpoint: Checkpoint to execute.
553
+ delay_seconds: Delay from now (mutually exclusive with scheduled_time).
554
+ scheduled_time: Specific time to execute.
555
+ priority: Task priority.
556
+ context: Execution context.
557
+ recurring: If True, reschedule after execution.
558
+ interval_seconds: Interval for recurring tasks.
559
+
560
+ Returns:
561
+ Scheduled task ID.
562
+
563
+ Raises:
564
+ ValueError: If neither delay nor scheduled_time provided.
565
+ """
566
+ if delay_seconds is not None:
567
+ run_time = datetime.now() + timedelta(seconds=delay_seconds)
568
+ elif scheduled_time is not None:
569
+ run_time = scheduled_time
570
+ else:
571
+ raise ValueError("Either delay_seconds or scheduled_time must be provided")
572
+
573
+ task = ScheduledTask(
574
+ task_id=f"scheduled-{uuid4().hex[:12]}",
575
+ checkpoint=checkpoint,
576
+ scheduled_time=run_time,
577
+ priority=priority,
578
+ context=context or {},
579
+ recurring=recurring,
580
+ interval_seconds=interval_seconds,
581
+ )
582
+
583
+ self._scheduled_tasks.put(task)
584
+ logger.info(
585
+ f"Scheduled {checkpoint.name} for {run_time.isoformat()}"
586
+ + (f" (recurring every {interval_seconds}s)" if recurring else "")
587
+ )
588
+
589
+ # Start scheduler if not running
590
+ self._ensure_scheduler_running()
591
+
592
+ return task.task_id
593
+
594
+ def cancel_scheduled(self, task_id: str) -> bool:
595
+ """Cancel a scheduled task.
596
+
597
+ Note: This creates a new queue excluding the task.
598
+
599
+ Args:
600
+ task_id: ID of the scheduled task.
601
+
602
+ Returns:
603
+ True if found and cancelled.
604
+ """
605
+ new_queue: PriorityQueue[ScheduledTask] = PriorityQueue()
606
+ found = False
607
+
608
+ while not self._scheduled_tasks.empty():
609
+ try:
610
+ task = self._scheduled_tasks.get_nowait()
611
+ if task.task_id == task_id:
612
+ found = True
613
+ else:
614
+ new_queue.put(task)
615
+ except Empty:
616
+ break
617
+
618
+ self._scheduled_tasks = new_queue
619
+ return found
620
+
621
+ def get_scheduled_tasks(self) -> list[ScheduledTask]:
622
+ """Get all scheduled tasks.
623
+
624
+ Returns:
625
+ List of scheduled tasks (copy).
626
+ """
627
+ tasks = []
628
+ temp_queue: PriorityQueue[ScheduledTask] = PriorityQueue()
629
+
630
+ while not self._scheduled_tasks.empty():
631
+ try:
632
+ task = self._scheduled_tasks.get_nowait()
633
+ tasks.append(task)
634
+ temp_queue.put(task)
635
+ except Empty:
636
+ break
637
+
638
+ self._scheduled_tasks = temp_queue
639
+ return tasks
640
+
641
+ def _ensure_scheduler_running(self) -> None:
642
+ """Ensure the scheduler thread is running."""
643
+ if self._scheduler_running:
644
+ return
645
+
646
+ self._scheduler_running = True
647
+ self._scheduler_thread = threading.Thread(
648
+ target=self._scheduler_loop,
649
+ daemon=True,
650
+ name="distributed-checkpoint-scheduler",
651
+ )
652
+ self._scheduler_thread.start()
653
+
654
+ def _scheduler_loop(self) -> None:
655
+ """Scheduler main loop."""
656
+ while self._scheduler_running:
657
+ try:
658
+ # Check for due tasks
659
+ while True:
660
+ try:
661
+ task = self._scheduled_tasks.get_nowait()
662
+ except Empty:
663
+ break
664
+
665
+ if datetime.now() >= task.scheduled_time:
666
+ # Execute task
667
+ self._execute_scheduled_task(task)
668
+ else:
669
+ # Put back and wait
670
+ self._scheduled_tasks.put(task)
671
+ break
672
+
673
+ time.sleep(1.0)
674
+
675
+ except Exception as e:
676
+ logger.error(f"Scheduler error: {e}")
677
+ time.sleep(5.0)
678
+
679
+ def _execute_scheduled_task(self, scheduled: ScheduledTask) -> None:
680
+ """Execute a scheduled task."""
681
+ try:
682
+ logger.info(f"Executing scheduled task: {scheduled.checkpoint.name}")
683
+ self.submit(
684
+ scheduled.checkpoint,
685
+ priority=scheduled.priority,
686
+ context=scheduled.context,
687
+ )
688
+
689
+ # Reschedule if recurring
690
+ if scheduled.recurring and scheduled.interval_seconds > 0:
691
+ next_run = datetime.now() + timedelta(seconds=scheduled.interval_seconds)
692
+ new_task = ScheduledTask(
693
+ task_id=f"scheduled-{uuid4().hex[:12]}",
694
+ checkpoint=scheduled.checkpoint,
695
+ scheduled_time=next_run,
696
+ priority=scheduled.priority,
697
+ context=scheduled.context,
698
+ recurring=True,
699
+ interval_seconds=scheduled.interval_seconds,
700
+ )
701
+ self._scheduled_tasks.put(new_task)
702
+ logger.info(f"Rescheduled {scheduled.checkpoint.name} for {next_run.isoformat()}")
703
+
704
+ except Exception as e:
705
+ logger.error(f"Failed to execute scheduled task {scheduled.task_id}: {e}")
706
+
707
+ # -------------------------------------------------------------------------
708
+ # Lifecycle
709
+ # -------------------------------------------------------------------------
710
+
711
+ def disconnect(self) -> None:
712
+ """Disconnect and stop scheduler."""
713
+ self._scheduler_running = False
714
+ if self._scheduler_thread:
715
+ self._scheduler_thread.join(timeout=5.0)
716
+ self._scheduler_thread = None
717
+ super().disconnect()
718
+
719
+ # -------------------------------------------------------------------------
720
+ # Callbacks
721
+ # -------------------------------------------------------------------------
722
+
723
+ def on_rate_limited(self, callback: Callable[[str], None]) -> None:
724
+ """Register a rate limit callback.
725
+
726
+ Args:
727
+ callback: Function called with checkpoint name when rate limited.
728
+ """
729
+ self._on_rate_limited.append(callback)
730
+
731
+ def on_circuit_open(self, callback: Callable[[], None]) -> None:
732
+ """Register a circuit open callback.
733
+
734
+ Args:
735
+ callback: Function called when circuit breaker opens.
736
+ """
737
+ self._on_circuit_open.append(callback)
738
+
739
+ # -------------------------------------------------------------------------
740
+ # Status and Monitoring
741
+ # -------------------------------------------------------------------------
742
+
743
+ def get_status(self) -> dict[str, Any]:
744
+ """Get orchestrator status summary.
745
+
746
+ Returns:
747
+ Status dictionary with metrics and state info.
748
+ """
749
+ return {
750
+ "backend": self._backend.name,
751
+ "is_connected": self.is_connected,
752
+ "circuit_breaker_state": self._circuit_breaker.state,
753
+ "scheduled_tasks": self._scheduled_tasks.qsize(),
754
+ "groups": {
755
+ name: len(task_ids)
756
+ for name, task_ids in self._groups.items()
757
+ },
758
+ "metrics": self.metrics.to_dict(),
759
+ "cluster": self.get_cluster_state().__dict__ if self.is_connected else None,
760
+ }
761
+
762
+ def reset_circuit_breaker(self) -> None:
763
+ """Reset the circuit breaker to closed state."""
764
+ self._circuit_breaker.reset()
765
+ logger.info("Circuit breaker reset")