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,1268 @@
1
+ """Suite export system with extensible formatters and exporters.
2
+
3
+ This module provides a highly extensible architecture for exporting
4
+ validation suites to various formats and destinations.
5
+
6
+ Key Features:
7
+ - Plugin architecture for output formats (YAML, JSON, Python, etc.)
8
+ - Configurable export options
9
+ - Template-based code generation
10
+ - Post-processing hooks
11
+
12
+ Example:
13
+ from truthound.profiler.suite_export import (
14
+ SuiteExporter,
15
+ create_exporter,
16
+ export_suite,
17
+ )
18
+
19
+ # Simple export
20
+ export_suite(suite, "rules.yaml", format="yaml")
21
+
22
+ # Customized export
23
+ exporter = create_exporter(
24
+ format="python",
25
+ config=ExportConfig(
26
+ include_docstrings=True,
27
+ include_type_hints=True,
28
+ )
29
+ )
30
+ exporter.export(suite, "validators.py")
31
+
32
+ # Custom formatter registration
33
+ @register_formatter("custom")
34
+ class CustomFormatter(SuiteFormatter):
35
+ ...
36
+ """
37
+
38
+ from __future__ import annotations
39
+
40
+ import json
41
+ from abc import ABC, abstractmethod
42
+ from dataclasses import dataclass, field
43
+ from enum import Enum
44
+ from pathlib import Path
45
+ from typing import (
46
+ Any,
47
+ Callable,
48
+ Generic,
49
+ Protocol,
50
+ Sequence,
51
+ TypeVar,
52
+ TYPE_CHECKING,
53
+ )
54
+
55
+ if TYPE_CHECKING:
56
+ from truthound.profiler.generators.suite_generator import ValidationSuite
57
+ from truthound.profiler.generators.base import GeneratedRule
58
+
59
+
60
+ # =============================================================================
61
+ # Enums and Types
62
+ # =============================================================================
63
+
64
+
65
+ class ExportFormat(str, Enum):
66
+ """Supported export formats."""
67
+
68
+ YAML = "yaml"
69
+ JSON = "json"
70
+ PYTHON = "python"
71
+ TOML = "toml"
72
+ CHECKPOINT = "checkpoint" # Truthound checkpoint format
73
+ GREAT_EXPECTATIONS = "great_expectations"
74
+ DEEQU = "deequ"
75
+ CUSTOM = "custom"
76
+
77
+
78
+ class CodeStyle(str, Enum):
79
+ """Python code generation style."""
80
+
81
+ FUNCTIONAL = "functional" # List of validator instances
82
+ CLASS_BASED = "class_based" # Validator class with methods
83
+ DECLARATIVE = "declarative" # Declarative configuration
84
+
85
+
86
+ class OutputMode(str, Enum):
87
+ """Output mode for the exporter."""
88
+
89
+ FILE = "file"
90
+ STDOUT = "stdout"
91
+ STRING = "string"
92
+
93
+
94
+ # =============================================================================
95
+ # Configuration
96
+ # =============================================================================
97
+
98
+
99
+ @dataclass(frozen=True)
100
+ class ExportConfig:
101
+ """Configuration for suite export.
102
+
103
+ This immutable configuration controls how validation suites
104
+ are exported to various formats.
105
+
106
+ Attributes:
107
+ include_metadata: Include suite metadata in output
108
+ include_rationale: Include rule rationale/explanation
109
+ include_confidence: Include confidence levels
110
+ include_summary: Include summary statistics
111
+ sort_rules: Sort rules by category/name
112
+ group_by_category: Group rules by category
113
+ indent: Indentation for formatted output
114
+ code_style: Style for Python code generation
115
+ include_docstrings: Include docstrings in Python code
116
+ include_type_hints: Include type hints in Python code
117
+ include_imports: Include import statements in Python code
118
+ max_line_length: Max line length for formatted output
119
+ custom_options: Additional format-specific options
120
+ """
121
+
122
+ # Content options
123
+ include_metadata: bool = True
124
+ include_rationale: bool = True
125
+ include_confidence: bool = True
126
+ include_summary: bool = True
127
+ include_description: bool = True
128
+
129
+ # Formatting options
130
+ sort_rules: bool = True
131
+ group_by_category: bool = False
132
+ indent: int = 2
133
+
134
+ # Python code generation options
135
+ code_style: CodeStyle = CodeStyle.FUNCTIONAL
136
+ include_docstrings: bool = True
137
+ include_type_hints: bool = True
138
+ include_imports: bool = True
139
+ max_line_length: int = 88
140
+
141
+ # Custom options
142
+ custom_options: dict[str, Any] = field(default_factory=dict)
143
+
144
+ def with_options(self, **kwargs: Any) -> "ExportConfig":
145
+ """Create new config with updated options."""
146
+ current = {
147
+ "include_metadata": self.include_metadata,
148
+ "include_rationale": self.include_rationale,
149
+ "include_confidence": self.include_confidence,
150
+ "include_summary": self.include_summary,
151
+ "include_description": self.include_description,
152
+ "sort_rules": self.sort_rules,
153
+ "group_by_category": self.group_by_category,
154
+ "indent": self.indent,
155
+ "code_style": self.code_style,
156
+ "include_docstrings": self.include_docstrings,
157
+ "include_type_hints": self.include_type_hints,
158
+ "include_imports": self.include_imports,
159
+ "max_line_length": self.max_line_length,
160
+ "custom_options": dict(self.custom_options),
161
+ }
162
+ current.update(kwargs)
163
+ return ExportConfig(**current)
164
+
165
+
166
+ # Default configurations for common use cases
167
+ DEFAULT_CONFIG = ExportConfig()
168
+ MINIMAL_CONFIG = ExportConfig(
169
+ include_metadata=False,
170
+ include_rationale=False,
171
+ include_confidence=False,
172
+ include_summary=False,
173
+ include_docstrings=False,
174
+ )
175
+ VERBOSE_CONFIG = ExportConfig(
176
+ include_metadata=True,
177
+ include_rationale=True,
178
+ include_confidence=True,
179
+ include_summary=True,
180
+ group_by_category=True,
181
+ )
182
+
183
+
184
+ # =============================================================================
185
+ # Formatter Protocol and Base Class
186
+ # =============================================================================
187
+
188
+
189
+ class SuiteFormatterProtocol(Protocol):
190
+ """Protocol for suite formatters."""
191
+
192
+ format_name: str
193
+ file_extension: str
194
+
195
+ def format(
196
+ self,
197
+ suite: "ValidationSuite",
198
+ config: ExportConfig,
199
+ ) -> str:
200
+ """Format a validation suite to string output."""
201
+ ...
202
+
203
+
204
+ class SuiteFormatter(ABC):
205
+ """Abstract base class for suite formatters.
206
+
207
+ Formatters are responsible for converting a ValidationSuite
208
+ to a specific output format (YAML, JSON, Python, etc.).
209
+
210
+ Subclasses must implement:
211
+ - format_name: Name of the format
212
+ - file_extension: Default file extension
213
+ - format(): Main formatting method
214
+
215
+ Example:
216
+ class MyFormatter(SuiteFormatter):
217
+ format_name = "my_format"
218
+ file_extension = ".mf"
219
+
220
+ def format(self, suite, config):
221
+ return "..."
222
+ """
223
+
224
+ format_name: str = "base"
225
+ file_extension: str = ".txt"
226
+
227
+ def __init__(self, **kwargs: Any):
228
+ """Initialize formatter with optional configuration."""
229
+ self.options = kwargs
230
+
231
+ @abstractmethod
232
+ def format(
233
+ self,
234
+ suite: "ValidationSuite",
235
+ config: ExportConfig,
236
+ ) -> str:
237
+ """Format the validation suite to string output.
238
+
239
+ Args:
240
+ suite: Validation suite to format
241
+ config: Export configuration
242
+
243
+ Returns:
244
+ Formatted string output
245
+ """
246
+ pass
247
+
248
+ def _sort_rules(
249
+ self,
250
+ rules: Sequence["GeneratedRule"],
251
+ config: ExportConfig,
252
+ ) -> list["GeneratedRule"]:
253
+ """Sort rules according to configuration."""
254
+ if not config.sort_rules:
255
+ return list(rules)
256
+
257
+ return sorted(
258
+ rules,
259
+ key=lambda r: (r.category.value, r.name),
260
+ )
261
+
262
+ def _group_rules(
263
+ self,
264
+ rules: Sequence["GeneratedRule"],
265
+ ) -> dict[str, list["GeneratedRule"]]:
266
+ """Group rules by category."""
267
+ groups: dict[str, list["GeneratedRule"]] = {}
268
+ for rule in rules:
269
+ cat = rule.category.value
270
+ if cat not in groups:
271
+ groups[cat] = []
272
+ groups[cat].append(rule)
273
+ return groups
274
+
275
+
276
+ # =============================================================================
277
+ # Built-in Formatters
278
+ # =============================================================================
279
+
280
+
281
+ class YAMLFormatter(SuiteFormatter):
282
+ """YAML format output for validation suites."""
283
+
284
+ format_name = "yaml"
285
+ file_extension = ".yaml"
286
+
287
+ def format(
288
+ self,
289
+ suite: "ValidationSuite",
290
+ config: ExportConfig,
291
+ ) -> str:
292
+ lines: list[str] = []
293
+
294
+ # Header
295
+ lines.append(f"# Validation Suite: {suite.name}")
296
+ lines.append(f"# Strictness: {suite.strictness.value}")
297
+ lines.append(f"# Total rules: {len(suite.rules)}")
298
+ lines.append("")
299
+
300
+ # Metadata
301
+ if config.include_metadata and suite.metadata:
302
+ lines.append("metadata:")
303
+ for key, value in suite.metadata.items():
304
+ lines.append(f" {key}: {value}")
305
+ lines.append("")
306
+
307
+ # Summary
308
+ if config.include_summary:
309
+ lines.append("summary:")
310
+ counts = suite._count_by_category()
311
+ lines.append(" by_category:")
312
+ for cat, count in sorted(counts.items()):
313
+ lines.append(f" {cat}: {count}")
314
+ conf_counts = suite._count_by_confidence()
315
+ lines.append(" by_confidence:")
316
+ for conf, count in sorted(conf_counts.items()):
317
+ lines.append(f" {conf}: {count}")
318
+ lines.append("")
319
+
320
+ # Rules
321
+ rules = self._sort_rules(suite.rules, config)
322
+
323
+ if config.group_by_category:
324
+ lines.extend(self._format_grouped_rules(rules, config))
325
+ else:
326
+ lines.extend(self._format_flat_rules(rules, config))
327
+
328
+ return "\n".join(lines)
329
+
330
+ def _format_flat_rules(
331
+ self,
332
+ rules: list["GeneratedRule"],
333
+ config: ExportConfig,
334
+ ) -> list[str]:
335
+ """Format rules as flat list."""
336
+ lines = ["rules:"]
337
+
338
+ for rule in rules:
339
+ lines.extend(self._format_rule(rule, config, indent=2))
340
+ lines.append("")
341
+
342
+ return lines
343
+
344
+ def _format_grouped_rules(
345
+ self,
346
+ rules: list["GeneratedRule"],
347
+ config: ExportConfig,
348
+ ) -> list[str]:
349
+ """Format rules grouped by category."""
350
+ lines = ["rules:"]
351
+ groups = self._group_rules(rules)
352
+
353
+ for category, cat_rules in sorted(groups.items()):
354
+ lines.append(f" # Category: {category}")
355
+ lines.append(f" {category}:")
356
+ for rule in cat_rules:
357
+ lines.extend(self._format_rule(rule, config, indent=4))
358
+ lines.append("")
359
+
360
+ return lines
361
+
362
+ def _format_rule(
363
+ self,
364
+ rule: "GeneratedRule",
365
+ config: ExportConfig,
366
+ indent: int = 2,
367
+ ) -> list[str]:
368
+ """Format a single rule."""
369
+ prefix = " " * indent
370
+ lines = [
371
+ f"{prefix}- name: {rule.name}",
372
+ f"{prefix} validator: {rule.validator_class}",
373
+ f"{prefix} category: {rule.category.value}",
374
+ ]
375
+
376
+ if config.include_confidence:
377
+ lines.append(f"{prefix} confidence: {rule.confidence.value}")
378
+
379
+ if rule.columns:
380
+ lines.append(f"{prefix} columns: {list(rule.columns)}")
381
+
382
+ if rule.parameters:
383
+ lines.append(f"{prefix} parameters:")
384
+ for k, v in rule.parameters.items():
385
+ if isinstance(v, str):
386
+ lines.append(f'{prefix} {k}: "{v}"')
387
+ else:
388
+ lines.append(f"{prefix} {k}: {v}")
389
+
390
+ if rule.mostly is not None:
391
+ lines.append(f"{prefix} mostly: {rule.mostly}")
392
+
393
+ if config.include_description and rule.description:
394
+ lines.append(f'{prefix} description: "{rule.description}"')
395
+
396
+ if config.include_rationale and rule.rationale:
397
+ lines.append(f'{prefix} rationale: "{rule.rationale}"')
398
+
399
+ return lines
400
+
401
+
402
+ class JSONFormatter(SuiteFormatter):
403
+ """JSON format output for validation suites."""
404
+
405
+ format_name = "json"
406
+ file_extension = ".json"
407
+
408
+ def format(
409
+ self,
410
+ suite: "ValidationSuite",
411
+ config: ExportConfig,
412
+ ) -> str:
413
+ data = self._build_dict(suite, config)
414
+ return json.dumps(
415
+ data,
416
+ indent=config.indent,
417
+ ensure_ascii=False,
418
+ default=str,
419
+ )
420
+
421
+ def _build_dict(
422
+ self,
423
+ suite: "ValidationSuite",
424
+ config: ExportConfig,
425
+ ) -> dict[str, Any]:
426
+ """Build dictionary representation."""
427
+ rules = self._sort_rules(suite.rules, config)
428
+
429
+ data: dict[str, Any] = {
430
+ "name": suite.name,
431
+ "strictness": suite.strictness.value,
432
+ }
433
+
434
+ if config.include_metadata:
435
+ data["metadata"] = suite.metadata
436
+ data["source_profile"] = suite.source_profile
437
+
438
+ if config.group_by_category:
439
+ groups = self._group_rules(rules)
440
+ data["rules"] = {
441
+ cat: [self._rule_to_dict(r, config) for r in cat_rules]
442
+ for cat, cat_rules in sorted(groups.items())
443
+ }
444
+ else:
445
+ data["rules"] = [self._rule_to_dict(r, config) for r in rules]
446
+
447
+ if config.include_summary:
448
+ data["summary"] = {
449
+ "total_rules": len(rules),
450
+ "by_category": suite._count_by_category(),
451
+ "by_confidence": suite._count_by_confidence(),
452
+ }
453
+
454
+ return data
455
+
456
+ def _rule_to_dict(
457
+ self,
458
+ rule: "GeneratedRule",
459
+ config: ExportConfig,
460
+ ) -> dict[str, Any]:
461
+ """Convert rule to dictionary."""
462
+ data: dict[str, Any] = {
463
+ "name": rule.name,
464
+ "validator_class": rule.validator_class,
465
+ "category": rule.category.value,
466
+ }
467
+
468
+ if config.include_confidence:
469
+ data["confidence"] = rule.confidence.value
470
+
471
+ if rule.columns:
472
+ data["columns"] = list(rule.columns)
473
+
474
+ if rule.parameters:
475
+ data["parameters"] = rule.parameters
476
+
477
+ if rule.mostly is not None:
478
+ data["mostly"] = rule.mostly
479
+
480
+ if config.include_description and rule.description:
481
+ data["description"] = rule.description
482
+
483
+ if config.include_rationale and rule.rationale:
484
+ data["rationale"] = rule.rationale
485
+
486
+ return data
487
+
488
+
489
+ class PythonFormatter(SuiteFormatter):
490
+ """Python code generation for validation suites."""
491
+
492
+ format_name = "python"
493
+ file_extension = ".py"
494
+
495
+ def format(
496
+ self,
497
+ suite: "ValidationSuite",
498
+ config: ExportConfig,
499
+ ) -> str:
500
+ if config.code_style == CodeStyle.CLASS_BASED:
501
+ return self._format_class_based(suite, config)
502
+ elif config.code_style == CodeStyle.DECLARATIVE:
503
+ return self._format_declarative(suite, config)
504
+ else:
505
+ return self._format_functional(suite, config)
506
+
507
+ def _format_functional(
508
+ self,
509
+ suite: "ValidationSuite",
510
+ config: ExportConfig,
511
+ ) -> str:
512
+ """Generate functional-style Python code."""
513
+ lines: list[str] = []
514
+
515
+ # Module docstring
516
+ if config.include_docstrings:
517
+ lines.extend([
518
+ '"""Auto-generated validation suite.',
519
+ "",
520
+ f"Suite: {suite.name}",
521
+ f"Strictness: {suite.strictness.value}",
522
+ f"Total rules: {len(suite.rules)}",
523
+ '"""',
524
+ "",
525
+ ])
526
+
527
+ # Imports
528
+ if config.include_imports:
529
+ lines.extend(self._generate_imports(suite, config))
530
+ lines.append("")
531
+
532
+ # Type alias
533
+ if config.include_type_hints:
534
+ lines.append("ValidatorList = list[Validator]")
535
+ lines.append("")
536
+ lines.append("")
537
+
538
+ # Main function
539
+ lines.append("def create_validators()" + (
540
+ " -> ValidatorList:" if config.include_type_hints else ":"
541
+ ))
542
+ if config.include_docstrings:
543
+ lines.append(' """Create validation rules for the suite."""')
544
+ lines.append(" validators = []")
545
+ lines.append("")
546
+
547
+ # Rules
548
+ rules = self._sort_rules(suite.rules, config)
549
+ for rule in rules:
550
+ lines.extend(self._format_validator_append(rule, config))
551
+
552
+ lines.append(" return validators")
553
+ lines.append("")
554
+
555
+ return "\n".join(lines)
556
+
557
+ def _format_class_based(
558
+ self,
559
+ suite: "ValidationSuite",
560
+ config: ExportConfig,
561
+ ) -> str:
562
+ """Generate class-based Python code."""
563
+ lines: list[str] = []
564
+
565
+ # Module docstring
566
+ if config.include_docstrings:
567
+ lines.extend([
568
+ '"""Auto-generated validation suite.',
569
+ "",
570
+ f"Suite: {suite.name}",
571
+ '"""',
572
+ "",
573
+ ])
574
+
575
+ # Imports
576
+ if config.include_imports:
577
+ lines.extend(self._generate_imports(suite, config))
578
+ lines.append("from dataclasses import dataclass")
579
+ lines.append("")
580
+
581
+ # Suite class
582
+ suite_name = self._to_class_name(suite.name)
583
+ lines.append("@dataclass")
584
+ lines.append(f"class {suite_name}ValidationSuite:")
585
+ if config.include_docstrings:
586
+ lines.append(f' """Validation suite: {suite.name}."""')
587
+ lines.append("")
588
+
589
+ # Class attributes
590
+ lines.append(f' name: str = "{suite.name}"')
591
+ lines.append(f' strictness: str = "{suite.strictness.value}"')
592
+ lines.append("")
593
+
594
+ # Create validators method
595
+ lines.append(" def create_validators(self)" + (
596
+ " -> list[Validator]:" if config.include_type_hints else ":"
597
+ ))
598
+ if config.include_docstrings:
599
+ lines.append(' """Create all validators for this suite."""')
600
+ lines.append(" validators = []")
601
+ lines.append("")
602
+
603
+ rules = self._sort_rules(suite.rules, config)
604
+ for rule in rules:
605
+ lines.extend(self._format_validator_append(rule, config, indent=8))
606
+
607
+ lines.append(" return validators")
608
+ lines.append("")
609
+
610
+ return "\n".join(lines)
611
+
612
+ def _format_declarative(
613
+ self,
614
+ suite: "ValidationSuite",
615
+ config: ExportConfig,
616
+ ) -> str:
617
+ """Generate declarative-style Python code."""
618
+ lines: list[str] = []
619
+
620
+ # Module docstring
621
+ if config.include_docstrings:
622
+ lines.extend([
623
+ '"""Auto-generated validation suite (declarative style).',
624
+ "",
625
+ f"Suite: {suite.name}",
626
+ '"""',
627
+ "",
628
+ ])
629
+
630
+ # Imports
631
+ if config.include_imports:
632
+ lines.append("from typing import Any")
633
+ lines.append("")
634
+
635
+ # Declarative config
636
+ lines.append("SUITE_CONFIG = {")
637
+ lines.append(f' "name": "{suite.name}",')
638
+ lines.append(f' "strictness": "{suite.strictness.value}",')
639
+ lines.append(' "rules": [')
640
+
641
+ rules = self._sort_rules(suite.rules, config)
642
+ for rule in rules:
643
+ lines.append(" {")
644
+ lines.append(f' "name": "{rule.name}",')
645
+ lines.append(f' "validator": "{rule.validator_class}",')
646
+ lines.append(f' "category": "{rule.category.value}",')
647
+
648
+ if rule.columns:
649
+ lines.append(f' "columns": {list(rule.columns)},')
650
+ if rule.parameters:
651
+ lines.append(f' "parameters": {rule.parameters!r},')
652
+ if rule.mostly is not None:
653
+ lines.append(f' "mostly": {rule.mostly},')
654
+
655
+ lines.append(" },")
656
+
657
+ lines.append(" ],")
658
+ lines.append("}")
659
+ lines.append("")
660
+
661
+ return "\n".join(lines)
662
+
663
+ def _generate_imports(
664
+ self,
665
+ suite: "ValidationSuite",
666
+ config: ExportConfig,
667
+ ) -> list[str]:
668
+ """Generate import statements."""
669
+ lines = []
670
+
671
+ if config.include_type_hints:
672
+ lines.append("from typing import Any")
673
+ lines.append("")
674
+
675
+ # Collect unique validator classes
676
+ validators = sorted(set(r.validator_class for r in suite.rules))
677
+
678
+ lines.append("from truthound.validators import (")
679
+ for v in validators:
680
+ lines.append(f" {v},")
681
+ lines.append(")")
682
+
683
+ if config.include_type_hints:
684
+ lines.append("from truthound.validators.base import Validator")
685
+
686
+ return lines
687
+
688
+ def _format_validator_append(
689
+ self,
690
+ rule: "GeneratedRule",
691
+ config: ExportConfig,
692
+ indent: int = 4,
693
+ ) -> list[str]:
694
+ """Format validator.append() call."""
695
+ prefix = " " * indent
696
+ lines = []
697
+
698
+ # Comment
699
+ if config.include_description and rule.description:
700
+ lines.append(f"{prefix}# {rule.name}: {rule.description}")
701
+ else:
702
+ lines.append(f"{prefix}# {rule.name}")
703
+
704
+ # Build parameters
705
+ params: list[str] = []
706
+ if rule.columns:
707
+ params.append(f"columns={list(rule.columns)}")
708
+ for k, v in rule.parameters.items():
709
+ if isinstance(v, str):
710
+ params.append(f'{k}="{v}"')
711
+ else:
712
+ params.append(f"{k}={v!r}")
713
+ if rule.mostly is not None:
714
+ params.append(f"mostly={rule.mostly}")
715
+
716
+ param_str = ", ".join(params)
717
+
718
+ # Check line length
719
+ full_line = f"{prefix}validators.append({rule.validator_class}({param_str}))"
720
+ if len(full_line) <= config.max_line_length:
721
+ lines.append(full_line)
722
+ else:
723
+ # Multi-line format
724
+ lines.append(f"{prefix}validators.append(")
725
+ lines.append(f"{prefix} {rule.validator_class}(")
726
+ for i, param in enumerate(params):
727
+ comma = "," if i < len(params) - 1 else ""
728
+ lines.append(f"{prefix} {param}{comma}")
729
+ lines.append(f"{prefix} )")
730
+ lines.append(f"{prefix})")
731
+
732
+ lines.append("")
733
+ return lines
734
+
735
+ def _to_class_name(self, name: str) -> str:
736
+ """Convert name to valid Python class name."""
737
+ # Remove special characters and convert to PascalCase
738
+ parts = name.replace("-", "_").replace(".", "_").split("_")
739
+ return "".join(p.capitalize() for p in parts if p)
740
+
741
+
742
+ class TOMLFormatter(SuiteFormatter):
743
+ """TOML format output for validation suites."""
744
+
745
+ format_name = "toml"
746
+ file_extension = ".toml"
747
+
748
+ def format(
749
+ self,
750
+ suite: "ValidationSuite",
751
+ config: ExportConfig,
752
+ ) -> str:
753
+ lines: list[str] = []
754
+
755
+ # Suite header
756
+ lines.append("[suite]")
757
+ lines.append(f'name = "{suite.name}"')
758
+ lines.append(f'strictness = "{suite.strictness.value}"')
759
+
760
+ if config.include_metadata:
761
+ lines.append(f'source_profile = "{suite.source_profile}"')
762
+ if suite.metadata:
763
+ for key, value in suite.metadata.items():
764
+ if isinstance(value, str):
765
+ lines.append(f'{key} = "{value}"')
766
+ elif isinstance(value, list):
767
+ lines.append(f'{key} = {value}')
768
+ else:
769
+ lines.append(f'{key} = {value}')
770
+
771
+ lines.append("")
772
+
773
+ # Rules
774
+ rules = self._sort_rules(suite.rules, config)
775
+ for rule in rules:
776
+ lines.append(f"[[rules]]")
777
+ lines.append(f'name = "{rule.name}"')
778
+ lines.append(f'validator = "{rule.validator_class}"')
779
+ lines.append(f'category = "{rule.category.value}"')
780
+
781
+ if config.include_confidence:
782
+ lines.append(f'confidence = "{rule.confidence.value}"')
783
+
784
+ if rule.columns:
785
+ cols_str = ", ".join(f'"{c}"' for c in rule.columns)
786
+ lines.append(f"columns = [{cols_str}]")
787
+
788
+ if rule.parameters:
789
+ lines.append("[rules.parameters]")
790
+ for k, v in rule.parameters.items():
791
+ if isinstance(v, str):
792
+ lines.append(f'{k} = "{v}"')
793
+ else:
794
+ lines.append(f"{k} = {v}")
795
+
796
+ if rule.mostly is not None:
797
+ lines.append(f"mostly = {rule.mostly}")
798
+
799
+ if config.include_description and rule.description:
800
+ lines.append(f'description = "{rule.description}"')
801
+
802
+ lines.append("")
803
+
804
+ return "\n".join(lines)
805
+
806
+
807
+ class CheckpointFormatter(SuiteFormatter):
808
+ """Truthound Checkpoint format for CI/CD integration."""
809
+
810
+ format_name = "checkpoint"
811
+ file_extension = ".yaml"
812
+
813
+ def format(
814
+ self,
815
+ suite: "ValidationSuite",
816
+ config: ExportConfig,
817
+ ) -> str:
818
+ lines: list[str] = []
819
+
820
+ # Header
821
+ lines.append("# Auto-generated Truthound Checkpoint")
822
+ lines.append(f"# Source: {suite.name}")
823
+ lines.append("")
824
+ lines.append("checkpoints:")
825
+ lines.append(f" - name: {suite.name}_validation")
826
+ lines.append(' data_source: "${DATA_SOURCE}" # Set via environment')
827
+ lines.append(" validators:")
828
+
829
+ # Convert rules to checkpoint validators
830
+ rules = self._sort_rules(suite.rules, config)
831
+ for rule in rules:
832
+ lines.append(f" - type: {rule.validator_class}")
833
+ if rule.columns:
834
+ lines.append(f" columns: {list(rule.columns)}")
835
+ if rule.parameters:
836
+ for k, v in rule.parameters.items():
837
+ if isinstance(v, str):
838
+ lines.append(f' {k}: "{v}"')
839
+ else:
840
+ lines.append(f" {k}: {v}")
841
+ if rule.mostly is not None:
842
+ lines.append(f" mostly: {rule.mostly}")
843
+
844
+ lines.append("")
845
+ lines.append(" # Severity thresholds")
846
+ lines.append(" min_severity: warning")
847
+ lines.append("")
848
+ lines.append(" # Optional actions")
849
+ lines.append(" actions:")
850
+ lines.append(" - store_result")
851
+ lines.append("")
852
+
853
+ return "\n".join(lines)
854
+
855
+
856
+ # =============================================================================
857
+ # Formatter Registry
858
+ # =============================================================================
859
+
860
+
861
+ class FormatterRegistry:
862
+ """Registry for suite formatters.
863
+
864
+ Provides plugin-based formatter registration and lookup.
865
+ """
866
+
867
+ def __init__(self) -> None:
868
+ self._formatters: dict[str, type[SuiteFormatter]] = {}
869
+
870
+ def register(
871
+ self,
872
+ formatter_class: type[SuiteFormatter],
873
+ name: str | None = None,
874
+ ) -> None:
875
+ """Register a formatter class."""
876
+ key = name or formatter_class.format_name
877
+ self._formatters[key] = formatter_class
878
+
879
+ def get(self, name: str) -> type[SuiteFormatter]:
880
+ """Get formatter by name."""
881
+ if name not in self._formatters:
882
+ available = list(self._formatters.keys())
883
+ raise KeyError(
884
+ f"Formatter '{name}' not found. Available: {available}"
885
+ )
886
+ return self._formatters[name]
887
+
888
+ def list_all(self) -> dict[str, type[SuiteFormatter]]:
889
+ """List all registered formatters."""
890
+ return dict(self._formatters)
891
+
892
+ def create(self, name: str, **kwargs: Any) -> SuiteFormatter:
893
+ """Create formatter instance by name."""
894
+ formatter_class = self.get(name)
895
+ return formatter_class(**kwargs)
896
+
897
+ def get_extension(self, name: str) -> str:
898
+ """Get file extension for format."""
899
+ return self.get(name).file_extension
900
+
901
+
902
+ # Global registry
903
+ formatter_registry = FormatterRegistry()
904
+
905
+ # Register built-in formatters
906
+ formatter_registry.register(YAMLFormatter)
907
+ formatter_registry.register(JSONFormatter)
908
+ formatter_registry.register(PythonFormatter)
909
+ formatter_registry.register(TOMLFormatter)
910
+ formatter_registry.register(CheckpointFormatter)
911
+
912
+
913
+ def register_formatter(
914
+ name: str | None = None,
915
+ ) -> Callable[[type[SuiteFormatter]], type[SuiteFormatter]]:
916
+ """Decorator to register a formatter.
917
+
918
+ Example:
919
+ @register_formatter("custom")
920
+ class CustomFormatter(SuiteFormatter):
921
+ ...
922
+ """
923
+ def decorator(cls: type[SuiteFormatter]) -> type[SuiteFormatter]:
924
+ formatter_registry.register(cls, name)
925
+ return cls
926
+ return decorator
927
+
928
+
929
+ # =============================================================================
930
+ # Post-Processors
931
+ # =============================================================================
932
+
933
+
934
+ class ExportPostProcessor(Protocol):
935
+ """Protocol for export post-processors."""
936
+
937
+ def process(self, content: str, suite: "ValidationSuite") -> str:
938
+ """Process the exported content."""
939
+ ...
940
+
941
+
942
+ class AddHeaderPostProcessor:
943
+ """Add custom header to output."""
944
+
945
+ def __init__(self, header: str):
946
+ self.header = header
947
+
948
+ def process(self, content: str, suite: "ValidationSuite") -> str:
949
+ return f"{self.header}\n\n{content}"
950
+
951
+
952
+ class AddFooterPostProcessor:
953
+ """Add custom footer to output."""
954
+
955
+ def __init__(self, footer: str):
956
+ self.footer = footer
957
+
958
+ def process(self, content: str, suite: "ValidationSuite") -> str:
959
+ return f"{content}\n\n{self.footer}"
960
+
961
+
962
+ class TemplatePostProcessor:
963
+ """Apply template substitution."""
964
+
965
+ def __init__(self, substitutions: dict[str, str]):
966
+ self.substitutions = substitutions
967
+
968
+ def process(self, content: str, suite: "ValidationSuite") -> str:
969
+ result = content
970
+ for key, value in self.substitutions.items():
971
+ result = result.replace(f"${{{key}}}", value)
972
+ return result
973
+
974
+
975
+ # =============================================================================
976
+ # Suite Exporter
977
+ # =============================================================================
978
+
979
+
980
+ @dataclass
981
+ class ExportResult:
982
+ """Result of a suite export operation."""
983
+
984
+ success: bool
985
+ output_path: Path | None
986
+ content: str
987
+ format: str
988
+ message: str = ""
989
+ error: Exception | None = None
990
+
991
+
992
+ class SuiteExporter:
993
+ """Main exporter for validation suites.
994
+
995
+ Combines formatters, configuration, and post-processors
996
+ to provide a flexible export pipeline.
997
+
998
+ Example:
999
+ exporter = SuiteExporter(format="yaml")
1000
+ exporter.export(suite, "rules.yaml")
1001
+
1002
+ # With custom config
1003
+ exporter = SuiteExporter(
1004
+ format="python",
1005
+ config=ExportConfig(code_style=CodeStyle.CLASS_BASED),
1006
+ )
1007
+ result = exporter.export_to_string(suite)
1008
+
1009
+ # With post-processors
1010
+ exporter.add_post_processor(
1011
+ AddHeaderPostProcessor("# Custom header")
1012
+ )
1013
+ """
1014
+
1015
+ def __init__(
1016
+ self,
1017
+ format: str | ExportFormat = "yaml",
1018
+ config: ExportConfig | None = None,
1019
+ formatter: SuiteFormatter | None = None,
1020
+ ):
1021
+ """Initialize exporter.
1022
+
1023
+ Args:
1024
+ format: Output format name or enum
1025
+ config: Export configuration
1026
+ formatter: Custom formatter instance (overrides format)
1027
+ """
1028
+ self.format_name = (
1029
+ format.value if isinstance(format, ExportFormat) else format
1030
+ )
1031
+ self.config = config or DEFAULT_CONFIG
1032
+ self._post_processors: list[ExportPostProcessor] = []
1033
+
1034
+ if formatter:
1035
+ self._formatter = formatter
1036
+ else:
1037
+ self._formatter = formatter_registry.create(self.format_name)
1038
+
1039
+ @property
1040
+ def formatter(self) -> SuiteFormatter:
1041
+ """Get the formatter instance."""
1042
+ return self._formatter
1043
+
1044
+ def add_post_processor(self, processor: ExportPostProcessor) -> "SuiteExporter":
1045
+ """Add a post-processor to the pipeline."""
1046
+ self._post_processors.append(processor)
1047
+ return self
1048
+
1049
+ def clear_post_processors(self) -> "SuiteExporter":
1050
+ """Clear all post-processors."""
1051
+ self._post_processors.clear()
1052
+ return self
1053
+
1054
+ def export_to_string(
1055
+ self,
1056
+ suite: "ValidationSuite",
1057
+ config: ExportConfig | None = None,
1058
+ ) -> str:
1059
+ """Export suite to string.
1060
+
1061
+ Args:
1062
+ suite: Validation suite to export
1063
+ config: Override configuration
1064
+
1065
+ Returns:
1066
+ Formatted string output
1067
+ """
1068
+ cfg = config or self.config
1069
+ content = self._formatter.format(suite, cfg)
1070
+
1071
+ # Apply post-processors
1072
+ for processor in self._post_processors:
1073
+ content = processor.process(content, suite)
1074
+
1075
+ return content
1076
+
1077
+ def export(
1078
+ self,
1079
+ suite: "ValidationSuite",
1080
+ output_path: str | Path,
1081
+ config: ExportConfig | None = None,
1082
+ ) -> ExportResult:
1083
+ """Export suite to file.
1084
+
1085
+ Args:
1086
+ suite: Validation suite to export
1087
+ output_path: Output file path
1088
+ config: Override configuration
1089
+
1090
+ Returns:
1091
+ Export result
1092
+ """
1093
+ path = Path(output_path)
1094
+
1095
+ try:
1096
+ content = self.export_to_string(suite, config)
1097
+
1098
+ # Ensure parent directory exists
1099
+ path.parent.mkdir(parents=True, exist_ok=True)
1100
+
1101
+ with open(path, "w", encoding="utf-8") as f:
1102
+ f.write(content)
1103
+
1104
+ return ExportResult(
1105
+ success=True,
1106
+ output_path=path,
1107
+ content=content,
1108
+ format=self.format_name,
1109
+ message=f"Successfully exported to {path}",
1110
+ )
1111
+
1112
+ except Exception as e:
1113
+ return ExportResult(
1114
+ success=False,
1115
+ output_path=path,
1116
+ content="",
1117
+ format=self.format_name,
1118
+ message=f"Export failed: {e}",
1119
+ error=e,
1120
+ )
1121
+
1122
+ def export_to_stdout(
1123
+ self,
1124
+ suite: "ValidationSuite",
1125
+ config: ExportConfig | None = None,
1126
+ ) -> str:
1127
+ """Export suite and print to stdout.
1128
+
1129
+ Args:
1130
+ suite: Validation suite to export
1131
+ config: Override configuration
1132
+
1133
+ Returns:
1134
+ Formatted string output (also printed)
1135
+ """
1136
+ content = self.export_to_string(suite, config)
1137
+ print(content)
1138
+ return content
1139
+
1140
+
1141
+ # =============================================================================
1142
+ # Convenience Functions
1143
+ # =============================================================================
1144
+
1145
+
1146
+ def create_exporter(
1147
+ format: str = "yaml",
1148
+ config: ExportConfig | None = None,
1149
+ **formatter_options: Any,
1150
+ ) -> SuiteExporter:
1151
+ """Create a suite exporter with the given format.
1152
+
1153
+ Args:
1154
+ format: Output format (yaml, json, python, toml, checkpoint)
1155
+ config: Export configuration
1156
+ **formatter_options: Options passed to formatter
1157
+
1158
+ Returns:
1159
+ Configured SuiteExporter instance
1160
+ """
1161
+ formatter = formatter_registry.create(format, **formatter_options)
1162
+ return SuiteExporter(
1163
+ format=format,
1164
+ config=config,
1165
+ formatter=formatter,
1166
+ )
1167
+
1168
+
1169
+ def export_suite(
1170
+ suite: "ValidationSuite",
1171
+ output_path: str | Path,
1172
+ format: str | None = None,
1173
+ config: ExportConfig | None = None,
1174
+ ) -> ExportResult:
1175
+ """Export a validation suite to file.
1176
+
1177
+ Args:
1178
+ suite: Validation suite to export
1179
+ output_path: Output file path
1180
+ format: Output format (inferred from extension if not provided)
1181
+ config: Export configuration
1182
+
1183
+ Returns:
1184
+ Export result
1185
+ """
1186
+ path = Path(output_path)
1187
+
1188
+ # Infer format from extension if not provided
1189
+ if format is None:
1190
+ ext_to_format = {
1191
+ ".yaml": "yaml",
1192
+ ".yml": "yaml",
1193
+ ".json": "json",
1194
+ ".py": "python",
1195
+ ".toml": "toml",
1196
+ }
1197
+ format = ext_to_format.get(path.suffix.lower(), "yaml")
1198
+
1199
+ exporter = create_exporter(format=format, config=config)
1200
+ return exporter.export(suite, path)
1201
+
1202
+
1203
+ def format_suite(
1204
+ suite: "ValidationSuite",
1205
+ format: str = "yaml",
1206
+ config: ExportConfig | None = None,
1207
+ ) -> str:
1208
+ """Format a validation suite to string.
1209
+
1210
+ Args:
1211
+ suite: Validation suite to format
1212
+ format: Output format
1213
+ config: Export configuration
1214
+
1215
+ Returns:
1216
+ Formatted string
1217
+ """
1218
+ exporter = create_exporter(format=format, config=config)
1219
+ return exporter.export_to_string(suite, config)
1220
+
1221
+
1222
+ def get_available_formats() -> list[str]:
1223
+ """Get list of available export formats."""
1224
+ return list(formatter_registry.list_all().keys())
1225
+
1226
+
1227
+ # =============================================================================
1228
+ # Exports
1229
+ # =============================================================================
1230
+
1231
+
1232
+ __all__ = [
1233
+ # Enums
1234
+ "ExportFormat",
1235
+ "CodeStyle",
1236
+ "OutputMode",
1237
+ # Configuration
1238
+ "ExportConfig",
1239
+ "DEFAULT_CONFIG",
1240
+ "MINIMAL_CONFIG",
1241
+ "VERBOSE_CONFIG",
1242
+ # Protocol and base
1243
+ "SuiteFormatterProtocol",
1244
+ "SuiteFormatter",
1245
+ # Built-in formatters
1246
+ "YAMLFormatter",
1247
+ "JSONFormatter",
1248
+ "PythonFormatter",
1249
+ "TOMLFormatter",
1250
+ "CheckpointFormatter",
1251
+ # Registry
1252
+ "FormatterRegistry",
1253
+ "formatter_registry",
1254
+ "register_formatter",
1255
+ # Post-processors
1256
+ "ExportPostProcessor",
1257
+ "AddHeaderPostProcessor",
1258
+ "AddFooterPostProcessor",
1259
+ "TemplatePostProcessor",
1260
+ # Exporter
1261
+ "ExportResult",
1262
+ "SuiteExporter",
1263
+ # Convenience
1264
+ "create_exporter",
1265
+ "export_suite",
1266
+ "format_suite",
1267
+ "get_available_formats",
1268
+ ]