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,1220 @@
1
+ """Enterprise Prometheus metrics system for Truthound.
2
+
3
+ This module extends the base metrics system with enterprise features:
4
+ - Full validator metrics exposure
5
+ - Checkpoint execution metrics
6
+ - DataSource performance metrics
7
+ - HTTP endpoint for Prometheus scraping
8
+ - Push gateway support
9
+ - OpenTelemetry export
10
+
11
+ Architecture:
12
+ MetricsManager
13
+ |
14
+ +---> ValidatorMetrics (per-validator stats)
15
+ +---> CheckpointMetrics (checkpoint execution)
16
+ +---> DataSourceMetrics (datasource performance)
17
+ +---> SystemMetrics (memory, CPU, etc.)
18
+ |
19
+ v
20
+ MetricsServer (HTTP /metrics endpoint)
21
+ |
22
+ +---> Prometheus scrape
23
+ +---> Push gateway
24
+
25
+ Usage:
26
+ >>> from truthound.infrastructure.metrics import (
27
+ ... get_metrics, configure_metrics,
28
+ ... ValidatorMetrics, CheckpointMetrics,
29
+ ... )
30
+ >>>
31
+ >>> # Configure and start HTTP server
32
+ >>> configure_metrics(
33
+ ... enable_http=True,
34
+ ... port=9090,
35
+ ... service="truthound",
36
+ ... )
37
+ >>>
38
+ >>> # Record validator metrics
39
+ >>> metrics = get_metrics()
40
+ >>> with metrics.validator.time("not_null", "users", "email"):
41
+ ... run_validation()
42
+ >>>
43
+ >>> # Record checkpoint metrics
44
+ >>> metrics.checkpoint.execution_started("daily_check")
45
+ >>> metrics.checkpoint.execution_completed("daily_check", success=True, issues=5)
46
+ """
47
+
48
+ from __future__ import annotations
49
+
50
+ import http.server
51
+ import os
52
+ import socket
53
+ import socketserver
54
+ import threading
55
+ import time
56
+ from abc import ABC, abstractmethod
57
+ from contextlib import contextmanager
58
+ from dataclasses import dataclass, field
59
+ from datetime import datetime, timezone
60
+ from enum import Enum
61
+ from typing import Any, Callable, Iterator
62
+
63
+ # Re-export base metrics components
64
+ from truthound.observability.metrics import (
65
+ Counter,
66
+ Gauge,
67
+ Histogram,
68
+ Summary,
69
+ MetricsRegistry,
70
+ MetricsCollector,
71
+ PrometheusExporter,
72
+ StatsDExporter,
73
+ )
74
+
75
+
76
+ # =============================================================================
77
+ # Metrics Configuration
78
+ # =============================================================================
79
+
80
+
81
+ @dataclass
82
+ class MetricsConfig:
83
+ """Metrics configuration.
84
+
85
+ Example:
86
+ >>> config = MetricsConfig(
87
+ ... enabled=True,
88
+ ... service="truthound",
89
+ ... environment="production",
90
+ ... enable_http=True,
91
+ ... port=9090,
92
+ ... push_gateway_url="http://pushgateway:9091",
93
+ ... )
94
+ """
95
+
96
+ enabled: bool = True
97
+ service: str = ""
98
+ environment: str = ""
99
+ namespace: str = "truthound"
100
+
101
+ # HTTP server
102
+ enable_http: bool = False
103
+ host: str = "0.0.0.0"
104
+ port: int = 9090
105
+ path: str = "/metrics"
106
+
107
+ # Push gateway
108
+ push_gateway_url: str = ""
109
+ push_interval: float = 15.0
110
+ push_job: str = "truthound"
111
+
112
+ # Default labels
113
+ default_labels: dict[str, str] = field(default_factory=dict)
114
+
115
+ # Histogram buckets
116
+ latency_buckets: tuple[float, ...] = (
117
+ 0.001, 0.005, 0.01, 0.025, 0.05, 0.1,
118
+ 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
119
+ )
120
+ size_buckets: tuple[float, ...] = (
121
+ 100, 1000, 10000, 100000, 1000000,
122
+ 10000000, 100000000, 1000000000,
123
+ )
124
+
125
+ @classmethod
126
+ def from_environment(cls) -> "MetricsConfig":
127
+ """Load from environment variables."""
128
+ return cls(
129
+ enabled=os.getenv("METRICS_ENABLED", "true").lower() == "true",
130
+ service=os.getenv("SERVICE_NAME", "truthound"),
131
+ environment=os.getenv("ENVIRONMENT", "development"),
132
+ enable_http=os.getenv("METRICS_HTTP_ENABLED", "false").lower() == "true",
133
+ port=int(os.getenv("METRICS_PORT", "9090")),
134
+ push_gateway_url=os.getenv("METRICS_PUSH_GATEWAY_URL", ""),
135
+ )
136
+
137
+
138
+ # =============================================================================
139
+ # Validator Metrics
140
+ # =============================================================================
141
+
142
+
143
+ class ValidatorMetrics:
144
+ """Metrics for validator execution.
145
+
146
+ Tracks:
147
+ - Execution count per validator
148
+ - Execution duration (histogram)
149
+ - Pass/fail rates
150
+ - Issue counts
151
+ - Row processing rates
152
+ """
153
+
154
+ def __init__(
155
+ self,
156
+ registry: MetricsRegistry,
157
+ namespace: str = "truthound",
158
+ latency_buckets: tuple[float, ...] | None = None,
159
+ ) -> None:
160
+ """Initialize validator metrics.
161
+
162
+ Args:
163
+ registry: Metrics registry.
164
+ namespace: Metric namespace prefix.
165
+ latency_buckets: Histogram buckets for latency.
166
+ """
167
+ self._registry = registry
168
+ self._namespace = namespace
169
+
170
+ # Validator execution counter
171
+ self.executions = Counter(
172
+ f"{namespace}_validator_executions_total",
173
+ "Total number of validator executions",
174
+ labels=("validator", "dataset", "column", "status"),
175
+ )
176
+ registry.register(self.executions)
177
+
178
+ # Validator execution duration
179
+ self.duration = Histogram(
180
+ f"{namespace}_validator_duration_seconds",
181
+ "Validator execution duration in seconds",
182
+ buckets=latency_buckets or (
183
+ 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0
184
+ ),
185
+ labels=("validator", "dataset"),
186
+ )
187
+ registry.register(self.duration)
188
+
189
+ # Issues found counter
190
+ self.issues = Counter(
191
+ f"{namespace}_validator_issues_total",
192
+ "Total number of issues found by validators",
193
+ labels=("validator", "dataset", "column", "severity"),
194
+ )
195
+ registry.register(self.issues)
196
+
197
+ # Rows processed counter
198
+ self.rows_processed = Counter(
199
+ f"{namespace}_validator_rows_processed_total",
200
+ "Total number of rows processed by validators",
201
+ labels=("validator", "dataset"),
202
+ )
203
+ registry.register(self.rows_processed)
204
+
205
+ # Active validators gauge
206
+ self.active = Gauge(
207
+ f"{namespace}_validators_active",
208
+ "Number of currently active validators",
209
+ labels=("dataset",),
210
+ )
211
+ registry.register(self.active)
212
+
213
+ # Pass rate (calculated)
214
+ self._pass_counts: dict[str, int] = {}
215
+ self._total_counts: dict[str, int] = {}
216
+
217
+ def record_execution(
218
+ self,
219
+ validator: str,
220
+ dataset: str,
221
+ column: str = "",
222
+ *,
223
+ success: bool,
224
+ duration_seconds: float,
225
+ issues_found: int = 0,
226
+ rows_processed: int = 0,
227
+ severity: str = "warning",
228
+ ) -> None:
229
+ """Record a validator execution.
230
+
231
+ Args:
232
+ validator: Validator name.
233
+ dataset: Dataset name.
234
+ column: Column name (if applicable).
235
+ success: Whether validation passed.
236
+ duration_seconds: Execution duration.
237
+ issues_found: Number of issues found.
238
+ rows_processed: Number of rows processed.
239
+ severity: Issue severity level.
240
+ """
241
+ status = "success" if success else "failure"
242
+
243
+ # Increment execution counter
244
+ self.executions.inc(
245
+ validator=validator,
246
+ dataset=dataset,
247
+ column=column or "_all",
248
+ status=status,
249
+ )
250
+
251
+ # Record duration
252
+ self.duration.observe(duration_seconds, validator=validator, dataset=dataset)
253
+
254
+ # Record issues
255
+ if issues_found > 0:
256
+ self.issues.add(
257
+ issues_found,
258
+ validator=validator,
259
+ dataset=dataset,
260
+ column=column or "_all",
261
+ severity=severity,
262
+ )
263
+
264
+ # Record rows
265
+ if rows_processed > 0:
266
+ self.rows_processed.add(
267
+ rows_processed,
268
+ validator=validator,
269
+ dataset=dataset,
270
+ )
271
+
272
+ # Update pass rate tracking
273
+ key = f"{validator}:{dataset}"
274
+ self._total_counts[key] = self._total_counts.get(key, 0) + 1
275
+ if success:
276
+ self._pass_counts[key] = self._pass_counts.get(key, 0) + 1
277
+
278
+ @contextmanager
279
+ def time(
280
+ self,
281
+ validator: str,
282
+ dataset: str,
283
+ column: str = "",
284
+ ) -> Iterator[None]:
285
+ """Context manager to time validator execution.
286
+
287
+ Args:
288
+ validator: Validator name.
289
+ dataset: Dataset name.
290
+ column: Column name.
291
+
292
+ Example:
293
+ >>> with metrics.validator.time("not_null", "users", "email"):
294
+ ... run_validation()
295
+ """
296
+ self.active.inc(dataset=dataset)
297
+ start = time.perf_counter()
298
+ success = True
299
+ issues = 0
300
+
301
+ try:
302
+ yield
303
+ except Exception:
304
+ success = False
305
+ raise
306
+ finally:
307
+ duration = time.perf_counter() - start
308
+ self.active.dec(dataset=dataset)
309
+ self.record_execution(
310
+ validator,
311
+ dataset,
312
+ column,
313
+ success=success,
314
+ duration_seconds=duration,
315
+ issues_found=issues,
316
+ )
317
+
318
+ def get_pass_rate(self, validator: str, dataset: str) -> float:
319
+ """Get pass rate for a validator.
320
+
321
+ Args:
322
+ validator: Validator name.
323
+ dataset: Dataset name.
324
+
325
+ Returns:
326
+ Pass rate (0.0 to 1.0).
327
+ """
328
+ key = f"{validator}:{dataset}"
329
+ total = self._total_counts.get(key, 0)
330
+ if total == 0:
331
+ return 1.0
332
+ return self._pass_counts.get(key, 0) / total
333
+
334
+
335
+ # =============================================================================
336
+ # Checkpoint Metrics
337
+ # =============================================================================
338
+
339
+
340
+ class CheckpointMetrics:
341
+ """Metrics for checkpoint execution.
342
+
343
+ Tracks:
344
+ - Checkpoint execution count
345
+ - Execution duration
346
+ - Pass/fail status
347
+ - Issue distribution
348
+ """
349
+
350
+ def __init__(
351
+ self,
352
+ registry: MetricsRegistry,
353
+ namespace: str = "truthound",
354
+ latency_buckets: tuple[float, ...] | None = None,
355
+ ) -> None:
356
+ """Initialize checkpoint metrics."""
357
+ self._registry = registry
358
+ self._namespace = namespace
359
+ self._start_times: dict[str, float] = {}
360
+
361
+ # Checkpoint executions
362
+ self.executions = Counter(
363
+ f"{namespace}_checkpoint_executions_total",
364
+ "Total number of checkpoint executions",
365
+ labels=("checkpoint", "status"),
366
+ )
367
+ registry.register(self.executions)
368
+
369
+ # Checkpoint duration
370
+ self.duration = Histogram(
371
+ f"{namespace}_checkpoint_duration_seconds",
372
+ "Checkpoint execution duration in seconds",
373
+ buckets=latency_buckets or (
374
+ 0.1, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0, 120.0, 300.0
375
+ ),
376
+ labels=("checkpoint",),
377
+ )
378
+ registry.register(self.duration)
379
+
380
+ # Issues by checkpoint
381
+ self.issues = Gauge(
382
+ f"{namespace}_checkpoint_issues",
383
+ "Current number of issues in checkpoint",
384
+ labels=("checkpoint", "severity"),
385
+ )
386
+ registry.register(self.issues)
387
+
388
+ # Validators run per checkpoint
389
+ self.validators_run = Counter(
390
+ f"{namespace}_checkpoint_validators_run_total",
391
+ "Total validators run per checkpoint",
392
+ labels=("checkpoint",),
393
+ )
394
+ registry.register(self.validators_run)
395
+
396
+ # Last execution timestamp
397
+ self.last_execution = Gauge(
398
+ f"{namespace}_checkpoint_last_execution_timestamp",
399
+ "Timestamp of last checkpoint execution",
400
+ labels=("checkpoint",),
401
+ )
402
+ registry.register(self.last_execution)
403
+
404
+ # Currently running checkpoints
405
+ self.running = Gauge(
406
+ f"{namespace}_checkpoints_running",
407
+ "Number of currently running checkpoints",
408
+ )
409
+ registry.register(self.running)
410
+
411
+ def execution_started(self, checkpoint: str) -> None:
412
+ """Record checkpoint execution start.
413
+
414
+ Args:
415
+ checkpoint: Checkpoint name.
416
+ """
417
+ self._start_times[checkpoint] = time.time()
418
+ self.running.inc()
419
+
420
+ def execution_completed(
421
+ self,
422
+ checkpoint: str,
423
+ *,
424
+ success: bool,
425
+ issues: int = 0,
426
+ validators_run: int = 0,
427
+ issues_by_severity: dict[str, int] | None = None,
428
+ ) -> None:
429
+ """Record checkpoint execution completion.
430
+
431
+ Args:
432
+ checkpoint: Checkpoint name.
433
+ success: Whether checkpoint passed.
434
+ issues: Total issues found.
435
+ validators_run: Number of validators executed.
436
+ issues_by_severity: Issues broken down by severity.
437
+ """
438
+ self.running.dec()
439
+
440
+ status = "success" if success else "failure"
441
+ self.executions.inc(checkpoint=checkpoint, status=status)
442
+
443
+ # Record duration
444
+ start_time = self._start_times.pop(checkpoint, time.time())
445
+ duration = time.time() - start_time
446
+ self.duration.observe(duration, checkpoint=checkpoint)
447
+
448
+ # Record validators run
449
+ if validators_run > 0:
450
+ self.validators_run.add(validators_run, checkpoint=checkpoint)
451
+
452
+ # Record issues by severity
453
+ if issues_by_severity:
454
+ for severity, count in issues_by_severity.items():
455
+ self.issues.set(count, checkpoint=checkpoint, severity=severity)
456
+ else:
457
+ self.issues.set(issues, checkpoint=checkpoint, severity="total")
458
+
459
+ # Update last execution time
460
+ self.last_execution.set_to_current_time(checkpoint=checkpoint)
461
+
462
+ @contextmanager
463
+ def track(self, checkpoint: str) -> Iterator[None]:
464
+ """Context manager to track checkpoint execution.
465
+
466
+ Args:
467
+ checkpoint: Checkpoint name.
468
+
469
+ Example:
470
+ >>> with metrics.checkpoint.track("daily_check"):
471
+ ... run_checkpoint()
472
+ """
473
+ self.execution_started(checkpoint)
474
+ success = True
475
+ try:
476
+ yield
477
+ except Exception:
478
+ success = False
479
+ raise
480
+ finally:
481
+ self.execution_completed(checkpoint, success=success)
482
+
483
+
484
+ # =============================================================================
485
+ # DataSource Metrics
486
+ # =============================================================================
487
+
488
+
489
+ class DataSourceMetrics:
490
+ """Metrics for data source operations.
491
+
492
+ Tracks:
493
+ - Query execution count
494
+ - Query duration
495
+ - Rows read/written
496
+ - Connection pool stats
497
+ - Error rates
498
+ """
499
+
500
+ def __init__(
501
+ self,
502
+ registry: MetricsRegistry,
503
+ namespace: str = "truthound",
504
+ latency_buckets: tuple[float, ...] | None = None,
505
+ size_buckets: tuple[float, ...] | None = None,
506
+ ) -> None:
507
+ """Initialize datasource metrics."""
508
+ self._registry = registry
509
+ self._namespace = namespace
510
+
511
+ # Query counter
512
+ self.queries = Counter(
513
+ f"{namespace}_datasource_queries_total",
514
+ "Total number of datasource queries",
515
+ labels=("datasource", "operation", "status"),
516
+ )
517
+ registry.register(self.queries)
518
+
519
+ # Query duration
520
+ self.query_duration = Histogram(
521
+ f"{namespace}_datasource_query_duration_seconds",
522
+ "Datasource query duration in seconds",
523
+ buckets=latency_buckets or (
524
+ 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0
525
+ ),
526
+ labels=("datasource", "operation"),
527
+ )
528
+ registry.register(self.query_duration)
529
+
530
+ # Rows processed
531
+ self.rows = Counter(
532
+ f"{namespace}_datasource_rows_total",
533
+ "Total rows processed by datasource",
534
+ labels=("datasource", "operation"),
535
+ )
536
+ registry.register(self.rows)
537
+
538
+ # Bytes processed
539
+ self.bytes = Counter(
540
+ f"{namespace}_datasource_bytes_total",
541
+ "Total bytes processed by datasource",
542
+ labels=("datasource", "operation"),
543
+ )
544
+ registry.register(self.bytes)
545
+
546
+ # Connection pool
547
+ self.pool_size = Gauge(
548
+ f"{namespace}_datasource_pool_size",
549
+ "Current connection pool size",
550
+ labels=("datasource",),
551
+ )
552
+ registry.register(self.pool_size)
553
+
554
+ self.pool_available = Gauge(
555
+ f"{namespace}_datasource_pool_available",
556
+ "Available connections in pool",
557
+ labels=("datasource",),
558
+ )
559
+ registry.register(self.pool_available)
560
+
561
+ # Errors
562
+ self.errors = Counter(
563
+ f"{namespace}_datasource_errors_total",
564
+ "Total datasource errors",
565
+ labels=("datasource", "error_type"),
566
+ )
567
+ registry.register(self.errors)
568
+
569
+ def record_query(
570
+ self,
571
+ datasource: str,
572
+ operation: str,
573
+ *,
574
+ success: bool,
575
+ duration_seconds: float,
576
+ rows: int = 0,
577
+ bytes_processed: int = 0,
578
+ error_type: str = "",
579
+ ) -> None:
580
+ """Record a datasource query.
581
+
582
+ Args:
583
+ datasource: Datasource name.
584
+ operation: Operation type (read, write, scan, etc).
585
+ success: Whether query succeeded.
586
+ duration_seconds: Query duration.
587
+ rows: Rows processed.
588
+ bytes_processed: Bytes processed.
589
+ error_type: Error type if failed.
590
+ """
591
+ status = "success" if success else "failure"
592
+
593
+ self.queries.inc(datasource=datasource, operation=operation, status=status)
594
+ self.query_duration.observe(
595
+ duration_seconds, datasource=datasource, operation=operation
596
+ )
597
+
598
+ if rows > 0:
599
+ self.rows.add(rows, datasource=datasource, operation=operation)
600
+
601
+ if bytes_processed > 0:
602
+ self.bytes.add(bytes_processed, datasource=datasource, operation=operation)
603
+
604
+ if not success and error_type:
605
+ self.errors.inc(datasource=datasource, error_type=error_type)
606
+
607
+ @contextmanager
608
+ def time_query(
609
+ self,
610
+ datasource: str,
611
+ operation: str = "query",
612
+ ) -> Iterator[None]:
613
+ """Context manager to time datasource query.
614
+
615
+ Args:
616
+ datasource: Datasource name.
617
+ operation: Operation type.
618
+ """
619
+ start = time.perf_counter()
620
+ success = True
621
+ error_type = ""
622
+
623
+ try:
624
+ yield
625
+ except Exception as e:
626
+ success = False
627
+ error_type = type(e).__name__
628
+ raise
629
+ finally:
630
+ duration = time.perf_counter() - start
631
+ self.record_query(
632
+ datasource,
633
+ operation,
634
+ success=success,
635
+ duration_seconds=duration,
636
+ error_type=error_type,
637
+ )
638
+
639
+ def update_pool_stats(
640
+ self,
641
+ datasource: str,
642
+ size: int,
643
+ available: int,
644
+ ) -> None:
645
+ """Update connection pool statistics.
646
+
647
+ Args:
648
+ datasource: Datasource name.
649
+ size: Total pool size.
650
+ available: Available connections.
651
+ """
652
+ self.pool_size.set(size, datasource=datasource)
653
+ self.pool_available.set(available, datasource=datasource)
654
+
655
+
656
+ # =============================================================================
657
+ # System Metrics
658
+ # =============================================================================
659
+
660
+
661
+ class SystemMetrics:
662
+ """System-level metrics (memory, CPU, etc).
663
+
664
+ Tracks:
665
+ - Process memory usage
666
+ - CPU usage
667
+ - File descriptors
668
+ - Thread count
669
+ """
670
+
671
+ def __init__(
672
+ self,
673
+ registry: MetricsRegistry,
674
+ namespace: str = "truthound",
675
+ ) -> None:
676
+ """Initialize system metrics."""
677
+ self._registry = registry
678
+ self._namespace = namespace
679
+
680
+ # Memory
681
+ self.memory_bytes = Gauge(
682
+ f"{namespace}_process_memory_bytes",
683
+ "Process memory usage in bytes",
684
+ labels=("type",),
685
+ )
686
+ registry.register(self.memory_bytes)
687
+
688
+ # CPU
689
+ self.cpu_seconds = Counter(
690
+ f"{namespace}_process_cpu_seconds_total",
691
+ "Total CPU time spent in seconds",
692
+ labels=("mode",),
693
+ )
694
+ registry.register(self.cpu_seconds)
695
+
696
+ # File descriptors
697
+ self.open_fds = Gauge(
698
+ f"{namespace}_process_open_fds",
699
+ "Number of open file descriptors",
700
+ )
701
+ registry.register(self.open_fds)
702
+
703
+ # Threads
704
+ self.threads = Gauge(
705
+ f"{namespace}_process_threads",
706
+ "Number of threads",
707
+ )
708
+ registry.register(self.threads)
709
+
710
+ # Start time
711
+ self.start_time = Gauge(
712
+ f"{namespace}_process_start_time_seconds",
713
+ "Start time of the process (Unix timestamp)",
714
+ )
715
+ registry.register(self.start_time)
716
+ self.start_time.set(time.time())
717
+
718
+ def collect(self) -> None:
719
+ """Collect current system metrics."""
720
+ try:
721
+ import resource
722
+
723
+ # Memory usage
724
+ usage = resource.getrusage(resource.RUSAGE_SELF)
725
+ self.memory_bytes.set(usage.ru_maxrss * 1024, type="rss")
726
+
727
+ # CPU time
728
+ self.cpu_seconds.add(usage.ru_utime, mode="user")
729
+ self.cpu_seconds.add(usage.ru_stime, mode="system")
730
+
731
+ except Exception:
732
+ pass
733
+
734
+ try:
735
+ # Thread count
736
+ self.threads.set(threading.active_count())
737
+ except Exception:
738
+ pass
739
+
740
+ try:
741
+ # Open file descriptors (Linux)
742
+ import os
743
+
744
+ fd_dir = f"/proc/{os.getpid()}/fd"
745
+ if os.path.isdir(fd_dir):
746
+ self.open_fds.set(len(os.listdir(fd_dir)))
747
+ except Exception:
748
+ pass
749
+
750
+
751
+ # =============================================================================
752
+ # Metrics Server (HTTP)
753
+ # =============================================================================
754
+
755
+
756
+ class MetricsServer:
757
+ """HTTP server for Prometheus metrics endpoint.
758
+
759
+ Provides /metrics endpoint for Prometheus scraping.
760
+
761
+ Example:
762
+ >>> server = MetricsServer(registry, port=9090)
763
+ >>> server.start()
764
+ >>> # ... metrics available at http://localhost:9090/metrics
765
+ >>> server.stop()
766
+ """
767
+
768
+ def __init__(
769
+ self,
770
+ registry: MetricsRegistry,
771
+ *,
772
+ host: str = "0.0.0.0",
773
+ port: int = 9090,
774
+ path: str = "/metrics",
775
+ ) -> None:
776
+ """Initialize metrics server.
777
+
778
+ Args:
779
+ registry: Metrics registry.
780
+ host: Server host.
781
+ port: Server port.
782
+ path: Metrics endpoint path.
783
+ """
784
+ self._registry = registry
785
+ self._host = host
786
+ self._port = port
787
+ self._path = path
788
+ self._exporter = PrometheusExporter()
789
+ self._server: socketserver.TCPServer | None = None
790
+ self._thread: threading.Thread | None = None
791
+
792
+ def start(self) -> None:
793
+ """Start the metrics server."""
794
+ if self._server:
795
+ return
796
+
797
+ registry = self._registry
798
+ exporter = self._exporter
799
+ path = self._path
800
+
801
+ class MetricsHandler(http.server.BaseHTTPRequestHandler):
802
+ def do_GET(self) -> None:
803
+ if self.path == path:
804
+ content = exporter.export(registry)
805
+ self.send_response(200)
806
+ self.send_header("Content-Type", "text/plain; charset=utf-8")
807
+ self.send_header("Content-Length", str(len(content)))
808
+ self.end_headers()
809
+ self.wfile.write(content.encode("utf-8"))
810
+ else:
811
+ self.send_response(404)
812
+ self.end_headers()
813
+
814
+ def log_message(self, format: str, *args: Any) -> None:
815
+ pass # Suppress logging
816
+
817
+ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
818
+ allow_reuse_address = True
819
+
820
+ self._server = ThreadedTCPServer((self._host, self._port), MetricsHandler)
821
+ self._thread = threading.Thread(
822
+ target=self._server.serve_forever,
823
+ daemon=True,
824
+ name="metrics-server",
825
+ )
826
+ self._thread.start()
827
+
828
+ def stop(self) -> None:
829
+ """Stop the metrics server."""
830
+ if self._server:
831
+ self._server.shutdown()
832
+ self._server = None
833
+ self._thread = None
834
+
835
+ @property
836
+ def url(self) -> str:
837
+ """Get the metrics endpoint URL."""
838
+ host = self._host if self._host != "0.0.0.0" else "localhost"
839
+ return f"http://{host}:{self._port}{self._path}"
840
+
841
+
842
+ # =============================================================================
843
+ # Push Gateway Support
844
+ # =============================================================================
845
+
846
+
847
+ class PushGatewayPusher:
848
+ """Push metrics to Prometheus Push Gateway.
849
+
850
+ Example:
851
+ >>> pusher = PushGatewayPusher(
852
+ ... registry,
853
+ ... url="http://pushgateway:9091",
854
+ ... job="truthound",
855
+ ... )
856
+ >>> pusher.start()
857
+ """
858
+
859
+ def __init__(
860
+ self,
861
+ registry: MetricsRegistry,
862
+ *,
863
+ url: str,
864
+ job: str = "truthound",
865
+ instance: str = "",
866
+ grouping: dict[str, str] | None = None,
867
+ push_interval: float = 15.0,
868
+ ) -> None:
869
+ """Initialize push gateway pusher.
870
+
871
+ Args:
872
+ registry: Metrics registry.
873
+ url: Push gateway URL.
874
+ job: Job name.
875
+ instance: Instance name.
876
+ grouping: Additional grouping labels.
877
+ push_interval: Push interval in seconds.
878
+ """
879
+ self._registry = registry
880
+ self._url = url.rstrip("/")
881
+ self._job = job
882
+ self._instance = instance or socket.gethostname()
883
+ self._grouping = grouping or {}
884
+ self._push_interval = push_interval
885
+ self._exporter = PrometheusExporter()
886
+ self._running = False
887
+ self._thread: threading.Thread | None = None
888
+
889
+ def start(self) -> None:
890
+ """Start pushing metrics."""
891
+ if self._running:
892
+ return
893
+
894
+ self._running = True
895
+ self._thread = threading.Thread(
896
+ target=self._push_loop,
897
+ daemon=True,
898
+ name="metrics-pusher",
899
+ )
900
+ self._thread.start()
901
+
902
+ def stop(self) -> None:
903
+ """Stop pushing metrics."""
904
+ self._running = False
905
+ if self._thread:
906
+ self._thread.join(timeout=5)
907
+ self._thread = None
908
+
909
+ def push(self) -> bool:
910
+ """Push metrics once.
911
+
912
+ Returns:
913
+ True if successful.
914
+ """
915
+ try:
916
+ import urllib.request
917
+ import urllib.error
918
+
919
+ # Build URL with grouping
920
+ path_parts = [f"/job/{self._job}"]
921
+ if self._instance:
922
+ path_parts.append(f"/instance/{self._instance}")
923
+ for key, value in self._grouping.items():
924
+ path_parts.append(f"/{key}/{value}")
925
+
926
+ url = f"{self._url}/metrics" + "".join(path_parts)
927
+
928
+ data = self._exporter.export(self._registry).encode("utf-8")
929
+
930
+ request = urllib.request.Request(
931
+ url,
932
+ data=data,
933
+ method="POST",
934
+ headers={"Content-Type": "text/plain"},
935
+ )
936
+
937
+ with urllib.request.urlopen(request, timeout=30):
938
+ return True
939
+
940
+ except Exception:
941
+ return False
942
+
943
+ def _push_loop(self) -> None:
944
+ """Background push loop."""
945
+ while self._running:
946
+ self.push()
947
+ time.sleep(self._push_interval)
948
+
949
+
950
+ # =============================================================================
951
+ # Metrics Manager
952
+ # =============================================================================
953
+
954
+
955
+ class MetricsManager:
956
+ """Central manager for all metrics.
957
+
958
+ Provides access to all metric categories and manages the metrics server.
959
+
960
+ Example:
961
+ >>> manager = MetricsManager(config=MetricsConfig(enable_http=True))
962
+ >>> manager.start()
963
+ >>>
964
+ >>> # Record metrics
965
+ >>> manager.validator.record_execution("not_null", "users", success=True, ...)
966
+ >>> manager.checkpoint.execution_started("daily")
967
+ >>>
968
+ >>> # Get Prometheus format
969
+ >>> print(manager.export())
970
+ """
971
+
972
+ def __init__(self, config: MetricsConfig | None = None) -> None:
973
+ """Initialize metrics manager.
974
+
975
+ Args:
976
+ config: Metrics configuration.
977
+ """
978
+ self._config = config or MetricsConfig()
979
+ self._registry = MetricsRegistry()
980
+
981
+ # Initialize metric categories
982
+ self.validator = ValidatorMetrics(
983
+ self._registry,
984
+ namespace=self._config.namespace,
985
+ latency_buckets=self._config.latency_buckets,
986
+ )
987
+
988
+ self.checkpoint = CheckpointMetrics(
989
+ self._registry,
990
+ namespace=self._config.namespace,
991
+ latency_buckets=self._config.latency_buckets,
992
+ )
993
+
994
+ self.datasource = DataSourceMetrics(
995
+ self._registry,
996
+ namespace=self._config.namespace,
997
+ latency_buckets=self._config.latency_buckets,
998
+ size_buckets=self._config.size_buckets,
999
+ )
1000
+
1001
+ self.system = SystemMetrics(
1002
+ self._registry,
1003
+ namespace=self._config.namespace,
1004
+ )
1005
+
1006
+ # Exporters
1007
+ self._prometheus_exporter = PrometheusExporter()
1008
+
1009
+ # HTTP server
1010
+ self._server: MetricsServer | None = None
1011
+
1012
+ # Push gateway
1013
+ self._pusher: PushGatewayPusher | None = None
1014
+
1015
+ @property
1016
+ def registry(self) -> MetricsRegistry:
1017
+ """Get the metrics registry."""
1018
+ return self._registry
1019
+
1020
+ @property
1021
+ def config(self) -> MetricsConfig:
1022
+ """Get the configuration."""
1023
+ return self._config
1024
+
1025
+ def start(self) -> None:
1026
+ """Start metrics server and push gateway (if configured)."""
1027
+ if not self._config.enabled:
1028
+ return
1029
+
1030
+ # Start HTTP server
1031
+ if self._config.enable_http:
1032
+ self._server = MetricsServer(
1033
+ self._registry,
1034
+ host=self._config.host,
1035
+ port=self._config.port,
1036
+ path=self._config.path,
1037
+ )
1038
+ self._server.start()
1039
+
1040
+ # Start push gateway
1041
+ if self._config.push_gateway_url:
1042
+ self._pusher = PushGatewayPusher(
1043
+ self._registry,
1044
+ url=self._config.push_gateway_url,
1045
+ job=self._config.push_job,
1046
+ push_interval=self._config.push_interval,
1047
+ )
1048
+ self._pusher.start()
1049
+
1050
+ def stop(self) -> None:
1051
+ """Stop metrics server and push gateway."""
1052
+ if self._server:
1053
+ self._server.stop()
1054
+ self._server = None
1055
+
1056
+ if self._pusher:
1057
+ self._pusher.stop()
1058
+ self._pusher = None
1059
+
1060
+ def export(self) -> str:
1061
+ """Export metrics in Prometheus format.
1062
+
1063
+ Returns:
1064
+ Prometheus text format metrics.
1065
+ """
1066
+ # Collect system metrics before export
1067
+ self.system.collect()
1068
+ return self._prometheus_exporter.export(self._registry)
1069
+
1070
+ def counter(
1071
+ self,
1072
+ name: str,
1073
+ description: str = "",
1074
+ labels: list[str] | tuple[str, ...] = (),
1075
+ ) -> Counter:
1076
+ """Create a custom counter.
1077
+
1078
+ Args:
1079
+ name: Counter name.
1080
+ description: Description.
1081
+ labels: Label names.
1082
+
1083
+ Returns:
1084
+ Counter instance.
1085
+ """
1086
+ full_name = f"{self._config.namespace}_{name}"
1087
+ counter = Counter(full_name, description, labels=labels)
1088
+ return self._registry.register(counter) # type: ignore
1089
+
1090
+ def gauge(
1091
+ self,
1092
+ name: str,
1093
+ description: str = "",
1094
+ labels: list[str] | tuple[str, ...] = (),
1095
+ ) -> Gauge:
1096
+ """Create a custom gauge.
1097
+
1098
+ Args:
1099
+ name: Gauge name.
1100
+ description: Description.
1101
+ labels: Label names.
1102
+
1103
+ Returns:
1104
+ Gauge instance.
1105
+ """
1106
+ full_name = f"{self._config.namespace}_{name}"
1107
+ gauge = Gauge(full_name, description, labels=labels)
1108
+ return self._registry.register(gauge) # type: ignore
1109
+
1110
+ def histogram(
1111
+ self,
1112
+ name: str,
1113
+ description: str = "",
1114
+ labels: list[str] | tuple[str, ...] = (),
1115
+ buckets: tuple[float, ...] | None = None,
1116
+ ) -> Histogram:
1117
+ """Create a custom histogram.
1118
+
1119
+ Args:
1120
+ name: Histogram name.
1121
+ description: Description.
1122
+ labels: Label names.
1123
+ buckets: Bucket boundaries.
1124
+
1125
+ Returns:
1126
+ Histogram instance.
1127
+ """
1128
+ full_name = f"{self._config.namespace}_{name}"
1129
+ histogram = Histogram(
1130
+ full_name,
1131
+ description,
1132
+ labels=labels,
1133
+ buckets=buckets or self._config.latency_buckets,
1134
+ )
1135
+ return self._registry.register(histogram) # type: ignore
1136
+
1137
+
1138
+ # =============================================================================
1139
+ # Global Metrics Management
1140
+ # =============================================================================
1141
+
1142
+ _global_manager: MetricsManager | None = None
1143
+ _lock = threading.Lock()
1144
+
1145
+
1146
+ def configure_metrics(
1147
+ *,
1148
+ enabled: bool = True,
1149
+ service: str = "",
1150
+ environment: str = "",
1151
+ namespace: str = "truthound",
1152
+ enable_http: bool = False,
1153
+ host: str = "0.0.0.0",
1154
+ port: int = 9090,
1155
+ push_gateway_url: str = "",
1156
+ **kwargs: Any,
1157
+ ) -> MetricsManager:
1158
+ """Configure global metrics.
1159
+
1160
+ Args:
1161
+ enabled: Enable metrics.
1162
+ service: Service name.
1163
+ environment: Environment name.
1164
+ namespace: Metric namespace prefix.
1165
+ enable_http: Enable HTTP server.
1166
+ host: HTTP server host.
1167
+ port: HTTP server port.
1168
+ push_gateway_url: Push gateway URL.
1169
+ **kwargs: Additional MetricsConfig parameters.
1170
+
1171
+ Returns:
1172
+ Configured MetricsManager.
1173
+ """
1174
+ global _global_manager
1175
+
1176
+ with _lock:
1177
+ if _global_manager:
1178
+ _global_manager.stop()
1179
+
1180
+ config = MetricsConfig(
1181
+ enabled=enabled,
1182
+ service=service,
1183
+ environment=environment,
1184
+ namespace=namespace,
1185
+ enable_http=enable_http,
1186
+ host=host,
1187
+ port=port,
1188
+ push_gateway_url=push_gateway_url,
1189
+ **kwargs,
1190
+ )
1191
+
1192
+ _global_manager = MetricsManager(config)
1193
+ _global_manager.start()
1194
+
1195
+ return _global_manager
1196
+
1197
+
1198
+ def get_metrics() -> MetricsManager:
1199
+ """Get the global metrics manager.
1200
+
1201
+ Returns:
1202
+ MetricsManager instance.
1203
+ """
1204
+ global _global_manager
1205
+
1206
+ with _lock:
1207
+ if _global_manager is None:
1208
+ config = MetricsConfig.from_environment()
1209
+ _global_manager = MetricsManager(config)
1210
+ return _global_manager
1211
+
1212
+
1213
+ def reset_metrics() -> None:
1214
+ """Reset global metrics."""
1215
+ global _global_manager
1216
+
1217
+ with _lock:
1218
+ if _global_manager:
1219
+ _global_manager.stop()
1220
+ _global_manager = None