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,1127 @@
1
+ """Validator scaffold generator.
2
+
3
+ This module provides scaffolding for creating custom validators with
4
+ various template variants:
5
+ - basic: Minimal validator with core structure
6
+ - column: Column-level validator with target column support
7
+ - pattern: Pattern matching validator with regex
8
+ - range: Numeric range validator
9
+ - composite: Multi-validator composite
10
+ - full: Full-featured with tests and documentation
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Any, ClassVar
16
+
17
+ from truthound.cli_modules.scaffolding.base import (
18
+ BaseScaffold,
19
+ ScaffoldConfig,
20
+ ScaffoldResult,
21
+ register_scaffold,
22
+ )
23
+
24
+
25
+ @register_scaffold(
26
+ name="validator",
27
+ description="Generate a custom validator",
28
+ aliases=("val", "v"),
29
+ )
30
+ class ValidatorScaffold(BaseScaffold):
31
+ """Scaffold generator for validators.
32
+
33
+ Supports multiple template variants for different validation patterns.
34
+ """
35
+
36
+ name: ClassVar[str] = "validator"
37
+ description: ClassVar[str] = "Generate a custom validator"
38
+ aliases: ClassVar[tuple[str, ...]] = ("val", "v")
39
+
40
+ TEMPLATE_VARIANTS: ClassVar[tuple[str, ...]] = (
41
+ "basic",
42
+ "column",
43
+ "pattern",
44
+ "range",
45
+ "comparison",
46
+ "composite",
47
+ "full",
48
+ )
49
+
50
+ def get_options(self) -> dict[str, Any]:
51
+ """Get validator-specific options."""
52
+ return {
53
+ "category": {
54
+ "type": "str",
55
+ "default": "custom",
56
+ "description": "Validator category (e.g., numeric, string, business)",
57
+ },
58
+ "severity": {
59
+ "type": "str",
60
+ "default": "MEDIUM",
61
+ "description": "Default severity level",
62
+ "choices": ["LOW", "MEDIUM", "HIGH", "CRITICAL"],
63
+ },
64
+ "columns": {
65
+ "type": "list[str]",
66
+ "default": None,
67
+ "description": "Target columns for column validators",
68
+ },
69
+ "pattern": {
70
+ "type": "str",
71
+ "default": None,
72
+ "description": "Regex pattern for pattern validators",
73
+ },
74
+ "min_value": {
75
+ "type": "float",
76
+ "default": None,
77
+ "description": "Minimum value for range validators",
78
+ },
79
+ "max_value": {
80
+ "type": "float",
81
+ "default": None,
82
+ "description": "Maximum value for range validators",
83
+ },
84
+ }
85
+
86
+ def _generate_files(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
87
+ """Generate validator files based on variant."""
88
+ variant = config.template_variant
89
+
90
+ # Generate main validator file
91
+ if variant == "basic":
92
+ self._generate_basic(config, result)
93
+ elif variant == "column":
94
+ self._generate_column(config, result)
95
+ elif variant == "pattern":
96
+ self._generate_pattern(config, result)
97
+ elif variant == "range":
98
+ self._generate_range(config, result)
99
+ elif variant == "comparison":
100
+ self._generate_comparison(config, result)
101
+ elif variant == "composite":
102
+ self._generate_composite(config, result)
103
+ elif variant == "full":
104
+ self._generate_full(config, result)
105
+ else:
106
+ self._generate_basic(config, result)
107
+
108
+ # Generate __init__.py
109
+ self._generate_init(config, result)
110
+
111
+ # Generate tests if requested
112
+ if config.include_tests:
113
+ self._generate_tests(config, result)
114
+
115
+ # Generate docs if requested
116
+ if config.include_docs:
117
+ self._generate_docs(config, result)
118
+
119
+ def _generate_init(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
120
+ """Generate __init__.py file."""
121
+ content = f'''"""Package for {config.title_name} validator."""
122
+
123
+ from {config.name}.validator import {config.class_name}Validator
124
+
125
+ __all__ = ["{config.class_name}Validator"]
126
+ '''
127
+ result.add_file(f"{config.name}/__init__.py", content)
128
+
129
+ def _generate_basic(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
130
+ """Generate basic validator template."""
131
+ severity = config.extra.get("severity", "MEDIUM")
132
+ category = config.extra.get("category", config.category)
133
+
134
+ content = f'''{self._get_header(config)}
135
+
136
+ from __future__ import annotations
137
+
138
+ from typing import Any
139
+
140
+ import polars as pl
141
+
142
+ from truthound.validators.base import Validator, ValidationIssue, ValidatorConfig
143
+ from truthound.validators.sdk import custom_validator
144
+ from truthound.types import Severity
145
+
146
+
147
+ @custom_validator(
148
+ name="{config.name}",
149
+ category="{category}",
150
+ description="{config.description or f'{config.title_name} validator'}",
151
+ version="{config.version}",
152
+ author="{config.author}",
153
+ tags=["{category}"],
154
+ )
155
+ class {config.class_name}Validator(Validator):
156
+ """{config.description or f'{config.title_name} validator.'}
157
+
158
+ This validator checks data quality based on custom business rules.
159
+
160
+ Example:
161
+ >>> validator = {config.class_name}Validator()
162
+ >>> issues = validator.validate(lf)
163
+ """
164
+
165
+ name: str = "{config.name}"
166
+ category: str = "{category}"
167
+ default_severity: Severity = Severity.{severity}
168
+
169
+ def __init__(
170
+ self,
171
+ config: ValidatorConfig | None = None,
172
+ **kwargs: Any,
173
+ ) -> None:
174
+ """Initialize the validator.
175
+
176
+ Args:
177
+ config: Optional validator configuration
178
+ **kwargs: Additional keyword arguments
179
+ """
180
+ super().__init__(config, **kwargs)
181
+
182
+ def validate(self, lf: pl.LazyFrame) -> list[ValidationIssue]:
183
+ """Validate the data.
184
+
185
+ Args:
186
+ lf: Polars LazyFrame to validate
187
+
188
+ Returns:
189
+ List of validation issues found
190
+ """
191
+ issues: list[ValidationIssue] = []
192
+ total_rows = lf.select(pl.len()).collect().item()
193
+
194
+ if total_rows == 0:
195
+ return issues
196
+
197
+ # TODO: Implement your validation logic here
198
+ # Example:
199
+ # for col in self._get_target_columns(lf):
200
+ # violations = lf.filter(pl.col(col).is_null()).collect()
201
+ # if violations.height > 0:
202
+ # issues.append(ValidationIssue(
203
+ # column=col,
204
+ # issue_type=self.name,
205
+ # count=violations.height,
206
+ # severity=self.default_severity,
207
+ # details=f"Found {{violations.height}} issues in '{{col}}'",
208
+ # ))
209
+
210
+ return issues
211
+ '''
212
+ result.add_file(f"{config.name}/validator.py", content)
213
+
214
+ def _generate_column(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
215
+ """Generate column validator template."""
216
+ severity = config.extra.get("severity", "MEDIUM")
217
+ category = config.extra.get("category", config.category)
218
+
219
+ content = f'''{self._get_header(config)}
220
+
221
+ from __future__ import annotations
222
+
223
+ from typing import Any
224
+
225
+ import polars as pl
226
+
227
+ from truthound.validators.base import (
228
+ ColumnValidator,
229
+ ValidationIssue,
230
+ ValidatorConfig,
231
+ )
232
+ from truthound.validators.sdk import custom_validator
233
+ from truthound.types import Severity
234
+
235
+
236
+ @custom_validator(
237
+ name="{config.name}",
238
+ category="{category}",
239
+ description="{config.description or f'{config.title_name} column validator'}",
240
+ version="{config.version}",
241
+ author="{config.author}",
242
+ tags=["{category}", "column"],
243
+ )
244
+ class {config.class_name}Validator(ColumnValidator):
245
+ """{config.description or f'{config.title_name} column validator.'}
246
+
247
+ Validates each column that matches the configured criteria.
248
+
249
+ Example:
250
+ >>> validator = {config.class_name}Validator(columns=["value", "amount"])
251
+ >>> issues = validator.validate(lf)
252
+ """
253
+
254
+ name: str = "{config.name}"
255
+ category: str = "{category}"
256
+ default_severity: Severity = Severity.{severity}
257
+
258
+ def __init__(
259
+ self,
260
+ config: ValidatorConfig | None = None,
261
+ columns: tuple[str, ...] | list[str] | None = None,
262
+ severity: Severity = Severity.{severity},
263
+ **kwargs: Any,
264
+ ) -> None:
265
+ """Initialize the validator.
266
+
267
+ Args:
268
+ config: Validator configuration
269
+ columns: Columns to validate (None = all applicable)
270
+ severity: Severity level for issues
271
+ **kwargs: Additional keyword arguments
272
+ """
273
+ super().__init__(config, columns=columns, **kwargs)
274
+ self._severity = severity
275
+
276
+ def check_column(
277
+ self,
278
+ lf: pl.LazyFrame,
279
+ col: str,
280
+ total_rows: int,
281
+ ) -> ValidationIssue | None:
282
+ """Check a single column for violations.
283
+
284
+ Args:
285
+ lf: LazyFrame to validate
286
+ col: Column name to check
287
+ total_rows: Total row count
288
+
289
+ Returns:
290
+ ValidationIssue if violations found, None otherwise
291
+ """
292
+ # TODO: Implement your column validation logic
293
+ # Example: Check for null values
294
+ # violations = lf.filter(pl.col(col).is_null()).collect()
295
+ #
296
+ # if violations.height == 0:
297
+ # return None
298
+ #
299
+ # samples = violations[col].head(5).to_list()
300
+ # return ValidationIssue(
301
+ # column=col,
302
+ # issue_type=self.name,
303
+ # count=violations.height,
304
+ # severity=self._severity,
305
+ # details=f"Found {{violations.height}} issues in '{{col}}'",
306
+ # sample_values=samples,
307
+ # )
308
+
309
+ return None
310
+ '''
311
+ result.add_file(f"{config.name}/validator.py", content)
312
+
313
+ def _generate_pattern(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
314
+ """Generate pattern validator template."""
315
+ severity = config.extra.get("severity", "MEDIUM")
316
+ category = config.extra.get("category", config.category)
317
+ pattern = config.extra.get("pattern", r".*")
318
+
319
+ content = f'''{self._get_header(config)}
320
+
321
+ from __future__ import annotations
322
+
323
+ import re
324
+ from typing import Any
325
+
326
+ import polars as pl
327
+
328
+ from truthound.validators.base import (
329
+ Validator,
330
+ ValidationIssue,
331
+ ValidatorConfig,
332
+ RegexValidatorMixin,
333
+ StringValidatorMixin,
334
+ )
335
+ from truthound.validators.sdk import custom_validator
336
+ from truthound.types import Severity
337
+
338
+
339
+ @custom_validator(
340
+ name="{config.name}",
341
+ category="{category}",
342
+ description="{config.description or f'{config.title_name} pattern validator'}",
343
+ version="{config.version}",
344
+ author="{config.author}",
345
+ tags=["{category}", "pattern", "regex"],
346
+ )
347
+ class {config.class_name}Validator(Validator, StringValidatorMixin, RegexValidatorMixin):
348
+ """{config.description or f'{config.title_name} pattern validator.'}
349
+
350
+ Validates string values against a regex pattern.
351
+
352
+ Example:
353
+ >>> validator = {config.class_name}Validator(pattern=r"^[A-Z]{{2,3}}-\\\\d{{4}}$")
354
+ >>> issues = validator.validate(lf)
355
+ """
356
+
357
+ name: str = "{config.name}"
358
+ category: str = "{category}"
359
+ default_severity: Severity = Severity.{severity}
360
+
361
+ def __init__(
362
+ self,
363
+ config: ValidatorConfig | None = None,
364
+ pattern: str = r"{pattern}",
365
+ columns: tuple[str, ...] | list[str] | None = None,
366
+ case_sensitive: bool = True,
367
+ invert: bool = False,
368
+ severity: Severity = Severity.{severity},
369
+ **kwargs: Any,
370
+ ) -> None:
371
+ """Initialize the validator.
372
+
373
+ Args:
374
+ config: Validator configuration
375
+ pattern: Regex pattern to match
376
+ columns: Columns to validate
377
+ case_sensitive: Whether pattern matching is case sensitive
378
+ invert: If True, match values that DON'T match the pattern
379
+ severity: Severity level for issues
380
+ **kwargs: Additional keyword arguments
381
+ """
382
+ super().__init__(config, **kwargs)
383
+ self._pattern_str = pattern
384
+ self._columns = columns
385
+ self._case_sensitive = case_sensitive
386
+ self._invert = invert
387
+ self._severity = severity
388
+
389
+ # Compile pattern for validation
390
+ flags = 0 if case_sensitive else re.IGNORECASE
391
+ self._pattern = re.compile(pattern, flags)
392
+
393
+ def validate(self, lf: pl.LazyFrame) -> list[ValidationIssue]:
394
+ """Validate string columns against pattern.
395
+
396
+ Args:
397
+ lf: LazyFrame to validate
398
+
399
+ Returns:
400
+ List of validation issues
401
+ """
402
+ issues: list[ValidationIssue] = []
403
+ total_rows = lf.select(pl.len()).collect().item()
404
+
405
+ if total_rows == 0:
406
+ return issues
407
+
408
+ # Get string columns
409
+ columns = self._columns or self._get_string_columns(lf)
410
+
411
+ for column in columns:
412
+ if column not in lf.collect_schema().names():
413
+ continue
414
+
415
+ # Check pattern match
416
+ if self._invert:
417
+ # Find values that match (when we want non-matches)
418
+ violations = lf.filter(
419
+ pl.col(column).str.contains(self._pattern_str)
420
+ ).collect()
421
+ else:
422
+ # Find values that don't match (when we want matches)
423
+ violations = lf.filter(
424
+ pl.col(column).is_not_null()
425
+ & ~pl.col(column).str.contains(self._pattern_str)
426
+ ).collect()
427
+
428
+ if violations.height == 0:
429
+ continue
430
+
431
+ samples = violations[column].head(5).to_list()
432
+ match_type = "match" if self._invert else "do not match"
433
+
434
+ issues.append(ValidationIssue(
435
+ column=column,
436
+ issue_type=f"{{self.name}}_pattern_mismatch",
437
+ count=violations.height,
438
+ severity=self._severity,
439
+ details=(
440
+ f"Column '{{column}}' has {{violations.height}} values that "
441
+ f"{{match_type}} pattern '{{self._pattern_str}}'"
442
+ ),
443
+ sample_values=samples,
444
+ expected=self._pattern_str if not self._invert else f"NOT {{self._pattern_str}}",
445
+ ))
446
+
447
+ return issues
448
+ '''
449
+ result.add_file(f"{config.name}/validator.py", content)
450
+
451
+ def _generate_range(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
452
+ """Generate range validator template."""
453
+ severity = config.extra.get("severity", "MEDIUM")
454
+ category = config.extra.get("category", config.category)
455
+ min_value = config.extra.get("min_value")
456
+ max_value = config.extra.get("max_value")
457
+
458
+ min_val_str = str(min_value) if min_value is not None else "None"
459
+ max_val_str = str(max_value) if max_value is not None else "None"
460
+
461
+ content = f'''{self._get_header(config)}
462
+
463
+ from __future__ import annotations
464
+
465
+ from typing import Any
466
+
467
+ import polars as pl
468
+
469
+ from truthound.validators.base import (
470
+ Validator,
471
+ ValidationIssue,
472
+ ValidatorConfig,
473
+ NumericValidatorMixin,
474
+ NUMERIC_TYPES,
475
+ )
476
+ from truthound.validators.sdk import custom_validator
477
+ from truthound.types import Severity
478
+
479
+
480
+ @custom_validator(
481
+ name="{config.name}",
482
+ category="{category}",
483
+ description="{config.description or f'{config.title_name} range validator'}",
484
+ version="{config.version}",
485
+ author="{config.author}",
486
+ tags=["{category}", "range", "numeric"],
487
+ )
488
+ class {config.class_name}Validator(Validator, NumericValidatorMixin):
489
+ """{config.description or f'{config.title_name} range validator.'}
490
+
491
+ Validates numeric values are within a specified range.
492
+
493
+ Example:
494
+ >>> validator = {config.class_name}Validator(min_value=0, max_value=100)
495
+ >>> issues = validator.validate(lf)
496
+ """
497
+
498
+ name: str = "{config.name}"
499
+ category: str = "{category}"
500
+ default_severity: Severity = Severity.{severity}
501
+
502
+ def __init__(
503
+ self,
504
+ config: ValidatorConfig | None = None,
505
+ min_value: float | int | None = {min_val_str},
506
+ max_value: float | int | None = {max_val_str},
507
+ columns: tuple[str, ...] | list[str] | None = None,
508
+ inclusive: bool = True,
509
+ severity: Severity = Severity.{severity},
510
+ **kwargs: Any,
511
+ ) -> None:
512
+ """Initialize the validator.
513
+
514
+ Args:
515
+ config: Validator configuration
516
+ min_value: Minimum allowed value (None = no minimum)
517
+ max_value: Maximum allowed value (None = no maximum)
518
+ columns: Columns to validate
519
+ inclusive: Whether bounds are inclusive
520
+ severity: Severity level for issues
521
+ **kwargs: Additional keyword arguments
522
+ """
523
+ super().__init__(config, **kwargs)
524
+ self._min_value = min_value
525
+ self._max_value = max_value
526
+ self._columns = columns
527
+ self._inclusive = inclusive
528
+ self._severity = severity
529
+
530
+ if min_value is None and max_value is None:
531
+ raise ValueError("At least one of min_value or max_value must be specified")
532
+
533
+ def _build_violation_expr(self, col: str) -> pl.Expr:
534
+ """Build expression to find violations."""
535
+ conditions: list[pl.Expr] = []
536
+
537
+ if self._min_value is not None:
538
+ if self._inclusive:
539
+ conditions.append(pl.col(col) < self._min_value)
540
+ else:
541
+ conditions.append(pl.col(col) <= self._min_value)
542
+
543
+ if self._max_value is not None:
544
+ if self._inclusive:
545
+ conditions.append(pl.col(col) > self._max_value)
546
+ else:
547
+ conditions.append(pl.col(col) >= self._max_value)
548
+
549
+ if len(conditions) == 1:
550
+ return conditions[0]
551
+ return conditions[0] | conditions[1]
552
+
553
+ def _format_range(self) -> str:
554
+ """Format the expected range for display."""
555
+ min_bracket = "[" if self._inclusive else "("
556
+ max_bracket = "]" if self._inclusive else ")"
557
+ min_val = str(self._min_value) if self._min_value is not None else "-∞"
558
+ max_val = str(self._max_value) if self._max_value is not None else "+∞"
559
+ return f"{{min_bracket}}{{min_val}}, {{max_val}}{{max_bracket}}"
560
+
561
+ def validate(self, lf: pl.LazyFrame) -> list[ValidationIssue]:
562
+ """Validate numeric values are in range.
563
+
564
+ Args:
565
+ lf: LazyFrame to validate
566
+
567
+ Returns:
568
+ List of validation issues
569
+ """
570
+ issues: list[ValidationIssue] = []
571
+ total_rows = lf.select(pl.len()).collect().item()
572
+
573
+ if total_rows == 0:
574
+ return issues
575
+
576
+ # Get numeric columns
577
+ columns = self._columns or self._get_numeric_columns(lf)
578
+
579
+ for column in columns:
580
+ if column not in lf.collect_schema().names():
581
+ continue
582
+
583
+ violation_expr = self._build_violation_expr(column)
584
+ violations = lf.filter(violation_expr).collect()
585
+
586
+ if violations.height == 0:
587
+ continue
588
+
589
+ samples = violations[column].head(5).to_list()
590
+
591
+ issues.append(ValidationIssue(
592
+ column=column,
593
+ issue_type=f"{{self.name}}_out_of_range",
594
+ count=violations.height,
595
+ severity=self._severity,
596
+ details=(
597
+ f"Column '{{column}}' has {{violations.height}} values "
598
+ f"outside range {{self._format_range()}}"
599
+ ),
600
+ sample_values=samples,
601
+ expected=self._format_range(),
602
+ ))
603
+
604
+ return issues
605
+ '''
606
+ result.add_file(f"{config.name}/validator.py", content)
607
+
608
+ def _generate_comparison(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
609
+ """Generate comparison validator template."""
610
+ severity = config.extra.get("severity", "HIGH")
611
+ category = config.extra.get("category", config.category)
612
+
613
+ content = f'''{self._get_header(config)}
614
+
615
+ from __future__ import annotations
616
+
617
+ from typing import Any
618
+
619
+ import polars as pl
620
+
621
+ from truthound.validators.base import Validator, ValidationIssue, ValidatorConfig
622
+ from truthound.validators.sdk import custom_validator
623
+ from truthound.types import Severity
624
+
625
+
626
+ @custom_validator(
627
+ name="{config.name}",
628
+ category="{category}",
629
+ description="{config.description or f'{config.title_name} comparison validator'}",
630
+ version="{config.version}",
631
+ author="{config.author}",
632
+ tags=["{category}", "comparison", "cross_column"],
633
+ )
634
+ class {config.class_name}Validator(Validator):
635
+ """{config.description or f'{config.title_name} comparison validator.'}
636
+
637
+ Compares values between columns.
638
+
639
+ Supported operators:
640
+ - eq: left == right
641
+ - ne: left != right
642
+ - lt: left < right
643
+ - le: left <= right
644
+ - gt: left > right
645
+ - ge: left >= right
646
+
647
+ Example:
648
+ >>> validator = {config.class_name}Validator(
649
+ ... left_column="start_date",
650
+ ... right_column="end_date",
651
+ ... operator="lt",
652
+ ... )
653
+ >>> issues = validator.validate(lf)
654
+ """
655
+
656
+ name: str = "{config.name}"
657
+ category: str = "{category}"
658
+ default_severity: Severity = Severity.{severity}
659
+
660
+ OPERATORS = {{
661
+ "eq": ("==", lambda l, r: l != r), # Violation when NOT equal
662
+ "ne": ("!=", lambda l, r: l == r), # Violation when equal
663
+ "lt": ("<", lambda l, r: l >= r), # Violation when >=
664
+ "le": ("<=", lambda l, r: l > r), # Violation when >
665
+ "gt": (">", lambda l, r: l <= r), # Violation when <=
666
+ "ge": (">=", lambda l, r: l < r), # Violation when <
667
+ }}
668
+
669
+ def __init__(
670
+ self,
671
+ config: ValidatorConfig | None = None,
672
+ left_column: str = "",
673
+ right_column: str = "",
674
+ operator: str = "eq",
675
+ severity: Severity = Severity.{severity},
676
+ **kwargs: Any,
677
+ ) -> None:
678
+ """Initialize the validator.
679
+
680
+ Args:
681
+ config: Validator configuration
682
+ left_column: Left side column name
683
+ right_column: Right side column name
684
+ operator: Comparison operator
685
+ severity: Severity level for issues
686
+ **kwargs: Additional keyword arguments
687
+ """
688
+ super().__init__(config, **kwargs)
689
+ self._left_column = left_column
690
+ self._right_column = right_column
691
+ self._operator = operator
692
+ self._severity = severity
693
+
694
+ if not left_column or not right_column:
695
+ raise ValueError("Both left_column and right_column must be specified")
696
+
697
+ if operator not in self.OPERATORS:
698
+ raise ValueError(
699
+ f"Invalid operator '{{operator}}'. "
700
+ f"Valid operators: {{list(self.OPERATORS.keys())}}"
701
+ )
702
+
703
+ def _build_violation_expr(self) -> pl.Expr:
704
+ """Build expression to find violations."""
705
+ left = pl.col(self._left_column)
706
+ right = pl.col(self._right_column)
707
+ _, expr_builder = self.OPERATORS[self._operator]
708
+ return expr_builder(left, right)
709
+
710
+ def validate(self, lf: pl.LazyFrame) -> list[ValidationIssue]:
711
+ """Validate column comparison.
712
+
713
+ Args:
714
+ lf: LazyFrame to validate
715
+
716
+ Returns:
717
+ List of validation issues
718
+ """
719
+ issues: list[ValidationIssue] = []
720
+
721
+ # Check columns exist
722
+ schema = lf.collect_schema()
723
+ available = schema.names()
724
+
725
+ if self._left_column not in available:
726
+ self.logger.warning(f"Column '{{self._left_column}}' not found")
727
+ return issues
728
+
729
+ if self._right_column not in available:
730
+ self.logger.warning(f"Column '{{self._right_column}}' not found")
731
+ return issues
732
+
733
+ total_rows = lf.select(pl.len()).collect().item()
734
+ if total_rows == 0:
735
+ return issues
736
+
737
+ violation_expr = self._build_violation_expr()
738
+ violations = lf.filter(violation_expr).collect()
739
+
740
+ if violations.height == 0:
741
+ return issues
742
+
743
+ samples = violations.select([self._left_column, self._right_column]).head(5).to_dicts()
744
+ symbol, _ = self.OPERATORS[self._operator]
745
+
746
+ issues.append(ValidationIssue(
747
+ column=f"{{self._left_column}}, {{self._right_column}}",
748
+ issue_type=f"{{self.name}}_comparison_failed",
749
+ count=violations.height,
750
+ severity=self._severity,
751
+ details=(
752
+ f"Expected: {{self._left_column}} {{symbol}} {{self._right_column}}. "
753
+ f"Found {{violations.height}} violations."
754
+ ),
755
+ sample_values=samples,
756
+ expected=f"{{self._left_column}} {{symbol}} {{self._right_column}}",
757
+ ))
758
+
759
+ return issues
760
+ '''
761
+ result.add_file(f"{config.name}/validator.py", content)
762
+
763
+ def _generate_composite(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
764
+ """Generate composite validator template."""
765
+ severity = config.extra.get("severity", "MEDIUM")
766
+ category = config.extra.get("category", config.category)
767
+
768
+ content = f'''{self._get_header(config)}
769
+
770
+ from __future__ import annotations
771
+
772
+ from typing import Any
773
+
774
+ import polars as pl
775
+
776
+ from truthound.validators.base import Validator, ValidationIssue, ValidatorConfig
777
+ from truthound.validators.sdk import custom_validator, CompositeValidator
778
+ from truthound.types import Severity
779
+
780
+
781
+ @custom_validator(
782
+ name="{config.name}",
783
+ category="{category}",
784
+ description="{config.description or f'{config.title_name} composite validator'}",
785
+ version="{config.version}",
786
+ author="{config.author}",
787
+ tags=["{category}", "composite"],
788
+ )
789
+ class {config.class_name}Validator(CompositeValidator):
790
+ """{config.description or f'{config.title_name} composite validator.'}
791
+
792
+ Combines multiple validators into one validation pass.
793
+
794
+ Example:
795
+ >>> validator = {config.class_name}Validator()
796
+ >>> issues = validator.validate(lf)
797
+
798
+ # Or with custom validators
799
+ >>> validator = {config.class_name}Validator(validators=[
800
+ ... NullValidator(columns=("id",)),
801
+ ... UniqueValidator(columns=("id",)),
802
+ ... ])
803
+ """
804
+
805
+ name: str = "{config.name}"
806
+ category: str = "{category}"
807
+
808
+ def __init__(
809
+ self,
810
+ validators: list[Validator] | None = None,
811
+ config: ValidatorConfig | None = None,
812
+ **kwargs: Any,
813
+ ) -> None:
814
+ """Initialize the validator.
815
+
816
+ Args:
817
+ validators: List of validators to run
818
+ config: Validator configuration
819
+ **kwargs: Additional keyword arguments
820
+ """
821
+ super().__init__(validators=validators, config=config, **kwargs)
822
+
823
+ def get_validators(self) -> list[Validator]:
824
+ """Get the list of validators to run.
825
+
826
+ Override this method to define validators at the class level.
827
+ You can combine this with validators passed to __init__.
828
+
829
+ Returns:
830
+ List of Validator instances
831
+ """
832
+ validators = super().get_validators()
833
+
834
+ # TODO: Add your default validators here
835
+ # Example:
836
+ # from truthound.validators import NullValidator, UniqueValidator
837
+ # validators.extend([
838
+ # NullValidator(columns=("id", "name")),
839
+ # UniqueValidator(columns=("id",)),
840
+ # ])
841
+
842
+ return validators
843
+ '''
844
+ result.add_file(f"{config.name}/validator.py", content)
845
+
846
+ def _generate_full(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
847
+ """Generate full-featured validator template."""
848
+ # Generate column validator as the base
849
+ self._generate_column(config, result)
850
+
851
+ # Always include tests and docs for full template
852
+ self._generate_tests(config, result)
853
+ self._generate_docs(config, result)
854
+ self._generate_example(config, result)
855
+
856
+ def _generate_tests(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
857
+ """Generate test file."""
858
+ content = f'''"""Tests for {config.class_name}Validator."""
859
+
860
+ import pytest
861
+ import polars as pl
862
+
863
+ from truthound.validators.sdk.testing import (
864
+ ValidatorTestCase,
865
+ create_test_dataframe,
866
+ assert_no_issues,
867
+ assert_has_issue,
868
+ assert_issue_count,
869
+ )
870
+ from {config.name} import {config.class_name}Validator
871
+
872
+
873
+ class Test{config.class_name}Validator(ValidatorTestCase):
874
+ """Test cases for {config.class_name}Validator."""
875
+
876
+ def test_valid_data_produces_no_issues(self):
877
+ """Test that valid data produces no issues."""
878
+ # Arrange
879
+ lf = pl.LazyFrame({{
880
+ "column1": [1, 2, 3, 4, 5],
881
+ "column2": ["a", "b", "c", "d", "e"],
882
+ }})
883
+ validator = {config.class_name}Validator()
884
+
885
+ # Act
886
+ issues = validator.validate(lf)
887
+
888
+ # Assert
889
+ assert_no_issues(issues)
890
+
891
+ def test_invalid_data_produces_issues(self):
892
+ """Test that invalid data produces issues."""
893
+ # Arrange
894
+ lf = pl.LazyFrame({{
895
+ "column1": [None, 2, None, 4, None],
896
+ "column2": ["a", None, "c", None, "e"],
897
+ }})
898
+ validator = {config.class_name}Validator()
899
+
900
+ # Act
901
+ issues = validator.validate(lf)
902
+
903
+ # Assert
904
+ # TODO: Update assertion based on your validation logic
905
+ # Example:
906
+ # assert_has_issue(issues, issue_type="{config.name}")
907
+ # assert_issue_count(issues, 2)
908
+ pass
909
+
910
+ def test_empty_dataframe_returns_no_issues(self):
911
+ """Test with empty dataframe."""
912
+ # Arrange
913
+ lf = pl.LazyFrame({{
914
+ "column1": [],
915
+ "column2": [],
916
+ }})
917
+ validator = {config.class_name}Validator()
918
+
919
+ # Act
920
+ issues = validator.validate(lf)
921
+
922
+ # Assert
923
+ assert_no_issues(issues)
924
+
925
+ def test_specific_columns(self):
926
+ """Test validation with specific columns."""
927
+ # Arrange
928
+ lf = pl.LazyFrame({{
929
+ "target_col": [1, 2, 3],
930
+ "other_col": [None, None, None],
931
+ }})
932
+ validator = {config.class_name}Validator(columns=["target_col"])
933
+
934
+ # Act
935
+ issues = validator.validate(lf)
936
+
937
+ # Assert
938
+ # Only target_col should be validated
939
+ for issue in issues:
940
+ assert issue.column == "target_col"
941
+
942
+ def test_severity_override(self):
943
+ """Test that severity can be overridden."""
944
+ from truthound.types import Severity
945
+
946
+ # Arrange
947
+ lf = pl.LazyFrame({{"column1": [None, 2, 3]}})
948
+ validator = {config.class_name}Validator(severity=Severity.CRITICAL)
949
+
950
+ # Act
951
+ issues = validator.validate(lf)
952
+
953
+ # Assert
954
+ for issue in issues:
955
+ assert issue.severity == Severity.CRITICAL
956
+
957
+ def test_configuration_options(self):
958
+ """Test validator configuration options."""
959
+ # TODO: Add tests for your specific configuration options
960
+ pass
961
+
962
+ def test_edge_cases(self):
963
+ """Test edge cases."""
964
+ # TODO: Add edge case tests
965
+ pass
966
+
967
+
968
+ class Test{config.class_name}ValidatorPerformance:
969
+ """Performance tests for {config.class_name}Validator."""
970
+
971
+ @pytest.mark.slow
972
+ def test_large_dataset_performance(self):
973
+ """Test performance with large dataset."""
974
+ import time
975
+
976
+ # Arrange
977
+ n_rows = 1_000_000
978
+ lf = pl.LazyFrame({{
979
+ "column1": list(range(n_rows)),
980
+ "column2": ["value"] * n_rows,
981
+ }})
982
+ validator = {config.class_name}Validator()
983
+
984
+ # Act
985
+ start = time.time()
986
+ issues = validator.validate(lf)
987
+ elapsed = time.time() - start
988
+
989
+ # Assert - should complete in reasonable time
990
+ assert elapsed < 10.0, f"Validation took too long: {{elapsed:.2f}}s"
991
+ '''
992
+ result.add_file(f"{config.name}/tests/__init__.py", "")
993
+ result.add_file(f"{config.name}/tests/test_validator.py", content)
994
+
995
+ def _generate_docs(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
996
+ """Generate documentation file."""
997
+ content = f'''# {config.class_name}Validator
998
+
999
+ > {config.category} validator
1000
+
1001
+ ## Description
1002
+
1003
+ {config.description or 'TODO: Add description'}
1004
+
1005
+ ## Installation
1006
+
1007
+ This validator is part of the Truthound package:
1008
+
1009
+ ```bash
1010
+ pip install truthound
1011
+ ```
1012
+
1013
+ ## Usage
1014
+
1015
+ ```python
1016
+ from {config.name} import {config.class_name}Validator
1017
+
1018
+ # Create validator
1019
+ validator = {config.class_name}Validator()
1020
+
1021
+ # Validate data
1022
+ issues = validator.validate(lf)
1023
+ ```
1024
+
1025
+ ## Configuration
1026
+
1027
+ | Parameter | Type | Default | Description |
1028
+ |-----------|------|---------|-------------|
1029
+ | columns | list[str] | None | Columns to validate (None = all) |
1030
+ | severity | Severity | MEDIUM | Default severity level |
1031
+
1032
+ ## Examples
1033
+
1034
+ ### Basic Usage
1035
+
1036
+ ```python
1037
+ import polars as pl
1038
+ from {config.name} import {config.class_name}Validator
1039
+
1040
+ # Create sample data
1041
+ lf = pl.LazyFrame({{
1042
+ "value": [1, 2, 3, 4, 5],
1043
+ "name": ["a", "b", "c", "d", "e"],
1044
+ }})
1045
+
1046
+ # Create and run validator
1047
+ validator = {config.class_name}Validator()
1048
+ issues = validator.validate(lf)
1049
+
1050
+ print(f"Found {{len(issues)}} issues")
1051
+ ```
1052
+
1053
+ ### With Specific Columns
1054
+
1055
+ ```python
1056
+ validator = {config.class_name}Validator(columns=["value"])
1057
+ issues = validator.validate(lf)
1058
+ ```
1059
+
1060
+ ### With Custom Severity
1061
+
1062
+ ```python
1063
+ from truthound.types import Severity
1064
+
1065
+ validator = {config.class_name}Validator(severity=Severity.CRITICAL)
1066
+ issues = validator.validate(lf)
1067
+ ```
1068
+
1069
+ ## See Also
1070
+
1071
+ - [Validator SDK](https://github.com/seadonggyun4/Truthound/docs/sdk.md)
1072
+ - [Testing Guide](https://github.com/seadonggyun4/Truthound/docs/testing.md)
1073
+
1074
+ ---
1075
+
1076
+ *Version: {config.version}*
1077
+ *Author: {config.author or 'Unknown'}*
1078
+ *License: {config.license_type}*
1079
+ '''
1080
+ result.add_file(f"{config.name}/docs/README.md", content)
1081
+
1082
+ def _generate_example(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
1083
+ """Generate example usage file."""
1084
+ content = f'''#!/usr/bin/env python3
1085
+ """Example usage of {config.class_name}Validator."""
1086
+
1087
+ import polars as pl
1088
+
1089
+ from {config.name} import {config.class_name}Validator
1090
+
1091
+
1092
+ def main():
1093
+ """Run example validation."""
1094
+ # Create sample data
1095
+ lf = pl.LazyFrame({{
1096
+ "id": [1, 2, 3, 4, 5],
1097
+ "value": [10, 20, None, 40, 50],
1098
+ "name": ["Alice", "Bob", "", "David", "Eve"],
1099
+ }})
1100
+
1101
+ print("Sample data:")
1102
+ print(lf.collect())
1103
+ print()
1104
+
1105
+ # Create and run validator
1106
+ validator = {config.class_name}Validator()
1107
+ issues = validator.validate(lf)
1108
+
1109
+ # Print results
1110
+ print(f"Validation Results: {{len(issues)}} issues found")
1111
+ print("-" * 50)
1112
+
1113
+ for issue in issues:
1114
+ print(f" Column: {{issue.column}}")
1115
+ print(f" Type: {{issue.issue_type}}")
1116
+ print(f" Count: {{issue.count}}")
1117
+ print(f" Severity: {{issue.severity.value}}")
1118
+ print(f" Details: {{issue.details}}")
1119
+ if issue.sample_values:
1120
+ print(f" Samples: {{issue.sample_values[:5]}}")
1121
+ print()
1122
+
1123
+
1124
+ if __name__ == "__main__":
1125
+ main()
1126
+ '''
1127
+ result.add_file(f"{config.name}/examples/basic_usage.py", content, executable=True)