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,968 @@
1
+ """Testing utilities for reporter development.
2
+
3
+ This module provides tools for testing custom reporters including
4
+ mock data generators, assertion helpers, and test case base classes.
5
+
6
+ Example:
7
+ >>> from truthound.reporters.sdk.testing import (
8
+ ... ReporterTestCase,
9
+ ... create_mock_result,
10
+ ... assert_valid_output,
11
+ ... )
12
+ >>>
13
+ >>> class TestMyReporter(ReporterTestCase):
14
+ ... reporter_class = MyCustomReporter
15
+ ...
16
+ ... def test_render_output(self):
17
+ ... result = create_mock_result(passed=5, failed=3)
18
+ ... output = self.reporter.render(result)
19
+ ... assert_valid_output(output, format="json")
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import uuid
26
+ from abc import ABC
27
+ from dataclasses import dataclass, field
28
+ from datetime import datetime, timedelta
29
+ from enum import Enum
30
+ from typing import Any, Callable, Dict, List, Optional, Type, Union
31
+ from xml.etree import ElementTree
32
+
33
+ __all__ = [
34
+ # Test case base
35
+ "ReporterTestCase",
36
+ # Mock data generators
37
+ "create_mock_result",
38
+ "create_mock_results",
39
+ "create_mock_validator_result",
40
+ "MockResultBuilder",
41
+ # Assertions
42
+ "assert_valid_output",
43
+ "assert_json_valid",
44
+ "assert_xml_valid",
45
+ "assert_csv_valid",
46
+ "assert_contains_patterns",
47
+ # Fixtures
48
+ "create_sample_data",
49
+ "create_edge_case_data",
50
+ "create_stress_test_data",
51
+ # Utilities
52
+ "capture_output",
53
+ "benchmark_reporter",
54
+ ]
55
+
56
+
57
+ class Severity(Enum):
58
+ """Severity levels for mock validation results."""
59
+
60
+ INFO = "info"
61
+ WARNING = "warning"
62
+ ERROR = "error"
63
+ CRITICAL = "critical"
64
+
65
+
66
+ @dataclass
67
+ class MockValidatorResult:
68
+ """Mock validator result for testing."""
69
+
70
+ validator_name: str
71
+ column: Optional[str] = None
72
+ passed: bool = True
73
+ message: str = ""
74
+ severity: Severity = Severity.ERROR
75
+ details: Dict[str, Any] = field(default_factory=dict)
76
+ row_count: int = 100
77
+ failed_count: int = 0
78
+ execution_time_ms: float = 10.0
79
+ timestamp: Optional[datetime] = None
80
+
81
+ def __post_init__(self):
82
+ if self.timestamp is None:
83
+ self.timestamp = datetime.now()
84
+ if not self.message:
85
+ if self.passed:
86
+ self.message = f"Validation passed for {self.column or 'data'}"
87
+ else:
88
+ self.message = f"Validation failed for {self.column or 'data'}"
89
+
90
+ def to_dict(self) -> Dict[str, Any]:
91
+ """Convert to dictionary representation."""
92
+ return {
93
+ "validator_name": self.validator_name,
94
+ "column": self.column,
95
+ "passed": self.passed,
96
+ "message": self.message,
97
+ "severity": self.severity.value,
98
+ "details": self.details,
99
+ "row_count": self.row_count,
100
+ "failed_count": self.failed_count,
101
+ "execution_time_ms": self.execution_time_ms,
102
+ "timestamp": self.timestamp.isoformat() if self.timestamp else None,
103
+ }
104
+
105
+
106
+ @dataclass
107
+ class MockValidationResult:
108
+ """Mock validation result for testing reporters."""
109
+
110
+ run_id: str = field(default_factory=lambda: str(uuid.uuid4()))
111
+ data_asset: str = "test_data.csv"
112
+ validator_results: List[MockValidatorResult] = field(default_factory=list)
113
+ success: bool = True
114
+ started_at: Optional[datetime] = None
115
+ completed_at: Optional[datetime] = None
116
+ metadata: Dict[str, Any] = field(default_factory=dict)
117
+ row_count: int = 1000
118
+ columns: List[str] = field(default_factory=list)
119
+
120
+ def __post_init__(self):
121
+ if self.started_at is None:
122
+ self.started_at = datetime.now() - timedelta(seconds=5)
123
+ if self.completed_at is None:
124
+ self.completed_at = datetime.now()
125
+ if not self.columns:
126
+ self.columns = ["id", "name", "email", "age", "status"]
127
+ if not self.validator_results:
128
+ # Generate some default results
129
+ self._generate_default_results()
130
+
131
+ def _generate_default_results(self):
132
+ """Generate default validator results."""
133
+ self.validator_results = [
134
+ MockValidatorResult(
135
+ validator_name="not_null",
136
+ column="id",
137
+ passed=True,
138
+ row_count=self.row_count,
139
+ ),
140
+ MockValidatorResult(
141
+ validator_name="unique",
142
+ column="id",
143
+ passed=True,
144
+ row_count=self.row_count,
145
+ ),
146
+ MockValidatorResult(
147
+ validator_name="email_format",
148
+ column="email",
149
+ passed=self.success,
150
+ failed_count=0 if self.success else 5,
151
+ row_count=self.row_count,
152
+ ),
153
+ ]
154
+
155
+ @property
156
+ def passed_count(self) -> int:
157
+ """Number of passed validations."""
158
+ return sum(1 for r in self.validator_results if r.passed)
159
+
160
+ @property
161
+ def failed_count(self) -> int:
162
+ """Number of failed validations."""
163
+ return sum(1 for r in self.validator_results if not r.passed)
164
+
165
+ @property
166
+ def total_count(self) -> int:
167
+ """Total number of validations."""
168
+ return len(self.validator_results)
169
+
170
+ @property
171
+ def success_rate(self) -> float:
172
+ """Success rate as a percentage."""
173
+ if self.total_count == 0:
174
+ return 100.0
175
+ return (self.passed_count / self.total_count) * 100
176
+
177
+ @property
178
+ def duration_ms(self) -> float:
179
+ """Total duration in milliseconds."""
180
+ if self.started_at and self.completed_at:
181
+ return (self.completed_at - self.started_at).total_seconds() * 1000
182
+ return 0.0
183
+
184
+ def to_dict(self) -> Dict[str, Any]:
185
+ """Convert to dictionary representation."""
186
+ return {
187
+ "run_id": self.run_id,
188
+ "data_asset": self.data_asset,
189
+ "success": self.success,
190
+ "validator_results": [r.to_dict() for r in self.validator_results],
191
+ "passed_count": self.passed_count,
192
+ "failed_count": self.failed_count,
193
+ "total_count": self.total_count,
194
+ "success_rate": self.success_rate,
195
+ "row_count": self.row_count,
196
+ "columns": self.columns,
197
+ "started_at": self.started_at.isoformat() if self.started_at else None,
198
+ "completed_at": self.completed_at.isoformat() if self.completed_at else None,
199
+ "duration_ms": self.duration_ms,
200
+ "metadata": self.metadata,
201
+ }
202
+
203
+
204
+ class MockResultBuilder:
205
+ """Fluent builder for creating mock validation results.
206
+
207
+ Example:
208
+ >>> result = (
209
+ ... MockResultBuilder()
210
+ ... .with_data_asset("users.csv")
211
+ ... .with_row_count(10000)
212
+ ... .add_passed("not_null", column="id")
213
+ ... .add_passed("unique", column="id")
214
+ ... .add_failed("email_format", column="email", failed_count=50)
215
+ ... .build()
216
+ ... )
217
+ """
218
+
219
+ def __init__(self):
220
+ self._run_id: str = str(uuid.uuid4())
221
+ self._data_asset: str = "test_data.csv"
222
+ self._validator_results: List[MockValidatorResult] = []
223
+ self._metadata: Dict[str, Any] = {}
224
+ self._row_count: int = 1000
225
+ self._columns: List[str] = []
226
+ self._started_at: Optional[datetime] = None
227
+ self._completed_at: Optional[datetime] = None
228
+
229
+ def with_run_id(self, run_id: str) -> "MockResultBuilder":
230
+ """Set the run ID."""
231
+ self._run_id = run_id
232
+ return self
233
+
234
+ def with_data_asset(self, data_asset: str) -> "MockResultBuilder":
235
+ """Set the data asset name."""
236
+ self._data_asset = data_asset
237
+ return self
238
+
239
+ def with_row_count(self, count: int) -> "MockResultBuilder":
240
+ """Set the row count."""
241
+ self._row_count = count
242
+ return self
243
+
244
+ def with_columns(self, columns: List[str]) -> "MockResultBuilder":
245
+ """Set the column names."""
246
+ self._columns = columns
247
+ return self
248
+
249
+ def with_metadata(self, metadata: Dict[str, Any]) -> "MockResultBuilder":
250
+ """Set metadata."""
251
+ self._metadata = metadata
252
+ return self
253
+
254
+ def with_timestamps(
255
+ self,
256
+ started_at: datetime,
257
+ completed_at: Optional[datetime] = None,
258
+ ) -> "MockResultBuilder":
259
+ """Set timestamps."""
260
+ self._started_at = started_at
261
+ self._completed_at = completed_at or datetime.now()
262
+ return self
263
+
264
+ def add_result(self, result: MockValidatorResult) -> "MockResultBuilder":
265
+ """Add a validator result."""
266
+ self._validator_results.append(result)
267
+ return self
268
+
269
+ def add_passed(
270
+ self,
271
+ validator_name: str,
272
+ column: Optional[str] = None,
273
+ **kwargs,
274
+ ) -> "MockResultBuilder":
275
+ """Add a passed validation result."""
276
+ result = MockValidatorResult(
277
+ validator_name=validator_name,
278
+ column=column,
279
+ passed=True,
280
+ row_count=self._row_count,
281
+ **kwargs,
282
+ )
283
+ self._validator_results.append(result)
284
+ return self
285
+
286
+ def add_failed(
287
+ self,
288
+ validator_name: str,
289
+ column: Optional[str] = None,
290
+ failed_count: int = 1,
291
+ severity: Severity = Severity.ERROR,
292
+ **kwargs,
293
+ ) -> "MockResultBuilder":
294
+ """Add a failed validation result."""
295
+ result = MockValidatorResult(
296
+ validator_name=validator_name,
297
+ column=column,
298
+ passed=False,
299
+ failed_count=failed_count,
300
+ severity=severity,
301
+ row_count=self._row_count,
302
+ **kwargs,
303
+ )
304
+ self._validator_results.append(result)
305
+ return self
306
+
307
+ def add_warning(
308
+ self,
309
+ validator_name: str,
310
+ column: Optional[str] = None,
311
+ message: str = "",
312
+ **kwargs,
313
+ ) -> "MockResultBuilder":
314
+ """Add a warning validation result."""
315
+ result = MockValidatorResult(
316
+ validator_name=validator_name,
317
+ column=column,
318
+ passed=True,
319
+ severity=Severity.WARNING,
320
+ message=message,
321
+ row_count=self._row_count,
322
+ **kwargs,
323
+ )
324
+ self._validator_results.append(result)
325
+ return self
326
+
327
+ def build(self) -> MockValidationResult:
328
+ """Build the mock validation result."""
329
+ success = all(r.passed for r in self._validator_results)
330
+
331
+ return MockValidationResult(
332
+ run_id=self._run_id,
333
+ data_asset=self._data_asset,
334
+ validator_results=self._validator_results,
335
+ success=success,
336
+ started_at=self._started_at,
337
+ completed_at=self._completed_at,
338
+ metadata=self._metadata,
339
+ row_count=self._row_count,
340
+ columns=self._columns or ["id", "name", "email", "age", "status"],
341
+ )
342
+
343
+
344
+ def create_mock_result(
345
+ passed: int = 5,
346
+ failed: int = 0,
347
+ warnings: int = 0,
348
+ row_count: int = 1000,
349
+ data_asset: str = "test_data.csv",
350
+ ) -> MockValidationResult:
351
+ """Create a mock validation result with specified counts.
352
+
353
+ Args:
354
+ passed: Number of passed validations.
355
+ failed: Number of failed validations.
356
+ warnings: Number of warning validations.
357
+ row_count: Number of rows in the mock data.
358
+ data_asset: Name of the data asset.
359
+
360
+ Returns:
361
+ MockValidationResult with the specified configuration.
362
+
363
+ Example:
364
+ >>> result = create_mock_result(passed=10, failed=2)
365
+ >>> print(result.success_rate) # 83.33%
366
+ """
367
+ builder = MockResultBuilder().with_data_asset(data_asset).with_row_count(row_count)
368
+
369
+ validators = ["not_null", "unique", "range", "format", "regex", "length"]
370
+ columns = ["id", "name", "email", "age", "status", "created_at"]
371
+
372
+ for i in range(passed):
373
+ builder.add_passed(
374
+ validators[i % len(validators)],
375
+ column=columns[i % len(columns)],
376
+ )
377
+
378
+ for i in range(failed):
379
+ builder.add_failed(
380
+ validators[i % len(validators)],
381
+ column=columns[i % len(columns)],
382
+ failed_count=max(1, row_count // 100),
383
+ )
384
+
385
+ for i in range(warnings):
386
+ builder.add_warning(
387
+ validators[i % len(validators)],
388
+ column=columns[i % len(columns)],
389
+ message=f"Warning: potential issue in {columns[i % len(columns)]}",
390
+ )
391
+
392
+ return builder.build()
393
+
394
+
395
+ def create_mock_results(
396
+ count: int = 5,
397
+ success_rate: float = 0.8,
398
+ row_count: int = 1000,
399
+ ) -> List[MockValidationResult]:
400
+ """Create multiple mock validation results.
401
+
402
+ Args:
403
+ count: Number of results to create.
404
+ success_rate: Ratio of successful results (0.0 to 1.0).
405
+ row_count: Number of rows per result.
406
+
407
+ Returns:
408
+ List of MockValidationResult objects.
409
+
410
+ Example:
411
+ >>> results = create_mock_results(count=10, success_rate=0.7)
412
+ >>> successful = [r for r in results if r.success]
413
+ """
414
+ import random
415
+
416
+ results = []
417
+ for i in range(count):
418
+ is_success = random.random() < success_rate
419
+ passed = random.randint(3, 10)
420
+ failed = 0 if is_success else random.randint(1, 3)
421
+
422
+ result = create_mock_result(
423
+ passed=passed,
424
+ failed=failed,
425
+ row_count=row_count,
426
+ data_asset=f"data_{i:03d}.csv",
427
+ )
428
+ results.append(result)
429
+
430
+ return results
431
+
432
+
433
+ def create_mock_validator_result(
434
+ validator_name: str = "not_null",
435
+ column: Optional[str] = "id",
436
+ passed: bool = True,
437
+ **kwargs,
438
+ ) -> MockValidatorResult:
439
+ """Create a single mock validator result.
440
+
441
+ Args:
442
+ validator_name: Name of the validator.
443
+ column: Column being validated.
444
+ passed: Whether validation passed.
445
+ **kwargs: Additional arguments for MockValidatorResult.
446
+
447
+ Returns:
448
+ MockValidatorResult instance.
449
+ """
450
+ return MockValidatorResult(
451
+ validator_name=validator_name,
452
+ column=column,
453
+ passed=passed,
454
+ **kwargs,
455
+ )
456
+
457
+
458
+ class ReporterTestCase(ABC):
459
+ """Base class for reporter test cases.
460
+
461
+ Subclass this to create test suites for your custom reporters.
462
+ Provides common setup, teardown, and utility methods.
463
+
464
+ Example:
465
+ >>> class TestMyReporter(ReporterTestCase):
466
+ ... reporter_class = MyCustomReporter
467
+ ... reporter_kwargs = {"indent": 2}
468
+ ...
469
+ ... def test_basic_render(self):
470
+ ... result = self.create_result(passed=5, failed=1)
471
+ ... output = self.reporter.render(result)
472
+ ... self.assert_valid_output(output)
473
+ ...
474
+ ... def test_empty_result(self):
475
+ ... result = self.create_result(passed=0, failed=0)
476
+ ... output = self.reporter.render(result)
477
+ ... self.assertIsNotNone(output)
478
+ """
479
+
480
+ reporter_class: Optional[Type] = None
481
+ reporter_kwargs: Dict[str, Any] = {}
482
+
483
+ def setUp(self):
484
+ """Set up test fixtures."""
485
+ if self.reporter_class is not None:
486
+ self.reporter = self.reporter_class(**self.reporter_kwargs)
487
+ else:
488
+ self.reporter = None
489
+
490
+ def tearDown(self):
491
+ """Clean up after tests."""
492
+ self.reporter = None
493
+
494
+ def create_result(self, **kwargs) -> MockValidationResult:
495
+ """Create a mock validation result with the given parameters."""
496
+ return create_mock_result(**kwargs)
497
+
498
+ def create_results(self, count: int = 5, **kwargs) -> List[MockValidationResult]:
499
+ """Create multiple mock validation results."""
500
+ return create_mock_results(count=count, **kwargs)
501
+
502
+ def assert_valid_output(
503
+ self,
504
+ output: Any,
505
+ format: Optional[str] = None,
506
+ ) -> None:
507
+ """Assert that output is valid for the expected format."""
508
+ assert_valid_output(output, format=format)
509
+
510
+ def assert_json_structure(
511
+ self,
512
+ output: Any,
513
+ required_keys: Optional[List[str]] = None,
514
+ ) -> None:
515
+ """Assert JSON output has required structure."""
516
+ if isinstance(output, str):
517
+ data = json.loads(output)
518
+ else:
519
+ data = output
520
+
521
+ if required_keys:
522
+ for key in required_keys:
523
+ assert key in data, f"Missing required key: {key}"
524
+
525
+ def assert_contains_all(self, output: str, substrings: List[str]) -> None:
526
+ """Assert output contains all specified substrings."""
527
+ for substring in substrings:
528
+ assert substring in output, f"Missing expected content: {substring}"
529
+
530
+
531
+ def assert_valid_output(
532
+ output: Any,
533
+ format: Optional[str] = None,
534
+ ) -> None:
535
+ """Assert that output is valid.
536
+
537
+ Args:
538
+ output: The output to validate.
539
+ format: Expected format (json, xml, csv, text).
540
+
541
+ Raises:
542
+ AssertionError: If output is invalid.
543
+
544
+ Example:
545
+ >>> output = '{"success": true}'
546
+ >>> assert_valid_output(output, format="json") # OK
547
+ """
548
+ assert output is not None, "Output is None"
549
+
550
+ if format is None:
551
+ return
552
+
553
+ format = format.lower()
554
+
555
+ if format == "json":
556
+ assert_json_valid(output)
557
+ elif format == "xml":
558
+ assert_xml_valid(output)
559
+ elif format == "csv":
560
+ assert_csv_valid(output)
561
+ elif format == "text":
562
+ assert isinstance(output, str), f"Expected string, got {type(output)}"
563
+
564
+
565
+ def assert_json_valid(output: Any) -> Dict[str, Any]:
566
+ """Assert that output is valid JSON.
567
+
568
+ Args:
569
+ output: JSON string or dict to validate.
570
+
571
+ Returns:
572
+ Parsed JSON as dictionary.
573
+
574
+ Raises:
575
+ AssertionError: If output is not valid JSON.
576
+ """
577
+ if isinstance(output, dict):
578
+ return output
579
+
580
+ assert isinstance(output, str), f"Expected string or dict, got {type(output)}"
581
+
582
+ try:
583
+ return json.loads(output)
584
+ except json.JSONDecodeError as e:
585
+ raise AssertionError(f"Invalid JSON: {e}")
586
+
587
+
588
+ def assert_xml_valid(output: Any) -> ElementTree.Element:
589
+ """Assert that output is valid XML.
590
+
591
+ Args:
592
+ output: XML string or Element to validate.
593
+
594
+ Returns:
595
+ Parsed XML as Element.
596
+
597
+ Raises:
598
+ AssertionError: If output is not valid XML.
599
+ """
600
+ if isinstance(output, ElementTree.Element):
601
+ return output
602
+
603
+ assert isinstance(
604
+ output, (str, bytes)
605
+ ), f"Expected string, bytes, or Element, got {type(output)}"
606
+
607
+ try:
608
+ return ElementTree.fromstring(output)
609
+ except ElementTree.ParseError as e:
610
+ raise AssertionError(f"Invalid XML: {e}")
611
+
612
+
613
+ def assert_csv_valid(output: Any, has_header: bool = True) -> List[List[str]]:
614
+ """Assert that output is valid CSV.
615
+
616
+ Args:
617
+ output: CSV string to validate.
618
+ has_header: Whether CSV has a header row.
619
+
620
+ Returns:
621
+ Parsed CSV as list of rows.
622
+
623
+ Raises:
624
+ AssertionError: If output is not valid CSV.
625
+ """
626
+ import csv
627
+ from io import StringIO
628
+
629
+ assert isinstance(output, str), f"Expected string, got {type(output)}"
630
+
631
+ try:
632
+ reader = csv.reader(StringIO(output))
633
+ rows = list(reader)
634
+ assert len(rows) > 0, "CSV is empty"
635
+ return rows
636
+ except Exception as e:
637
+ raise AssertionError(f"Invalid CSV: {e}")
638
+
639
+
640
+ def assert_contains_patterns(
641
+ output: str,
642
+ patterns: List[str],
643
+ regex: bool = False,
644
+ ) -> None:
645
+ """Assert that output contains all specified patterns.
646
+
647
+ Args:
648
+ output: Output string to check.
649
+ patterns: List of patterns to find.
650
+ regex: If True, treat patterns as regular expressions.
651
+
652
+ Raises:
653
+ AssertionError: If any pattern is not found.
654
+
655
+ Example:
656
+ >>> output = "Total: 100 rows, 5 errors"
657
+ >>> assert_contains_patterns(output, ["Total:", "errors"])
658
+ """
659
+ import re
660
+
661
+ for pattern in patterns:
662
+ if regex:
663
+ match = re.search(pattern, output)
664
+ assert match is not None, f"Pattern not found: {pattern}"
665
+ else:
666
+ assert pattern in output, f"Pattern not found: {pattern}"
667
+
668
+
669
+ def create_sample_data() -> Dict[str, List[MockValidationResult]]:
670
+ """Create sample data for testing various scenarios.
671
+
672
+ Returns:
673
+ Dictionary mapping scenario names to mock results.
674
+
675
+ Example:
676
+ >>> samples = create_sample_data()
677
+ >>> for name, results in samples.items():
678
+ ... print(f"{name}: {len(results)} results")
679
+ """
680
+ return {
681
+ "all_passed": [create_mock_result(passed=10, failed=0)],
682
+ "all_failed": [create_mock_result(passed=0, failed=10)],
683
+ "mixed": [create_mock_result(passed=7, failed=3)],
684
+ "empty": [create_mock_result(passed=0, failed=0)],
685
+ "single_passed": [create_mock_result(passed=1, failed=0)],
686
+ "single_failed": [create_mock_result(passed=0, failed=1)],
687
+ "large_dataset": [create_mock_result(passed=50, failed=10, row_count=1000000)],
688
+ "multiple_runs": create_mock_results(count=5, success_rate=0.8),
689
+ }
690
+
691
+
692
+ def create_edge_case_data() -> Dict[str, MockValidationResult]:
693
+ """Create edge case data for testing boundary conditions.
694
+
695
+ Returns:
696
+ Dictionary mapping edge case names to mock results.
697
+
698
+ Example:
699
+ >>> edge_cases = create_edge_case_data()
700
+ >>> for name, result in edge_cases.items():
701
+ ... output = reporter.render(result)
702
+ ... assert_valid_output(output, format="json")
703
+ """
704
+ return {
705
+ "empty_validators": MockResultBuilder().build(),
706
+ "zero_rows": MockResultBuilder()
707
+ .with_row_count(0)
708
+ .add_passed("not_null", column="id")
709
+ .build(),
710
+ "unicode_data_asset": MockResultBuilder()
711
+ .with_data_asset("데이터_파일_🔥.csv")
712
+ .add_passed("not_null", column="이름")
713
+ .build(),
714
+ "special_chars": MockResultBuilder()
715
+ .with_data_asset('file"with<special>chars&.csv')
716
+ .add_failed(
717
+ "format",
718
+ column="col<with>chars",
719
+ message='Error: "value" is <invalid>',
720
+ )
721
+ .build(),
722
+ "long_column_names": MockResultBuilder()
723
+ .add_passed(
724
+ "not_null",
725
+ column="this_is_a_very_long_column_name_that_might_cause_formatting_issues_in_some_reporters",
726
+ )
727
+ .build(),
728
+ "many_validators": MockResultBuilder()
729
+ .with_row_count(1000)
730
+ ._build_many_validators(),
731
+ }
732
+
733
+
734
+ def _build_many_validators(builder: MockResultBuilder) -> MockValidationResult:
735
+ """Helper to build result with many validators."""
736
+ for i in range(100):
737
+ if i % 5 == 0:
738
+ builder.add_failed(f"validator_{i:03d}", column=f"col_{i % 10}")
739
+ else:
740
+ builder.add_passed(f"validator_{i:03d}", column=f"col_{i % 10}")
741
+ return builder.build()
742
+
743
+
744
+ # Monkey-patch the method
745
+ MockResultBuilder._build_many_validators = lambda self: _build_many_validators(self)
746
+
747
+
748
+ def create_stress_test_data(
749
+ num_validators: int = 1000,
750
+ num_rows: int = 10000000,
751
+ ) -> MockValidationResult:
752
+ """Create stress test data for performance testing.
753
+
754
+ Args:
755
+ num_validators: Number of validator results to generate.
756
+ num_rows: Number of rows in mock dataset.
757
+
758
+ Returns:
759
+ MockValidationResult for stress testing.
760
+
761
+ Example:
762
+ >>> import time
763
+ >>> data = create_stress_test_data(num_validators=10000)
764
+ >>> start = time.time()
765
+ >>> output = reporter.render(data)
766
+ >>> elapsed = time.time() - start
767
+ >>> print(f"Render time: {elapsed:.2f}s")
768
+ """
769
+ import random
770
+
771
+ builder = MockResultBuilder().with_row_count(num_rows).with_data_asset("stress_test.csv")
772
+
773
+ validators = ["not_null", "unique", "range", "format", "regex", "length", "custom"]
774
+ columns = [f"col_{i}" for i in range(50)]
775
+
776
+ for i in range(num_validators):
777
+ if random.random() < 0.9: # 90% pass rate
778
+ builder.add_passed(
779
+ random.choice(validators),
780
+ column=random.choice(columns),
781
+ )
782
+ else:
783
+ builder.add_failed(
784
+ random.choice(validators),
785
+ column=random.choice(columns),
786
+ failed_count=random.randint(1, num_rows // 1000),
787
+ )
788
+
789
+ return builder.build()
790
+
791
+
792
+ @dataclass
793
+ class CapturedOutput:
794
+ """Container for captured reporter output."""
795
+
796
+ content: Any
797
+ duration_ms: float
798
+ memory_bytes: int
799
+ exception: Optional[Exception] = None
800
+
801
+
802
+ def capture_output(
803
+ reporter: Any,
804
+ result: MockValidationResult,
805
+ method: str = "render",
806
+ ) -> CapturedOutput:
807
+ """Capture reporter output with timing and memory information.
808
+
809
+ Args:
810
+ reporter: The reporter instance.
811
+ result: Mock result to render.
812
+ method: Method name to call on reporter.
813
+
814
+ Returns:
815
+ CapturedOutput with content, timing, and memory info.
816
+
817
+ Example:
818
+ >>> captured = capture_output(my_reporter, result)
819
+ >>> print(f"Output length: {len(captured.content)}")
820
+ >>> print(f"Time: {captured.duration_ms:.2f}ms")
821
+ """
822
+ import time
823
+ import tracemalloc
824
+
825
+ tracemalloc.start()
826
+ start_time = time.perf_counter()
827
+ exception = None
828
+ content = None
829
+
830
+ try:
831
+ render_func = getattr(reporter, method)
832
+ content = render_func(result)
833
+ except Exception as e:
834
+ exception = e
835
+
836
+ end_time = time.perf_counter()
837
+ current, peak = tracemalloc.get_traced_memory()
838
+ tracemalloc.stop()
839
+
840
+ return CapturedOutput(
841
+ content=content,
842
+ duration_ms=(end_time - start_time) * 1000,
843
+ memory_bytes=peak,
844
+ exception=exception,
845
+ )
846
+
847
+
848
+ @dataclass
849
+ class BenchmarkResult:
850
+ """Result of a reporter benchmark."""
851
+
852
+ iterations: int
853
+ total_time_ms: float
854
+ avg_time_ms: float
855
+ min_time_ms: float
856
+ max_time_ms: float
857
+ p50_time_ms: float
858
+ p95_time_ms: float
859
+ p99_time_ms: float
860
+ peak_memory_bytes: int
861
+ output_size_bytes: int
862
+
863
+ def to_dict(self) -> Dict[str, Any]:
864
+ """Convert to dictionary representation."""
865
+ return {
866
+ "iterations": self.iterations,
867
+ "total_time_ms": self.total_time_ms,
868
+ "avg_time_ms": self.avg_time_ms,
869
+ "min_time_ms": self.min_time_ms,
870
+ "max_time_ms": self.max_time_ms,
871
+ "p50_time_ms": self.p50_time_ms,
872
+ "p95_time_ms": self.p95_time_ms,
873
+ "p99_time_ms": self.p99_time_ms,
874
+ "peak_memory_bytes": self.peak_memory_bytes,
875
+ "output_size_bytes": self.output_size_bytes,
876
+ }
877
+
878
+ def __repr__(self) -> str:
879
+ return (
880
+ f"BenchmarkResult(\n"
881
+ f" iterations={self.iterations},\n"
882
+ f" avg_time={self.avg_time_ms:.2f}ms,\n"
883
+ f" p95_time={self.p95_time_ms:.2f}ms,\n"
884
+ f" peak_memory={self.peak_memory_bytes / 1024:.2f}KB,\n"
885
+ f" output_size={self.output_size_bytes / 1024:.2f}KB\n"
886
+ f")"
887
+ )
888
+
889
+
890
+ def benchmark_reporter(
891
+ reporter: Any,
892
+ result: Optional[MockValidationResult] = None,
893
+ iterations: int = 100,
894
+ warmup: int = 5,
895
+ method: str = "render",
896
+ ) -> BenchmarkResult:
897
+ """Benchmark reporter performance.
898
+
899
+ Args:
900
+ reporter: The reporter instance to benchmark.
901
+ result: Mock result to render. If None, creates a default one.
902
+ iterations: Number of iterations to run.
903
+ warmup: Number of warmup iterations.
904
+ method: Method name to call on reporter.
905
+
906
+ Returns:
907
+ BenchmarkResult with performance statistics.
908
+
909
+ Example:
910
+ >>> result = create_mock_result(passed=100, failed=10)
911
+ >>> benchmark = benchmark_reporter(my_reporter, result, iterations=100)
912
+ >>> print(f"Average: {benchmark.avg_time_ms:.2f}ms")
913
+ >>> print(f"P95: {benchmark.p95_time_ms:.2f}ms")
914
+ """
915
+ import statistics
916
+ import time
917
+ import tracemalloc
918
+
919
+ if result is None:
920
+ result = create_mock_result(passed=10, failed=2)
921
+
922
+ render_func = getattr(reporter, method)
923
+
924
+ # Warmup
925
+ for _ in range(warmup):
926
+ render_func(result)
927
+
928
+ # Benchmark
929
+ times_ms = []
930
+ peak_memory = 0
931
+ output_size = 0
932
+
933
+ tracemalloc.start()
934
+
935
+ for _ in range(iterations):
936
+ start = time.perf_counter()
937
+ output = render_func(result)
938
+ end = time.perf_counter()
939
+
940
+ times_ms.append((end - start) * 1000)
941
+
942
+ current, peak = tracemalloc.get_traced_memory()
943
+ peak_memory = max(peak_memory, peak)
944
+
945
+ if output_size == 0:
946
+ if isinstance(output, str):
947
+ output_size = len(output.encode("utf-8"))
948
+ elif isinstance(output, bytes):
949
+ output_size = len(output)
950
+ elif isinstance(output, dict):
951
+ output_size = len(json.dumps(output).encode("utf-8"))
952
+
953
+ tracemalloc.stop()
954
+
955
+ times_ms.sort()
956
+
957
+ return BenchmarkResult(
958
+ iterations=iterations,
959
+ total_time_ms=sum(times_ms),
960
+ avg_time_ms=statistics.mean(times_ms),
961
+ min_time_ms=min(times_ms),
962
+ max_time_ms=max(times_ms),
963
+ p50_time_ms=times_ms[len(times_ms) // 2],
964
+ p95_time_ms=times_ms[int(len(times_ms) * 0.95)],
965
+ p99_time_ms=times_ms[int(len(times_ms) * 0.99)],
966
+ peak_memory_bytes=peak_memory,
967
+ output_size_bytes=output_size,
968
+ )