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,1150 @@
1
+ """Mixins for common reporter functionality.
2
+
3
+ This module provides reusable mixins that can be combined with reporter
4
+ base classes to add common functionality:
5
+
6
+ - FormattingMixin: Text formatting, tables, colors
7
+ - AggregationMixin: Grouping, counting, statistics
8
+ - FilteringMixin: Filtering by severity, column, validator
9
+ - SerializationMixin: JSON, YAML, XML serialization helpers
10
+ - TemplatingMixin: Template rendering with Jinja2
11
+ - StreamingMixin: Streaming output for large datasets
12
+
13
+ Example:
14
+ >>> class MyReporter(FormattingMixin, FilteringMixin, ValidationReporter[Config]):
15
+ ... def render(self, data):
16
+ ... issues = self.filter_by_severity(data, min_severity="medium")
17
+ ... return self.format_as_table(issues)
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import csv
23
+ import io
24
+ import json
25
+ from abc import ABC
26
+ from collections import defaultdict
27
+ from dataclasses import asdict, is_dataclass
28
+ from datetime import datetime, date
29
+ from enum import Enum
30
+ from typing import (
31
+ TYPE_CHECKING,
32
+ Any,
33
+ Callable,
34
+ Iterator,
35
+ Sequence,
36
+ TypeVar,
37
+ )
38
+
39
+ if TYPE_CHECKING:
40
+ from truthound.stores.results import ValidationResult, ValidatorResult
41
+
42
+
43
+ T = TypeVar("T")
44
+
45
+
46
+ # =============================================================================
47
+ # Severity Ordering
48
+ # =============================================================================
49
+
50
+ SEVERITY_ORDER = {
51
+ "critical": 0,
52
+ "high": 1,
53
+ "medium": 2,
54
+ "low": 3,
55
+ "info": 4,
56
+ "none": 5,
57
+ }
58
+
59
+
60
+ def _get_severity_level(severity: str | None) -> int:
61
+ """Get numeric severity level for comparison."""
62
+ if severity is None:
63
+ return SEVERITY_ORDER.get("none", 5)
64
+ return SEVERITY_ORDER.get(severity.lower(), 5)
65
+
66
+
67
+ # =============================================================================
68
+ # Formatting Mixin
69
+ # =============================================================================
70
+
71
+
72
+ class FormattingMixin(ABC):
73
+ """Mixin providing text formatting utilities.
74
+
75
+ Provides methods for:
76
+ - Table formatting (ASCII, Markdown, HTML)
77
+ - Text alignment and padding
78
+ - Color/style helpers
79
+ - Number formatting
80
+ - Date/time formatting
81
+ """
82
+
83
+ # -------------------------------------------------------------------------
84
+ # Table Formatting
85
+ # -------------------------------------------------------------------------
86
+
87
+ def format_as_table(
88
+ self,
89
+ rows: Sequence[dict[str, Any]],
90
+ columns: list[str] | None = None,
91
+ headers: dict[str, str] | None = None,
92
+ style: str = "ascii",
93
+ max_width: int | None = None,
94
+ ) -> str:
95
+ """Format data as a text table.
96
+
97
+ Args:
98
+ rows: Sequence of dictionaries representing rows.
99
+ columns: Column names to include (default: all columns from first row).
100
+ headers: Optional header labels (column name -> display name).
101
+ style: Table style ("ascii", "markdown", "simple", "grid").
102
+ max_width: Maximum column width (truncates content).
103
+
104
+ Returns:
105
+ Formatted table as string.
106
+
107
+ Example:
108
+ >>> rows = [{"name": "col1", "count": 10}, {"name": "col2", "count": 5}]
109
+ >>> print(self.format_as_table(rows, style="markdown"))
110
+ | name | count |
111
+ |------|-------|
112
+ | col1 | 10 |
113
+ | col2 | 5 |
114
+ """
115
+ if not rows:
116
+ return ""
117
+
118
+ # Determine columns
119
+ if columns is None:
120
+ columns = list(rows[0].keys())
121
+
122
+ headers = headers or {}
123
+
124
+ # Calculate column widths
125
+ widths: dict[str, int] = {}
126
+ for col in columns:
127
+ header_text = headers.get(col, col)
128
+ widths[col] = len(str(header_text))
129
+ for row in rows:
130
+ value = str(row.get(col, ""))
131
+ if max_width:
132
+ value = value[:max_width]
133
+ widths[col] = max(widths[col], len(value))
134
+
135
+ # Build table based on style
136
+ if style == "markdown":
137
+ return self._format_markdown_table(rows, columns, headers, widths, max_width)
138
+ elif style == "grid":
139
+ return self._format_grid_table(rows, columns, headers, widths, max_width)
140
+ elif style == "simple":
141
+ return self._format_simple_table(rows, columns, headers, widths, max_width)
142
+ else: # ascii
143
+ return self._format_ascii_table(rows, columns, headers, widths, max_width)
144
+
145
+ def _format_markdown_table(
146
+ self,
147
+ rows: Sequence[dict[str, Any]],
148
+ columns: list[str],
149
+ headers: dict[str, str],
150
+ widths: dict[str, int],
151
+ max_width: int | None,
152
+ ) -> str:
153
+ """Format as Markdown table."""
154
+ lines = []
155
+
156
+ # Header row
157
+ header_cells = [
158
+ headers.get(col, col).ljust(widths[col]) for col in columns
159
+ ]
160
+ lines.append("| " + " | ".join(header_cells) + " |")
161
+
162
+ # Separator
163
+ sep_cells = ["-" * widths[col] for col in columns]
164
+ lines.append("| " + " | ".join(sep_cells) + " |")
165
+
166
+ # Data rows
167
+ for row in rows:
168
+ cells = []
169
+ for col in columns:
170
+ value = str(row.get(col, ""))
171
+ if max_width:
172
+ value = value[:max_width]
173
+ cells.append(value.ljust(widths[col]))
174
+ lines.append("| " + " | ".join(cells) + " |")
175
+
176
+ return "\n".join(lines)
177
+
178
+ def _format_ascii_table(
179
+ self,
180
+ rows: Sequence[dict[str, Any]],
181
+ columns: list[str],
182
+ headers: dict[str, str],
183
+ widths: dict[str, int],
184
+ max_width: int | None,
185
+ ) -> str:
186
+ """Format as ASCII table with borders."""
187
+ lines = []
188
+
189
+ # Top border
190
+ border = "+" + "+".join("-" * (widths[col] + 2) for col in columns) + "+"
191
+ lines.append(border)
192
+
193
+ # Header row
194
+ header_cells = [
195
+ " " + headers.get(col, col).ljust(widths[col]) + " " for col in columns
196
+ ]
197
+ lines.append("|" + "|".join(header_cells) + "|")
198
+ lines.append(border)
199
+
200
+ # Data rows
201
+ for row in rows:
202
+ cells = []
203
+ for col in columns:
204
+ value = str(row.get(col, ""))
205
+ if max_width:
206
+ value = value[:max_width]
207
+ cells.append(" " + value.ljust(widths[col]) + " ")
208
+ lines.append("|" + "|".join(cells) + "|")
209
+
210
+ lines.append(border)
211
+ return "\n".join(lines)
212
+
213
+ def _format_grid_table(
214
+ self,
215
+ rows: Sequence[dict[str, Any]],
216
+ columns: list[str],
217
+ headers: dict[str, str],
218
+ widths: dict[str, int],
219
+ max_width: int | None,
220
+ ) -> str:
221
+ """Format as grid table with double borders."""
222
+ lines = []
223
+
224
+ # Top border
225
+ border = "╔" + "╤".join("═" * (widths[col] + 2) for col in columns) + "╗"
226
+ lines.append(border)
227
+
228
+ # Header row
229
+ header_cells = [
230
+ " " + headers.get(col, col).ljust(widths[col]) + " " for col in columns
231
+ ]
232
+ lines.append("║" + "│".join(header_cells) + "║")
233
+
234
+ # Header separator
235
+ sep = "╠" + "╪".join("═" * (widths[col] + 2) for col in columns) + "╣"
236
+ lines.append(sep)
237
+
238
+ # Data rows
239
+ for i, row in enumerate(rows):
240
+ cells = []
241
+ for col in columns:
242
+ value = str(row.get(col, ""))
243
+ if max_width:
244
+ value = value[:max_width]
245
+ cells.append(" " + value.ljust(widths[col]) + " ")
246
+ lines.append("║" + "│".join(cells) + "║")
247
+
248
+ # Bottom border
249
+ border = "╚" + "╧".join("═" * (widths[col] + 2) for col in columns) + "╝"
250
+ lines.append(border)
251
+ return "\n".join(lines)
252
+
253
+ def _format_simple_table(
254
+ self,
255
+ rows: Sequence[dict[str, Any]],
256
+ columns: list[str],
257
+ headers: dict[str, str],
258
+ widths: dict[str, int],
259
+ max_width: int | None,
260
+ ) -> str:
261
+ """Format as simple table with minimal separators."""
262
+ lines = []
263
+
264
+ # Header row
265
+ header_cells = [headers.get(col, col).ljust(widths[col]) for col in columns]
266
+ lines.append(" ".join(header_cells))
267
+
268
+ # Separator
269
+ sep_cells = ["-" * widths[col] for col in columns]
270
+ lines.append(" ".join(sep_cells))
271
+
272
+ # Data rows
273
+ for row in rows:
274
+ cells = []
275
+ for col in columns:
276
+ value = str(row.get(col, ""))
277
+ if max_width:
278
+ value = value[:max_width]
279
+ cells.append(value.ljust(widths[col]))
280
+ lines.append(" ".join(cells))
281
+
282
+ return "\n".join(lines)
283
+
284
+ # -------------------------------------------------------------------------
285
+ # Text Formatting
286
+ # -------------------------------------------------------------------------
287
+
288
+ def truncate(self, text: str, max_length: int, suffix: str = "...") -> str:
289
+ """Truncate text to maximum length.
290
+
291
+ Args:
292
+ text: Text to truncate.
293
+ max_length: Maximum length including suffix.
294
+ suffix: Suffix to add when truncating.
295
+
296
+ Returns:
297
+ Truncated text.
298
+ """
299
+ if len(text) <= max_length:
300
+ return text
301
+ return text[: max_length - len(suffix)] + suffix
302
+
303
+ def indent(self, text: str, prefix: str = " ", first_line: bool = True) -> str:
304
+ """Indent text with a prefix.
305
+
306
+ Args:
307
+ text: Text to indent.
308
+ prefix: Prefix to add to each line.
309
+ first_line: Whether to indent the first line.
310
+
311
+ Returns:
312
+ Indented text.
313
+ """
314
+ lines = text.split("\n")
315
+ if first_line:
316
+ return "\n".join(prefix + line for line in lines)
317
+ return lines[0] + "\n" + "\n".join(prefix + line for line in lines[1:])
318
+
319
+ def wrap(self, text: str, width: int = 80) -> str:
320
+ """Wrap text to specified width.
321
+
322
+ Args:
323
+ text: Text to wrap.
324
+ width: Maximum line width.
325
+
326
+ Returns:
327
+ Wrapped text.
328
+ """
329
+ import textwrap
330
+
331
+ return textwrap.fill(text, width=width)
332
+
333
+ # -------------------------------------------------------------------------
334
+ # Number Formatting
335
+ # -------------------------------------------------------------------------
336
+
337
+ def format_number(
338
+ self,
339
+ value: int | float,
340
+ precision: int = 2,
341
+ thousands_sep: str = ",",
342
+ ) -> str:
343
+ """Format a number with thousands separator.
344
+
345
+ Args:
346
+ value: Number to format.
347
+ precision: Decimal precision for floats.
348
+ thousands_sep: Thousands separator character.
349
+
350
+ Returns:
351
+ Formatted number string.
352
+ """
353
+ if isinstance(value, float):
354
+ formatted = f"{value:,.{precision}f}"
355
+ else:
356
+ formatted = f"{value:,}"
357
+ return formatted.replace(",", thousands_sep)
358
+
359
+ def format_percentage(
360
+ self,
361
+ value: float,
362
+ precision: int = 1,
363
+ include_sign: bool = False,
364
+ ) -> str:
365
+ """Format a value as percentage.
366
+
367
+ Args:
368
+ value: Value to format (0.0 to 1.0 or 0 to 100).
369
+ precision: Decimal precision.
370
+ include_sign: Whether to include + sign for positive values.
371
+
372
+ Returns:
373
+ Formatted percentage string.
374
+ """
375
+ # Assume value is already a percentage if > 1
376
+ if abs(value) <= 1:
377
+ value = value * 100
378
+
379
+ if include_sign and value > 0:
380
+ return f"+{value:.{precision}f}%"
381
+ return f"{value:.{precision}f}%"
382
+
383
+ def format_bytes(self, size: int) -> str:
384
+ """Format byte size as human readable.
385
+
386
+ Args:
387
+ size: Size in bytes.
388
+
389
+ Returns:
390
+ Human readable size (e.g., "1.5 MB").
391
+ """
392
+ for unit in ["B", "KB", "MB", "GB", "TB"]:
393
+ if abs(size) < 1024:
394
+ return f"{size:.1f} {unit}"
395
+ size /= 1024 # type: ignore
396
+ return f"{size:.1f} PB"
397
+
398
+ # -------------------------------------------------------------------------
399
+ # Date/Time Formatting
400
+ # -------------------------------------------------------------------------
401
+
402
+ def format_datetime(
403
+ self,
404
+ dt: datetime,
405
+ format: str = "%Y-%m-%d %H:%M:%S",
406
+ ) -> str:
407
+ """Format datetime object.
408
+
409
+ Args:
410
+ dt: Datetime to format.
411
+ format: strftime format string.
412
+
413
+ Returns:
414
+ Formatted datetime string.
415
+ """
416
+ return dt.strftime(format)
417
+
418
+ def format_duration(self, seconds: float) -> str:
419
+ """Format duration as human readable.
420
+
421
+ Args:
422
+ seconds: Duration in seconds.
423
+
424
+ Returns:
425
+ Human readable duration (e.g., "2h 30m 15s").
426
+ """
427
+ if seconds < 1:
428
+ return f"{seconds * 1000:.0f}ms"
429
+ if seconds < 60:
430
+ return f"{seconds:.1f}s"
431
+
432
+ minutes, secs = divmod(int(seconds), 60)
433
+ hours, minutes = divmod(minutes, 60)
434
+ days, hours = divmod(hours, 24)
435
+
436
+ parts = []
437
+ if days:
438
+ parts.append(f"{days}d")
439
+ if hours:
440
+ parts.append(f"{hours}h")
441
+ if minutes:
442
+ parts.append(f"{minutes}m")
443
+ if secs or not parts:
444
+ parts.append(f"{secs}s")
445
+
446
+ return " ".join(parts)
447
+
448
+ def format_relative_time(self, dt: datetime) -> str:
449
+ """Format datetime as relative time.
450
+
451
+ Args:
452
+ dt: Datetime to format.
453
+
454
+ Returns:
455
+ Relative time string (e.g., "5 minutes ago").
456
+ """
457
+ now = datetime.now(dt.tzinfo)
458
+ delta = now - dt
459
+
460
+ seconds = delta.total_seconds()
461
+
462
+ if seconds < 60:
463
+ return "just now"
464
+ if seconds < 3600:
465
+ mins = int(seconds / 60)
466
+ return f"{mins} minute{'s' if mins != 1 else ''} ago"
467
+ if seconds < 86400:
468
+ hours = int(seconds / 3600)
469
+ return f"{hours} hour{'s' if hours != 1 else ''} ago"
470
+ if seconds < 604800:
471
+ days = int(seconds / 86400)
472
+ return f"{days} day{'s' if days != 1 else ''} ago"
473
+
474
+ return self.format_datetime(dt, "%Y-%m-%d")
475
+
476
+
477
+ # =============================================================================
478
+ # Aggregation Mixin
479
+ # =============================================================================
480
+
481
+
482
+ class AggregationMixin(ABC):
483
+ """Mixin providing data aggregation utilities.
484
+
485
+ Provides methods for:
486
+ - Grouping by various keys
487
+ - Counting and statistics
488
+ - Summarization
489
+ """
490
+
491
+ def group_by_column(
492
+ self,
493
+ results: list["ValidatorResult"],
494
+ ) -> dict[str, list["ValidatorResult"]]:
495
+ """Group validator results by column.
496
+
497
+ Args:
498
+ results: List of validator results.
499
+
500
+ Returns:
501
+ Dictionary mapping column name to results.
502
+ """
503
+ grouped: dict[str, list[ValidatorResult]] = defaultdict(list)
504
+ for result in results:
505
+ column = result.column or "_table_"
506
+ grouped[column].append(result)
507
+ return dict(grouped)
508
+
509
+ def group_by_severity(
510
+ self,
511
+ results: list["ValidatorResult"],
512
+ ) -> dict[str, list["ValidatorResult"]]:
513
+ """Group validator results by severity.
514
+
515
+ Args:
516
+ results: List of validator results.
517
+
518
+ Returns:
519
+ Dictionary mapping severity to results.
520
+ """
521
+ grouped: dict[str, list[ValidatorResult]] = defaultdict(list)
522
+ for result in results:
523
+ severity = result.severity or "unknown"
524
+ grouped[severity.lower()].append(result)
525
+ return dict(grouped)
526
+
527
+ def group_by_validator(
528
+ self,
529
+ results: list["ValidatorResult"],
530
+ ) -> dict[str, list["ValidatorResult"]]:
531
+ """Group validator results by validator name.
532
+
533
+ Args:
534
+ results: List of validator results.
535
+
536
+ Returns:
537
+ Dictionary mapping validator name to results.
538
+ """
539
+ grouped: dict[str, list[ValidatorResult]] = defaultdict(list)
540
+ for result in results:
541
+ grouped[result.validator_name].append(result)
542
+ return dict(grouped)
543
+
544
+ def group_by(
545
+ self,
546
+ results: list["ValidatorResult"],
547
+ key: Callable[["ValidatorResult"], str],
548
+ ) -> dict[str, list["ValidatorResult"]]:
549
+ """Group validator results by custom key function.
550
+
551
+ Args:
552
+ results: List of validator results.
553
+ key: Function to extract grouping key from result.
554
+
555
+ Returns:
556
+ Dictionary mapping key to results.
557
+ """
558
+ grouped: dict[str, list[ValidatorResult]] = defaultdict(list)
559
+ for result in results:
560
+ grouped[key(result)].append(result)
561
+ return dict(grouped)
562
+
563
+ def count_by_severity(
564
+ self,
565
+ results: list["ValidatorResult"],
566
+ ) -> dict[str, int]:
567
+ """Count results by severity.
568
+
569
+ Args:
570
+ results: List of validator results.
571
+
572
+ Returns:
573
+ Dictionary mapping severity to count.
574
+ """
575
+ counts: dict[str, int] = defaultdict(int)
576
+ for result in results:
577
+ severity = result.severity or "unknown"
578
+ counts[severity.lower()] += 1
579
+ return dict(counts)
580
+
581
+ def count_by_column(
582
+ self,
583
+ results: list["ValidatorResult"],
584
+ ) -> dict[str, int]:
585
+ """Count results by column.
586
+
587
+ Args:
588
+ results: List of validator results.
589
+
590
+ Returns:
591
+ Dictionary mapping column to count.
592
+ """
593
+ counts: dict[str, int] = defaultdict(int)
594
+ for result in results:
595
+ column = result.column or "_table_"
596
+ counts[column] += 1
597
+ return dict(counts)
598
+
599
+ def get_summary_stats(
600
+ self,
601
+ result: "ValidationResult",
602
+ ) -> dict[str, Any]:
603
+ """Get summary statistics from validation result.
604
+
605
+ Args:
606
+ result: Validation result.
607
+
608
+ Returns:
609
+ Dictionary with summary statistics.
610
+ """
611
+ failed = [r for r in result.results if not r.success]
612
+
613
+ return {
614
+ "total_validators": len(result.results),
615
+ "passed": len(result.results) - len(failed),
616
+ "failed": len(failed),
617
+ "pass_rate": (
618
+ (len(result.results) - len(failed)) / len(result.results) * 100
619
+ if result.results
620
+ else 0
621
+ ),
622
+ "by_severity": self.count_by_severity(failed),
623
+ "by_column": self.count_by_column(failed),
624
+ "most_affected_column": (
625
+ max(self.count_by_column(failed).items(), key=lambda x: x[1])[0]
626
+ if failed
627
+ else None
628
+ ),
629
+ }
630
+
631
+
632
+ # =============================================================================
633
+ # Filtering Mixin
634
+ # =============================================================================
635
+
636
+
637
+ class FilteringMixin(ABC):
638
+ """Mixin providing data filtering utilities.
639
+
640
+ Provides methods for:
641
+ - Filtering by severity, column, validator
642
+ - Sorting results
643
+ - Limiting/pagination
644
+ """
645
+
646
+ def filter_by_severity(
647
+ self,
648
+ results: list["ValidatorResult"],
649
+ min_severity: str | None = None,
650
+ max_severity: str | None = None,
651
+ include_severities: list[str] | None = None,
652
+ exclude_severities: list[str] | None = None,
653
+ ) -> list["ValidatorResult"]:
654
+ """Filter results by severity.
655
+
656
+ Args:
657
+ results: List of validator results.
658
+ min_severity: Minimum severity to include.
659
+ max_severity: Maximum severity to include.
660
+ include_severities: List of severities to include (exclusive with min/max).
661
+ exclude_severities: List of severities to exclude.
662
+
663
+ Returns:
664
+ Filtered list of results.
665
+ """
666
+ filtered = []
667
+
668
+ for result in results:
669
+ severity = (result.severity or "none").lower()
670
+ level = _get_severity_level(severity)
671
+
672
+ # Check include list
673
+ if include_severities:
674
+ if severity not in [s.lower() for s in include_severities]:
675
+ continue
676
+ else:
677
+ # Check min severity
678
+ if min_severity:
679
+ min_level = _get_severity_level(min_severity)
680
+ if level > min_level:
681
+ continue
682
+
683
+ # Check max severity
684
+ if max_severity:
685
+ max_level = _get_severity_level(max_severity)
686
+ if level < max_level:
687
+ continue
688
+
689
+ # Check exclude list
690
+ if exclude_severities:
691
+ if severity in [s.lower() for s in exclude_severities]:
692
+ continue
693
+
694
+ filtered.append(result)
695
+
696
+ return filtered
697
+
698
+ def filter_by_column(
699
+ self,
700
+ results: list["ValidatorResult"],
701
+ include_columns: list[str] | None = None,
702
+ exclude_columns: list[str] | None = None,
703
+ ) -> list["ValidatorResult"]:
704
+ """Filter results by column.
705
+
706
+ Args:
707
+ results: List of validator results.
708
+ include_columns: Columns to include.
709
+ exclude_columns: Columns to exclude.
710
+
711
+ Returns:
712
+ Filtered list of results.
713
+ """
714
+ filtered = []
715
+
716
+ for result in results:
717
+ column = result.column or "_table_"
718
+
719
+ if include_columns and column not in include_columns:
720
+ continue
721
+ if exclude_columns and column in exclude_columns:
722
+ continue
723
+
724
+ filtered.append(result)
725
+
726
+ return filtered
727
+
728
+ def filter_by_validator(
729
+ self,
730
+ results: list["ValidatorResult"],
731
+ include_validators: list[str] | None = None,
732
+ exclude_validators: list[str] | None = None,
733
+ ) -> list["ValidatorResult"]:
734
+ """Filter results by validator name.
735
+
736
+ Args:
737
+ results: List of validator results.
738
+ include_validators: Validator names to include.
739
+ exclude_validators: Validator names to exclude.
740
+
741
+ Returns:
742
+ Filtered list of results.
743
+ """
744
+ filtered = []
745
+
746
+ for result in results:
747
+ if include_validators and result.validator_name not in include_validators:
748
+ continue
749
+ if exclude_validators and result.validator_name in exclude_validators:
750
+ continue
751
+
752
+ filtered.append(result)
753
+
754
+ return filtered
755
+
756
+ def filter_failed(
757
+ self,
758
+ results: list["ValidatorResult"],
759
+ ) -> list["ValidatorResult"]:
760
+ """Filter to only failed results.
761
+
762
+ Args:
763
+ results: List of validator results.
764
+
765
+ Returns:
766
+ List of failed results.
767
+ """
768
+ return [r for r in results if not r.success]
769
+
770
+ def filter_passed(
771
+ self,
772
+ results: list["ValidatorResult"],
773
+ ) -> list["ValidatorResult"]:
774
+ """Filter to only passed results.
775
+
776
+ Args:
777
+ results: List of validator results.
778
+
779
+ Returns:
780
+ List of passed results.
781
+ """
782
+ return [r for r in results if r.success]
783
+
784
+ def sort_by_severity(
785
+ self,
786
+ results: list["ValidatorResult"],
787
+ ascending: bool = False,
788
+ ) -> list["ValidatorResult"]:
789
+ """Sort results by severity.
790
+
791
+ Args:
792
+ results: List of validator results.
793
+ ascending: If True, sort from low to critical.
794
+
795
+ Returns:
796
+ Sorted list of results.
797
+ """
798
+ return sorted(
799
+ results,
800
+ key=lambda r: _get_severity_level(r.severity),
801
+ reverse=not ascending,
802
+ )
803
+
804
+ def sort_by_column(
805
+ self,
806
+ results: list["ValidatorResult"],
807
+ ascending: bool = True,
808
+ ) -> list["ValidatorResult"]:
809
+ """Sort results by column name.
810
+
811
+ Args:
812
+ results: List of validator results.
813
+ ascending: Sort order.
814
+
815
+ Returns:
816
+ Sorted list of results.
817
+ """
818
+ return sorted(
819
+ results,
820
+ key=lambda r: r.column or "",
821
+ reverse=not ascending,
822
+ )
823
+
824
+ def limit(
825
+ self,
826
+ results: list["ValidatorResult"],
827
+ count: int,
828
+ offset: int = 0,
829
+ ) -> list["ValidatorResult"]:
830
+ """Limit results with optional offset.
831
+
832
+ Args:
833
+ results: List of validator results.
834
+ count: Maximum number of results.
835
+ offset: Number of results to skip.
836
+
837
+ Returns:
838
+ Limited list of results.
839
+ """
840
+ return results[offset : offset + count]
841
+
842
+
843
+ # =============================================================================
844
+ # Serialization Mixin
845
+ # =============================================================================
846
+
847
+
848
+ class SerializationMixin(ABC):
849
+ """Mixin providing serialization utilities.
850
+
851
+ Provides methods for:
852
+ - JSON serialization with custom encoders
853
+ - XML element building
854
+ - YAML formatting
855
+ - CSV generation
856
+ """
857
+
858
+ def to_json(
859
+ self,
860
+ data: Any,
861
+ indent: int | None = 2,
862
+ sort_keys: bool = False,
863
+ ensure_ascii: bool = False,
864
+ ) -> str:
865
+ """Serialize data to JSON.
866
+
867
+ Args:
868
+ data: Data to serialize.
869
+ indent: Indentation level (None for compact).
870
+ sort_keys: Whether to sort dictionary keys.
871
+ ensure_ascii: Whether to escape non-ASCII characters.
872
+
873
+ Returns:
874
+ JSON string.
875
+ """
876
+ return json.dumps(
877
+ data,
878
+ indent=indent,
879
+ sort_keys=sort_keys,
880
+ ensure_ascii=ensure_ascii,
881
+ default=self._json_encoder,
882
+ )
883
+
884
+ def _json_encoder(self, obj: Any) -> Any:
885
+ """Custom JSON encoder for special types."""
886
+ if isinstance(obj, datetime):
887
+ return obj.isoformat()
888
+ if isinstance(obj, date):
889
+ return obj.isoformat()
890
+ if isinstance(obj, Enum):
891
+ return obj.value
892
+ if is_dataclass(obj) and not isinstance(obj, type):
893
+ return asdict(obj)
894
+ if hasattr(obj, "to_dict"):
895
+ return obj.to_dict()
896
+ raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
897
+
898
+ def to_csv(
899
+ self,
900
+ rows: Sequence[dict[str, Any]],
901
+ columns: list[str] | None = None,
902
+ delimiter: str = ",",
903
+ include_header: bool = True,
904
+ ) -> str:
905
+ """Serialize data to CSV.
906
+
907
+ Args:
908
+ rows: Sequence of dictionaries.
909
+ columns: Column order (default: keys from first row).
910
+ delimiter: Field delimiter.
911
+ include_header: Whether to include header row.
912
+
913
+ Returns:
914
+ CSV string.
915
+ """
916
+ if not rows:
917
+ return ""
918
+
919
+ if columns is None:
920
+ columns = list(rows[0].keys())
921
+
922
+ output = io.StringIO()
923
+ writer = csv.DictWriter(
924
+ output,
925
+ fieldnames=columns,
926
+ delimiter=delimiter,
927
+ extrasaction="ignore",
928
+ )
929
+
930
+ if include_header:
931
+ writer.writeheader()
932
+
933
+ for row in rows:
934
+ # Convert non-string values
935
+ converted = {}
936
+ for col in columns:
937
+ value = row.get(col, "")
938
+ if isinstance(value, (list, dict)):
939
+ value = json.dumps(value)
940
+ elif isinstance(value, datetime):
941
+ value = value.isoformat()
942
+ elif isinstance(value, Enum):
943
+ value = value.value
944
+ converted[col] = value
945
+ writer.writerow(converted)
946
+
947
+ return output.getvalue()
948
+
949
+ def to_xml_element(
950
+ self,
951
+ tag: str,
952
+ value: Any = None,
953
+ attributes: dict[str, str] | None = None,
954
+ children: list[str] | None = None,
955
+ ) -> str:
956
+ """Create an XML element string.
957
+
958
+ Args:
959
+ tag: Element tag name.
960
+ value: Text content.
961
+ attributes: Element attributes.
962
+ children: Child element strings.
963
+
964
+ Returns:
965
+ XML element string.
966
+ """
967
+ attrs = ""
968
+ if attributes:
969
+ attrs = " " + " ".join(f'{k}="{self._escape_xml(str(v))}"' for k, v in attributes.items())
970
+
971
+ if children:
972
+ content = "\n".join(children)
973
+ return f"<{tag}{attrs}>\n{content}\n</{tag}>"
974
+ elif value is not None:
975
+ return f"<{tag}{attrs}>{self._escape_xml(str(value))}</{tag}>"
976
+ else:
977
+ return f"<{tag}{attrs}/>"
978
+
979
+ def _escape_xml(self, text: str) -> str:
980
+ """Escape special XML characters."""
981
+ return (
982
+ text.replace("&", "&amp;")
983
+ .replace("<", "&lt;")
984
+ .replace(">", "&gt;")
985
+ .replace('"', "&quot;")
986
+ .replace("'", "&apos;")
987
+ )
988
+
989
+
990
+ # =============================================================================
991
+ # Templating Mixin
992
+ # =============================================================================
993
+
994
+
995
+ class TemplatingMixin(ABC):
996
+ """Mixin providing template rendering utilities.
997
+
998
+ Provides methods for:
999
+ - Jinja2 template rendering
1000
+ - Template string interpolation
1001
+ - Conditional content
1002
+ """
1003
+
1004
+ _jinja_env: Any = None
1005
+
1006
+ def render_template(
1007
+ self,
1008
+ template: str,
1009
+ context: dict[str, Any],
1010
+ ) -> str:
1011
+ """Render a Jinja2 template string.
1012
+
1013
+ Args:
1014
+ template: Jinja2 template string.
1015
+ context: Template context variables.
1016
+
1017
+ Returns:
1018
+ Rendered template.
1019
+
1020
+ Raises:
1021
+ ImportError: If jinja2 is not installed.
1022
+ """
1023
+ try:
1024
+ from jinja2 import Environment, BaseLoader
1025
+
1026
+ if self._jinja_env is None:
1027
+ self._jinja_env = Environment(loader=BaseLoader())
1028
+
1029
+ tmpl = self._jinja_env.from_string(template)
1030
+ return tmpl.render(**context)
1031
+
1032
+ except ImportError:
1033
+ raise ImportError(
1034
+ "jinja2 is required for template rendering. "
1035
+ "Install with: pip install jinja2"
1036
+ )
1037
+
1038
+ def render_template_file(
1039
+ self,
1040
+ template_path: str,
1041
+ context: dict[str, Any],
1042
+ ) -> str:
1043
+ """Render a Jinja2 template file.
1044
+
1045
+ Args:
1046
+ template_path: Path to template file.
1047
+ context: Template context variables.
1048
+
1049
+ Returns:
1050
+ Rendered template.
1051
+ """
1052
+ try:
1053
+ from jinja2 import Environment, FileSystemLoader
1054
+ import os
1055
+
1056
+ template_dir = os.path.dirname(template_path)
1057
+ template_name = os.path.basename(template_path)
1058
+
1059
+ env = Environment(loader=FileSystemLoader(template_dir))
1060
+ tmpl = env.get_template(template_name)
1061
+ return tmpl.render(**context)
1062
+
1063
+ except ImportError:
1064
+ raise ImportError(
1065
+ "jinja2 is required for template rendering. "
1066
+ "Install with: pip install jinja2"
1067
+ )
1068
+
1069
+ def interpolate(
1070
+ self,
1071
+ template: str,
1072
+ context: dict[str, Any],
1073
+ ) -> str:
1074
+ """Simple string interpolation without Jinja2.
1075
+
1076
+ Args:
1077
+ template: Template string with {key} placeholders.
1078
+ context: Values to interpolate.
1079
+
1080
+ Returns:
1081
+ Interpolated string.
1082
+ """
1083
+ return template.format(**context)
1084
+
1085
+
1086
+ # =============================================================================
1087
+ # Streaming Mixin
1088
+ # =============================================================================
1089
+
1090
+
1091
+ class StreamingMixin(ABC):
1092
+ """Mixin providing streaming output utilities.
1093
+
1094
+ Provides methods for:
1095
+ - Streaming output for large datasets
1096
+ - Progress tracking
1097
+ - Chunked processing
1098
+ """
1099
+
1100
+ def stream_results(
1101
+ self,
1102
+ results: list["ValidatorResult"],
1103
+ chunk_size: int = 100,
1104
+ ) -> Iterator[list["ValidatorResult"]]:
1105
+ """Stream results in chunks.
1106
+
1107
+ Args:
1108
+ results: List of validator results.
1109
+ chunk_size: Number of results per chunk.
1110
+
1111
+ Yields:
1112
+ Chunks of validator results.
1113
+ """
1114
+ for i in range(0, len(results), chunk_size):
1115
+ yield results[i : i + chunk_size]
1116
+
1117
+ def stream_lines(
1118
+ self,
1119
+ results: list["ValidatorResult"],
1120
+ formatter: Callable[["ValidatorResult"], str],
1121
+ ) -> Iterator[str]:
1122
+ """Stream formatted lines for each result.
1123
+
1124
+ Args:
1125
+ results: List of validator results.
1126
+ formatter: Function to format each result.
1127
+
1128
+ Yields:
1129
+ Formatted lines.
1130
+ """
1131
+ for result in results:
1132
+ yield formatter(result)
1133
+
1134
+ def render_streaming(
1135
+ self,
1136
+ results: list["ValidatorResult"],
1137
+ formatter: Callable[["ValidatorResult"], str],
1138
+ separator: str = "\n",
1139
+ ) -> str:
1140
+ """Render all results using streaming formatter.
1141
+
1142
+ Args:
1143
+ results: List of validator results.
1144
+ formatter: Function to format each result.
1145
+ separator: Separator between formatted results.
1146
+
1147
+ Returns:
1148
+ Complete rendered output.
1149
+ """
1150
+ return separator.join(self.stream_lines(results, formatter))