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,1018 @@
1
+ """OpenTelemetry integration testing and validation utilities.
2
+
3
+ This module provides comprehensive testing and validation for OpenTelemetry
4
+ integration, including mock exporters, integration validators, and
5
+ diagnostic tools for Jaeger/Zipkin connectivity.
6
+
7
+ Key Features:
8
+ - Mock span exporters for testing
9
+ - Integration validators for Jaeger/Zipkin
10
+ - Diagnostic tools for troubleshooting
11
+ - End-to-end tracing verification
12
+ - Configuration validation
13
+
14
+ Example:
15
+ from truthound.observability.tracing.integration import (
16
+ TracingValidator,
17
+ MockSpanExporter,
18
+ validate_jaeger_connection,
19
+ )
20
+
21
+ # Validate Jaeger connection
22
+ result = validate_jaeger_connection("http://localhost:14268")
23
+ if result.success:
24
+ print("Jaeger is reachable")
25
+
26
+ # Run integration tests
27
+ validator = TracingValidator()
28
+ report = validator.validate_all()
29
+ print(report)
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ import socket
36
+ import threading
37
+ import time
38
+ from dataclasses import dataclass, field
39
+ from datetime import datetime
40
+ from enum import Enum
41
+ from typing import Any, Callable
42
+ from urllib.parse import urlparse
43
+
44
+ from truthound.observability.tracing.span import (
45
+ Span,
46
+ SpanContextData,
47
+ SpanKind,
48
+ StatusCode,
49
+ )
50
+ from truthound.observability.tracing.processor import SpanProcessor
51
+ from truthound.observability.tracing.exporter import SpanExporter, ExportResult
52
+
53
+
54
+ # =============================================================================
55
+ # Mock Exporter for Testing
56
+ # =============================================================================
57
+
58
+
59
+ class MockSpanExporter(SpanExporter):
60
+ """Mock span exporter for testing purposes.
61
+
62
+ Collects exported spans in memory for verification.
63
+
64
+ Example:
65
+ exporter = MockSpanExporter()
66
+ provider.add_processor(SimpleSpanProcessor(exporter))
67
+
68
+ with tracer.start_as_current_span("test"):
69
+ pass
70
+
71
+ assert len(exporter.spans) == 1
72
+ assert exporter.spans[0].name == "test"
73
+
74
+ Attributes:
75
+ spans: List of exported spans
76
+ export_count: Number of export calls
77
+ last_export_time: Time of last export
78
+ """
79
+
80
+ def __init__(self, max_spans: int = 10000):
81
+ """Initialize mock exporter.
82
+
83
+ Args:
84
+ max_spans: Maximum spans to keep in memory
85
+ """
86
+ self.max_spans = max_spans
87
+ self.spans: list[Span] = []
88
+ self.export_count = 0
89
+ self.last_export_time: datetime | None = None
90
+ self._lock = threading.Lock()
91
+ self._shutdown = False
92
+
93
+ def export(self, spans: list[Span]) -> ExportResult:
94
+ """Export spans to memory.
95
+
96
+ Args:
97
+ spans: Spans to export
98
+
99
+ Returns:
100
+ ExportResult.SUCCESS
101
+ """
102
+ if self._shutdown:
103
+ return ExportResult.SUCCESS
104
+
105
+ with self._lock:
106
+ self.spans.extend(spans)
107
+ self.export_count += 1
108
+ self.last_export_time = datetime.now()
109
+
110
+ # Trim if over limit
111
+ if len(self.spans) > self.max_spans:
112
+ self.spans = self.spans[-self.max_spans:]
113
+
114
+ return ExportResult.SUCCESS
115
+
116
+ def shutdown(self) -> bool:
117
+ """Shutdown exporter."""
118
+ self._shutdown = True
119
+ return True
120
+
121
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
122
+ """Force flush (no-op for mock)."""
123
+ return True
124
+
125
+ def clear(self) -> None:
126
+ """Clear collected spans."""
127
+ with self._lock:
128
+ self.spans.clear()
129
+ self.export_count = 0
130
+
131
+ def get_span_by_name(self, name: str) -> Span | None:
132
+ """Find span by name.
133
+
134
+ Args:
135
+ name: Span name to find
136
+
137
+ Returns:
138
+ First matching span or None
139
+ """
140
+ with self._lock:
141
+ for span in self.spans:
142
+ if span.name == name:
143
+ return span
144
+ return None
145
+
146
+ def get_spans_by_name(self, name: str) -> list[Span]:
147
+ """Find all spans with given name.
148
+
149
+ Args:
150
+ name: Span name to find
151
+
152
+ Returns:
153
+ List of matching spans
154
+ """
155
+ with self._lock:
156
+ return [s for s in self.spans if s.name == name]
157
+
158
+ def get_span_by_trace_id(self, trace_id: str) -> list[Span]:
159
+ """Find all spans in a trace.
160
+
161
+ Args:
162
+ trace_id: Trace ID to find
163
+
164
+ Returns:
165
+ List of spans in the trace
166
+ """
167
+ with self._lock:
168
+ return [
169
+ s for s in self.spans
170
+ if s.context.trace_id == trace_id
171
+ ]
172
+
173
+ def get_stats(self) -> dict[str, Any]:
174
+ """Get exporter statistics.
175
+
176
+ Returns:
177
+ Statistics dictionary
178
+ """
179
+ with self._lock:
180
+ return {
181
+ "span_count": len(self.spans),
182
+ "export_count": self.export_count,
183
+ "last_export_time": (
184
+ self.last_export_time.isoformat()
185
+ if self.last_export_time else None
186
+ ),
187
+ }
188
+
189
+
190
+ # =============================================================================
191
+ # Recording Processor for Testing
192
+ # =============================================================================
193
+
194
+
195
+ class RecordingSpanProcessor(SpanProcessor):
196
+ """Span processor that records events for testing.
197
+
198
+ Records all start and end events for verification.
199
+
200
+ Example:
201
+ processor = RecordingSpanProcessor()
202
+ provider.add_processor(processor)
203
+
204
+ with tracer.start_as_current_span("test"):
205
+ pass
206
+
207
+ assert len(processor.started_spans) == 1
208
+ assert len(processor.ended_spans) == 1
209
+ """
210
+
211
+ def __init__(self):
212
+ """Initialize processor."""
213
+ self.started_spans: list[tuple[Span, SpanContextData | None]] = []
214
+ self.ended_spans: list[Span] = []
215
+ self._lock = threading.Lock()
216
+
217
+ def on_start(self, span: Span, parent_context: SpanContextData | None) -> None:
218
+ """Record span start.
219
+
220
+ Args:
221
+ span: Started span
222
+ parent_context: Parent span context
223
+ """
224
+ with self._lock:
225
+ self.started_spans.append((span, parent_context))
226
+
227
+ def on_end(self, span: Span) -> None:
228
+ """Record span end.
229
+
230
+ Args:
231
+ span: Ended span
232
+ """
233
+ with self._lock:
234
+ self.ended_spans.append(span)
235
+
236
+ def shutdown(self) -> bool:
237
+ """Shutdown processor."""
238
+ return True
239
+
240
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
241
+ """Force flush (no-op)."""
242
+ return True
243
+
244
+ def clear(self) -> None:
245
+ """Clear recorded spans."""
246
+ with self._lock:
247
+ self.started_spans.clear()
248
+ self.ended_spans.clear()
249
+
250
+ def get_stats(self) -> dict[str, Any]:
251
+ """Get processor statistics."""
252
+ with self._lock:
253
+ return {
254
+ "started_count": len(self.started_spans),
255
+ "ended_count": len(self.ended_spans),
256
+ }
257
+
258
+
259
+ # =============================================================================
260
+ # Connection Validators
261
+ # =============================================================================
262
+
263
+
264
+ class ConnectionStatus(str, Enum):
265
+ """Status of backend connection."""
266
+
267
+ CONNECTED = "connected"
268
+ UNREACHABLE = "unreachable"
269
+ AUTH_FAILED = "auth_failed"
270
+ TIMEOUT = "timeout"
271
+ ERROR = "error"
272
+ NOT_CONFIGURED = "not_configured"
273
+
274
+
275
+ @dataclass
276
+ class ConnectionResult:
277
+ """Result of connection validation.
278
+
279
+ Attributes:
280
+ status: Connection status
281
+ endpoint: Tested endpoint
282
+ latency_ms: Response latency in milliseconds
283
+ message: Status message
284
+ details: Additional details
285
+ """
286
+
287
+ status: ConnectionStatus
288
+ endpoint: str = ""
289
+ latency_ms: float = 0.0
290
+ message: str = ""
291
+ details: dict[str, Any] = field(default_factory=dict)
292
+
293
+ @property
294
+ def success(self) -> bool:
295
+ """Check if connection was successful."""
296
+ return self.status == ConnectionStatus.CONNECTED
297
+
298
+ def to_dict(self) -> dict[str, Any]:
299
+ """Convert to dictionary."""
300
+ return {
301
+ "status": self.status.value,
302
+ "endpoint": self.endpoint,
303
+ "latency_ms": self.latency_ms,
304
+ "message": self.message,
305
+ "success": self.success,
306
+ "details": self.details,
307
+ }
308
+
309
+
310
+ def validate_jaeger_connection(
311
+ endpoint: str,
312
+ timeout: float = 5.0,
313
+ ) -> ConnectionResult:
314
+ """Validate Jaeger backend connection.
315
+
316
+ Supports both HTTP (Thrift) and gRPC endpoints.
317
+
318
+ Args:
319
+ endpoint: Jaeger endpoint URL
320
+ timeout: Connection timeout in seconds
321
+
322
+ Returns:
323
+ ConnectionResult with status
324
+
325
+ Example:
326
+ # HTTP endpoint (Thrift)
327
+ result = validate_jaeger_connection("http://localhost:14268")
328
+
329
+ # gRPC endpoint
330
+ result = validate_jaeger_connection("grpc://localhost:14250")
331
+ """
332
+ start_time = time.time()
333
+
334
+ try:
335
+ parsed = urlparse(endpoint)
336
+ host = parsed.hostname or "localhost"
337
+ port = parsed.port or (14268 if parsed.scheme == "http" else 14250)
338
+
339
+ # Try socket connection first
340
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
341
+ sock.settimeout(timeout)
342
+
343
+ try:
344
+ sock.connect((host, port))
345
+ sock.close()
346
+ except socket.timeout:
347
+ return ConnectionResult(
348
+ status=ConnectionStatus.TIMEOUT,
349
+ endpoint=endpoint,
350
+ latency_ms=(time.time() - start_time) * 1000,
351
+ message=f"Connection to {host}:{port} timed out",
352
+ )
353
+ except socket.error as e:
354
+ return ConnectionResult(
355
+ status=ConnectionStatus.UNREACHABLE,
356
+ endpoint=endpoint,
357
+ latency_ms=(time.time() - start_time) * 1000,
358
+ message=f"Cannot connect to {host}:{port}: {e}",
359
+ )
360
+
361
+ # For HTTP, try health endpoint
362
+ if parsed.scheme in ("http", "https"):
363
+ try:
364
+ import urllib.request
365
+
366
+ health_url = f"{endpoint.rstrip('/')}/health"
367
+ req = urllib.request.Request(health_url, method="GET")
368
+ req.add_header("User-Agent", "truthound-validator/1.0")
369
+
370
+ with urllib.request.urlopen(req, timeout=timeout) as response:
371
+ if response.status == 200:
372
+ return ConnectionResult(
373
+ status=ConnectionStatus.CONNECTED,
374
+ endpoint=endpoint,
375
+ latency_ms=(time.time() - start_time) * 1000,
376
+ message="Jaeger is healthy",
377
+ details={"http_status": response.status},
378
+ )
379
+
380
+ except Exception as e:
381
+ # Health check failed, but socket connected
382
+ return ConnectionResult(
383
+ status=ConnectionStatus.CONNECTED,
384
+ endpoint=endpoint,
385
+ latency_ms=(time.time() - start_time) * 1000,
386
+ message="Jaeger socket reachable (health check failed)",
387
+ details={"warning": str(e)},
388
+ )
389
+
390
+ return ConnectionResult(
391
+ status=ConnectionStatus.CONNECTED,
392
+ endpoint=endpoint,
393
+ latency_ms=(time.time() - start_time) * 1000,
394
+ message="Jaeger is reachable",
395
+ )
396
+
397
+ except Exception as e:
398
+ return ConnectionResult(
399
+ status=ConnectionStatus.ERROR,
400
+ endpoint=endpoint,
401
+ latency_ms=(time.time() - start_time) * 1000,
402
+ message=f"Validation error: {e}",
403
+ )
404
+
405
+
406
+ def validate_zipkin_connection(
407
+ endpoint: str,
408
+ timeout: float = 5.0,
409
+ ) -> ConnectionResult:
410
+ """Validate Zipkin backend connection.
411
+
412
+ Args:
413
+ endpoint: Zipkin endpoint URL (e.g., http://localhost:9411)
414
+ timeout: Connection timeout in seconds
415
+
416
+ Returns:
417
+ ConnectionResult with status
418
+
419
+ Example:
420
+ result = validate_zipkin_connection("http://localhost:9411")
421
+ """
422
+ start_time = time.time()
423
+
424
+ try:
425
+ parsed = urlparse(endpoint)
426
+ host = parsed.hostname or "localhost"
427
+ port = parsed.port or 9411
428
+
429
+ # Try HTTP API endpoint
430
+ try:
431
+ import urllib.request
432
+
433
+ api_url = f"{endpoint.rstrip('/')}/api/v2/services"
434
+ req = urllib.request.Request(api_url, method="GET")
435
+ req.add_header("User-Agent", "truthound-validator/1.0")
436
+ req.add_header("Accept", "application/json")
437
+
438
+ with urllib.request.urlopen(req, timeout=timeout) as response:
439
+ if response.status == 200:
440
+ return ConnectionResult(
441
+ status=ConnectionStatus.CONNECTED,
442
+ endpoint=endpoint,
443
+ latency_ms=(time.time() - start_time) * 1000,
444
+ message="Zipkin is healthy",
445
+ details={"http_status": response.status},
446
+ )
447
+
448
+ except urllib.error.HTTPError as e:
449
+ if e.code == 401:
450
+ return ConnectionResult(
451
+ status=ConnectionStatus.AUTH_FAILED,
452
+ endpoint=endpoint,
453
+ latency_ms=(time.time() - start_time) * 1000,
454
+ message="Authentication required",
455
+ )
456
+ elif e.code == 404:
457
+ # API might be different version, but service is reachable
458
+ return ConnectionResult(
459
+ status=ConnectionStatus.CONNECTED,
460
+ endpoint=endpoint,
461
+ latency_ms=(time.time() - start_time) * 1000,
462
+ message="Zipkin reachable (API version mismatch)",
463
+ details={"http_status": e.code},
464
+ )
465
+
466
+ except urllib.error.URLError as e:
467
+ return ConnectionResult(
468
+ status=ConnectionStatus.UNREACHABLE,
469
+ endpoint=endpoint,
470
+ latency_ms=(time.time() - start_time) * 1000,
471
+ message=f"Cannot connect to Zipkin: {e.reason}",
472
+ )
473
+
474
+ except Exception as e:
475
+ return ConnectionResult(
476
+ status=ConnectionStatus.ERROR,
477
+ endpoint=endpoint,
478
+ latency_ms=(time.time() - start_time) * 1000,
479
+ message=f"Connection error: {e}",
480
+ )
481
+
482
+ # Fallback: try socket
483
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
484
+ sock.settimeout(timeout)
485
+
486
+ try:
487
+ sock.connect((host, port))
488
+ sock.close()
489
+ return ConnectionResult(
490
+ status=ConnectionStatus.CONNECTED,
491
+ endpoint=endpoint,
492
+ latency_ms=(time.time() - start_time) * 1000,
493
+ message="Zipkin socket reachable",
494
+ )
495
+ except socket.timeout:
496
+ return ConnectionResult(
497
+ status=ConnectionStatus.TIMEOUT,
498
+ endpoint=endpoint,
499
+ latency_ms=(time.time() - start_time) * 1000,
500
+ message="Connection timed out",
501
+ )
502
+ except socket.error as e:
503
+ return ConnectionResult(
504
+ status=ConnectionStatus.UNREACHABLE,
505
+ endpoint=endpoint,
506
+ latency_ms=(time.time() - start_time) * 1000,
507
+ message=f"Cannot connect: {e}",
508
+ )
509
+
510
+ except Exception as e:
511
+ return ConnectionResult(
512
+ status=ConnectionStatus.ERROR,
513
+ endpoint=endpoint,
514
+ latency_ms=(time.time() - start_time) * 1000,
515
+ message=f"Validation error: {e}",
516
+ )
517
+
518
+
519
+ def validate_otlp_connection(
520
+ endpoint: str,
521
+ timeout: float = 5.0,
522
+ use_tls: bool = False,
523
+ ) -> ConnectionResult:
524
+ """Validate OTLP (OpenTelemetry Protocol) endpoint connection.
525
+
526
+ Args:
527
+ endpoint: OTLP endpoint (e.g., http://localhost:4317)
528
+ timeout: Connection timeout in seconds
529
+ use_tls: Whether to use TLS
530
+
531
+ Returns:
532
+ ConnectionResult with status
533
+ """
534
+ start_time = time.time()
535
+
536
+ try:
537
+ parsed = urlparse(endpoint)
538
+ host = parsed.hostname or "localhost"
539
+ port = parsed.port or 4317
540
+
541
+ # Try socket connection
542
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
543
+ sock.settimeout(timeout)
544
+
545
+ try:
546
+ sock.connect((host, port))
547
+ sock.close()
548
+
549
+ return ConnectionResult(
550
+ status=ConnectionStatus.CONNECTED,
551
+ endpoint=endpoint,
552
+ latency_ms=(time.time() - start_time) * 1000,
553
+ message="OTLP endpoint is reachable",
554
+ details={"protocol": "grpc" if port == 4317 else "http"},
555
+ )
556
+
557
+ except socket.timeout:
558
+ return ConnectionResult(
559
+ status=ConnectionStatus.TIMEOUT,
560
+ endpoint=endpoint,
561
+ latency_ms=(time.time() - start_time) * 1000,
562
+ message="Connection timed out",
563
+ )
564
+
565
+ except socket.error as e:
566
+ return ConnectionResult(
567
+ status=ConnectionStatus.UNREACHABLE,
568
+ endpoint=endpoint,
569
+ latency_ms=(time.time() - start_time) * 1000,
570
+ message=f"Cannot connect: {e}",
571
+ )
572
+
573
+ except Exception as e:
574
+ return ConnectionResult(
575
+ status=ConnectionStatus.ERROR,
576
+ endpoint=endpoint,
577
+ latency_ms=(time.time() - start_time) * 1000,
578
+ message=f"Validation error: {e}",
579
+ )
580
+
581
+
582
+ # =============================================================================
583
+ # Tracing Validator
584
+ # =============================================================================
585
+
586
+
587
+ @dataclass
588
+ class ValidationReport:
589
+ """Comprehensive tracing validation report.
590
+
591
+ Attributes:
592
+ success: Overall validation success
593
+ provider_valid: TracerProvider is working
594
+ span_creation_valid: Spans can be created
595
+ context_propagation_valid: Context propagation works
596
+ exporter_valid: Exporter is working
597
+ backend_connections: Backend connection results
598
+ errors: List of errors
599
+ warnings: List of warnings
600
+ duration_ms: Total validation duration
601
+ """
602
+
603
+ success: bool = True
604
+ provider_valid: bool = False
605
+ span_creation_valid: bool = False
606
+ context_propagation_valid: bool = False
607
+ exporter_valid: bool = False
608
+ backend_connections: dict[str, ConnectionResult] = field(default_factory=dict)
609
+ errors: list[str] = field(default_factory=list)
610
+ warnings: list[str] = field(default_factory=list)
611
+ duration_ms: float = 0.0
612
+
613
+ def to_dict(self) -> dict[str, Any]:
614
+ """Convert to dictionary."""
615
+ return {
616
+ "success": self.success,
617
+ "provider_valid": self.provider_valid,
618
+ "span_creation_valid": self.span_creation_valid,
619
+ "context_propagation_valid": self.context_propagation_valid,
620
+ "exporter_valid": self.exporter_valid,
621
+ "backend_connections": {
622
+ k: v.to_dict()
623
+ for k, v in self.backend_connections.items()
624
+ },
625
+ "errors": self.errors,
626
+ "warnings": self.warnings,
627
+ "duration_ms": self.duration_ms,
628
+ }
629
+
630
+ def __str__(self) -> str:
631
+ """Human-readable report."""
632
+ lines = [
633
+ "OpenTelemetry Tracing Validation Report",
634
+ "=" * 40,
635
+ f"Overall Status: {'PASS' if self.success else 'FAIL'}",
636
+ "",
637
+ "Component Status:",
638
+ f" Provider: {'✓' if self.provider_valid else '✗'}",
639
+ f" Span Creation: {'✓' if self.span_creation_valid else '✗'}",
640
+ f" Context Propagation: {'✓' if self.context_propagation_valid else '✗'}",
641
+ f" Exporter: {'✓' if self.exporter_valid else '✗'}",
642
+ "",
643
+ ]
644
+
645
+ if self.backend_connections:
646
+ lines.append("Backend Connections:")
647
+ for name, result in self.backend_connections.items():
648
+ status = "✓" if result.success else "✗"
649
+ lines.append(f" {name}: {status} ({result.message})")
650
+ lines.append("")
651
+
652
+ if self.errors:
653
+ lines.append("Errors:")
654
+ for err in self.errors:
655
+ lines.append(f" - {err}")
656
+ lines.append("")
657
+
658
+ if self.warnings:
659
+ lines.append("Warnings:")
660
+ for warn in self.warnings:
661
+ lines.append(f" - {warn}")
662
+ lines.append("")
663
+
664
+ lines.append(f"Duration: {self.duration_ms:.2f}ms")
665
+
666
+ return "\n".join(lines)
667
+
668
+
669
+ class TracingValidator:
670
+ """Validates OpenTelemetry tracing integration.
671
+
672
+ Provides comprehensive validation of tracing setup including
673
+ provider configuration, span creation, context propagation,
674
+ and backend connectivity.
675
+
676
+ Example:
677
+ validator = TracingValidator()
678
+
679
+ # Validate everything
680
+ report = validator.validate_all()
681
+ print(report)
682
+
683
+ # Validate specific components
684
+ result = validator.validate_span_creation()
685
+ """
686
+
687
+ def __init__(
688
+ self,
689
+ jaeger_endpoint: str | None = None,
690
+ zipkin_endpoint: str | None = None,
691
+ otlp_endpoint: str | None = None,
692
+ ):
693
+ """Initialize validator.
694
+
695
+ Args:
696
+ jaeger_endpoint: Jaeger endpoint URL
697
+ zipkin_endpoint: Zipkin endpoint URL
698
+ otlp_endpoint: OTLP endpoint URL
699
+ """
700
+ self.jaeger_endpoint = jaeger_endpoint
701
+ self.zipkin_endpoint = zipkin_endpoint
702
+ self.otlp_endpoint = otlp_endpoint
703
+
704
+ def validate_all(self) -> ValidationReport:
705
+ """Run all validations.
706
+
707
+ Returns:
708
+ Comprehensive validation report
709
+ """
710
+ start_time = time.time()
711
+ report = ValidationReport()
712
+
713
+ # Validate provider
714
+ try:
715
+ report.provider_valid = self.validate_provider()
716
+ except Exception as e:
717
+ report.errors.append(f"Provider validation error: {e}")
718
+
719
+ # Validate span creation
720
+ try:
721
+ report.span_creation_valid = self.validate_span_creation()
722
+ except Exception as e:
723
+ report.errors.append(f"Span creation validation error: {e}")
724
+
725
+ # Validate context propagation
726
+ try:
727
+ report.context_propagation_valid = self.validate_context_propagation()
728
+ except Exception as e:
729
+ report.errors.append(f"Context propagation validation error: {e}")
730
+
731
+ # Validate exporter
732
+ try:
733
+ report.exporter_valid = self.validate_exporter()
734
+ except Exception as e:
735
+ report.errors.append(f"Exporter validation error: {e}")
736
+
737
+ # Validate backend connections
738
+ if self.jaeger_endpoint:
739
+ result = validate_jaeger_connection(self.jaeger_endpoint)
740
+ report.backend_connections["jaeger"] = result
741
+ if not result.success:
742
+ report.warnings.append(f"Jaeger not reachable: {result.message}")
743
+
744
+ if self.zipkin_endpoint:
745
+ result = validate_zipkin_connection(self.zipkin_endpoint)
746
+ report.backend_connections["zipkin"] = result
747
+ if not result.success:
748
+ report.warnings.append(f"Zipkin not reachable: {result.message}")
749
+
750
+ if self.otlp_endpoint:
751
+ result = validate_otlp_connection(self.otlp_endpoint)
752
+ report.backend_connections["otlp"] = result
753
+ if not result.success:
754
+ report.warnings.append(f"OTLP endpoint not reachable: {result.message}")
755
+
756
+ # Overall success
757
+ report.success = (
758
+ report.provider_valid and
759
+ report.span_creation_valid and
760
+ report.context_propagation_valid and
761
+ len(report.errors) == 0
762
+ )
763
+
764
+ report.duration_ms = (time.time() - start_time) * 1000
765
+ return report
766
+
767
+ def validate_provider(self) -> bool:
768
+ """Validate TracerProvider is working.
769
+
770
+ Returns:
771
+ True if provider is valid
772
+ """
773
+ from truthound.observability.tracing.provider import (
774
+ TracerProvider,
775
+ get_tracer_provider,
776
+ )
777
+
778
+ # Check global provider exists
779
+ provider = get_tracer_provider()
780
+ if provider is None:
781
+ return False
782
+
783
+ # Check we can get a tracer
784
+ tracer = provider.get_tracer("validation-test")
785
+ if tracer is None:
786
+ return False
787
+
788
+ return True
789
+
790
+ def validate_span_creation(self) -> bool:
791
+ """Validate spans can be created.
792
+
793
+ Returns:
794
+ True if span creation works
795
+ """
796
+ from truthound.observability.tracing.provider import (
797
+ TracerProvider,
798
+ Tracer,
799
+ )
800
+
801
+ # Create isolated provider for testing
802
+ mock_exporter = MockSpanExporter()
803
+ from truthound.observability.tracing.processor import SimpleSpanProcessor
804
+
805
+ provider = TracerProvider()
806
+ provider.add_processor(SimpleSpanProcessor(mock_exporter))
807
+
808
+ tracer = provider.get_tracer("validation-test")
809
+
810
+ # Create a span
811
+ with tracer.start_as_current_span("test-span") as span:
812
+ span.set_attribute("test.key", "test-value")
813
+
814
+ # Verify span was created
815
+ if len(mock_exporter.spans) != 1:
816
+ return False
817
+
818
+ exported_span = mock_exporter.spans[0]
819
+ if exported_span.name != "test-span":
820
+ return False
821
+
822
+ if exported_span.attributes.get("test.key") != "test-value":
823
+ return False
824
+
825
+ return True
826
+
827
+ def validate_context_propagation(self) -> bool:
828
+ """Validate context propagation works.
829
+
830
+ Returns:
831
+ True if context propagation works
832
+ """
833
+ from truthound.observability.tracing.provider import (
834
+ TracerProvider,
835
+ get_current_span,
836
+ )
837
+ from truthound.observability.tracing.processor import SimpleSpanProcessor
838
+
839
+ mock_exporter = MockSpanExporter()
840
+ provider = TracerProvider()
841
+ provider.add_processor(SimpleSpanProcessor(mock_exporter))
842
+
843
+ tracer = provider.get_tracer("validation-test")
844
+
845
+ # Create parent span
846
+ with tracer.start_as_current_span("parent") as parent:
847
+ parent_trace_id = parent.context.trace_id
848
+
849
+ # Create child span
850
+ with tracer.start_as_current_span("child") as child:
851
+ # Verify child has same trace ID
852
+ if child.context.trace_id != parent_trace_id:
853
+ return False
854
+
855
+ # Verify parent is set correctly
856
+ if child.parent is None:
857
+ return False
858
+
859
+ if child.parent.span_id != parent.context.span_id:
860
+ return False
861
+
862
+ # Verify we exported both spans
863
+ if len(mock_exporter.spans) != 2:
864
+ return False
865
+
866
+ return True
867
+
868
+ def validate_exporter(self) -> bool:
869
+ """Validate exporter functionality.
870
+
871
+ Returns:
872
+ True if exporter works
873
+ """
874
+ # Test mock exporter
875
+ exporter = MockSpanExporter()
876
+
877
+ from truthound.observability.tracing.span import Span, SpanContextData
878
+
879
+ # Create test span
880
+ context = SpanContextData(
881
+ trace_id="0" * 32,
882
+ span_id="0" * 16,
883
+ trace_flags=1,
884
+ )
885
+
886
+ span = Span(
887
+ name="test-export",
888
+ context=context,
889
+ kind=SpanKind.INTERNAL,
890
+ )
891
+ span.end()
892
+
893
+ # Export
894
+ result = exporter.export([span])
895
+ if result != ExportResult.SUCCESS:
896
+ return False
897
+
898
+ if len(exporter.spans) != 1:
899
+ return False
900
+
901
+ return True
902
+
903
+
904
+ # =============================================================================
905
+ # Diagnostic Tools
906
+ # =============================================================================
907
+
908
+
909
+ def diagnose_tracing_setup() -> dict[str, Any]:
910
+ """Run comprehensive tracing diagnostics.
911
+
912
+ Returns:
913
+ Diagnostic information dictionary
914
+ """
915
+ diagnostics = {
916
+ "timestamp": datetime.now().isoformat(),
917
+ "python_version": None,
918
+ "truthound_version": None,
919
+ "otel_packages": {},
920
+ "provider_info": {},
921
+ "environment": {},
922
+ }
923
+
924
+ # Python version
925
+ import sys
926
+ diagnostics["python_version"] = sys.version
927
+
928
+ # Truthound version
929
+ try:
930
+ import truthound
931
+ diagnostics["truthound_version"] = getattr(truthound, "__version__", "unknown")
932
+ except ImportError:
933
+ diagnostics["truthound_version"] = "not installed"
934
+
935
+ # OpenTelemetry packages
936
+ otel_packages = [
937
+ "opentelemetry-api",
938
+ "opentelemetry-sdk",
939
+ "opentelemetry-exporter-jaeger",
940
+ "opentelemetry-exporter-zipkin",
941
+ "opentelemetry-exporter-otlp",
942
+ ]
943
+
944
+ for pkg in otel_packages:
945
+ try:
946
+ import importlib.metadata
947
+ version = importlib.metadata.version(pkg.replace("-", "_"))
948
+ diagnostics["otel_packages"][pkg] = version
949
+ except Exception:
950
+ diagnostics["otel_packages"][pkg] = "not installed"
951
+
952
+ # Provider info
953
+ try:
954
+ from truthound.observability.tracing.provider import get_tracer_provider
955
+
956
+ provider = get_tracer_provider()
957
+ diagnostics["provider_info"] = {
958
+ "type": type(provider).__name__,
959
+ "resource": str(provider.resource) if hasattr(provider, "resource") else "N/A",
960
+ "sampler": str(provider.sampler) if hasattr(provider, "sampler") else "N/A",
961
+ }
962
+ except Exception as e:
963
+ diagnostics["provider_info"] = {"error": str(e)}
964
+
965
+ # Environment variables
966
+ import os
967
+ otel_env_vars = [
968
+ "OTEL_SERVICE_NAME",
969
+ "OTEL_EXPORTER_JAEGER_ENDPOINT",
970
+ "OTEL_EXPORTER_ZIPKIN_ENDPOINT",
971
+ "OTEL_EXPORTER_OTLP_ENDPOINT",
972
+ "OTEL_TRACES_SAMPLER",
973
+ "OTEL_TRACES_EXPORTER",
974
+ ]
975
+
976
+ for var in otel_env_vars:
977
+ value = os.environ.get(var)
978
+ if value:
979
+ diagnostics["environment"][var] = value
980
+
981
+ return diagnostics
982
+
983
+
984
+ def create_test_trace(
985
+ tracer_name: str = "test-tracer",
986
+ span_count: int = 3,
987
+ ) -> list[Span]:
988
+ """Create a test trace for verification.
989
+
990
+ Args:
991
+ tracer_name: Name for test tracer
992
+ span_count: Number of nested spans to create
993
+
994
+ Returns:
995
+ List of created spans
996
+ """
997
+ from truthound.observability.tracing.provider import TracerProvider
998
+ from truthound.observability.tracing.processor import SimpleSpanProcessor
999
+
1000
+ mock_exporter = MockSpanExporter()
1001
+ provider = TracerProvider()
1002
+ provider.add_processor(SimpleSpanProcessor(mock_exporter))
1003
+
1004
+ tracer = provider.get_tracer(tracer_name)
1005
+
1006
+ def create_nested_spans(depth: int, current: int = 0):
1007
+ if current >= depth:
1008
+ return
1009
+
1010
+ with tracer.start_as_current_span(f"span-{current}") as span:
1011
+ span.set_attribute("depth", current)
1012
+ span.set_attribute("timestamp", datetime.now().isoformat())
1013
+ time.sleep(0.001) # Small delay for realistic timing
1014
+ create_nested_spans(depth, current + 1)
1015
+
1016
+ create_nested_spans(span_count)
1017
+
1018
+ return list(mock_exporter.spans)