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,1177 @@
1
+ """Bridge components for cross-backend interoperability.
2
+
3
+ This module provides bridge classes that enable using components from
4
+ one backend with the other:
5
+
6
+ - Use OpenTelemetry SDK exporters with Truthound spans
7
+ - Use Truthound processors with OpenTelemetry spans
8
+ - Bridge samplers, propagators, and resources
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import threading
15
+ import time
16
+ from abc import ABC, abstractmethod
17
+ from dataclasses import dataclass, field
18
+ from typing import Any, Mapping, Sequence
19
+
20
+ from truthound.observability.tracing.otel.detection import is_otel_sdk_available
21
+ from truthound.observability.tracing.otel.adapter import SpanContextAdapter
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ # =============================================================================
27
+ # Span Context Bridge
28
+ # =============================================================================
29
+
30
+
31
+ class SpanContextBridge:
32
+ """Bridges span contexts between Truthound and OpenTelemetry formats.
33
+
34
+ Handles conversion of trace/span IDs, trace flags, and trace state
35
+ between the two format conventions.
36
+
37
+ Example:
38
+ >>> bridge = SpanContextBridge()
39
+ >>> otel_ctx = bridge.truthound_to_otel(truthound_ctx)
40
+ >>> truthound_ctx = bridge.otel_to_truthound(otel_ctx)
41
+ """
42
+
43
+ def truthound_to_otel(self, ctx: Any) -> Any:
44
+ """Convert Truthound SpanContextData to OTEL SpanContext.
45
+
46
+ Args:
47
+ ctx: Truthound SpanContextData.
48
+
49
+ Returns:
50
+ OpenTelemetry SpanContext.
51
+
52
+ Raises:
53
+ ImportError: If OTEL SDK is not available.
54
+ """
55
+ if not is_otel_sdk_available():
56
+ raise ImportError("OpenTelemetry SDK not available")
57
+
58
+ from opentelemetry.trace import SpanContext, TraceState
59
+
60
+ # Parse trace state
61
+ trace_state = TraceState()
62
+ if hasattr(ctx, "trace_state") and ctx.trace_state:
63
+ state_str = ctx.trace_state if isinstance(ctx.trace_state, str) else str(ctx.trace_state)
64
+ for pair in state_str.split(","):
65
+ if "=" in pair:
66
+ key, value = pair.split("=", 1)
67
+ trace_state = trace_state.add(key.strip(), value.strip())
68
+
69
+ # Convert IDs
70
+ trace_id = ctx.trace_id
71
+ span_id = ctx.span_id
72
+
73
+ if isinstance(trace_id, str):
74
+ trace_id = int(trace_id, 16)
75
+ if isinstance(span_id, str):
76
+ span_id = int(span_id, 16)
77
+
78
+ return SpanContext(
79
+ trace_id=trace_id,
80
+ span_id=span_id,
81
+ is_remote=getattr(ctx, "is_remote", False),
82
+ trace_flags=ctx.trace_flags,
83
+ trace_state=trace_state,
84
+ )
85
+
86
+ def otel_to_truthound(self, ctx: Any) -> Any:
87
+ """Convert OTEL SpanContext to Truthound SpanContextData.
88
+
89
+ Args:
90
+ ctx: OpenTelemetry SpanContext.
91
+
92
+ Returns:
93
+ Truthound SpanContextData.
94
+ """
95
+ from truthound.observability.tracing.span import SpanContextData
96
+
97
+ # Convert trace state to string
98
+ trace_state = ""
99
+ if hasattr(ctx, "trace_state") and ctx.trace_state:
100
+ pairs = [f"{k}={v}" for k, v in ctx.trace_state.items()]
101
+ trace_state = ",".join(pairs)
102
+
103
+ # Format IDs as hex strings
104
+ trace_id = ctx.trace_id
105
+ span_id = ctx.span_id
106
+
107
+ if isinstance(trace_id, int):
108
+ trace_id = format(trace_id, "032x")
109
+ if isinstance(span_id, int):
110
+ span_id = format(span_id, "016x")
111
+
112
+ return SpanContextData(
113
+ trace_id=trace_id,
114
+ span_id=span_id,
115
+ trace_flags=ctx.trace_flags,
116
+ trace_state=trace_state,
117
+ is_remote=ctx.is_remote,
118
+ )
119
+
120
+
121
+ # =============================================================================
122
+ # Span Bridge
123
+ # =============================================================================
124
+
125
+
126
+ class SpanBridge:
127
+ """Bridges spans between Truthound and OpenTelemetry formats.
128
+
129
+ Converts span data including attributes, events, links, and status.
130
+
131
+ Example:
132
+ >>> bridge = SpanBridge()
133
+ >>> otel_data = bridge.truthound_to_otel_data(truthound_span)
134
+ >>> # Use otel_data with OTEL exporter
135
+ """
136
+
137
+ def __init__(self) -> None:
138
+ """Initialize span bridge."""
139
+ self._context_bridge = SpanContextBridge()
140
+
141
+ def truthound_to_otel_data(self, span: Any) -> dict[str, Any]:
142
+ """Convert Truthound span to OTEL-compatible data dict.
143
+
144
+ This is useful for bridging to OTEL exporters.
145
+
146
+ Args:
147
+ span: Truthound Span.
148
+
149
+ Returns:
150
+ Dictionary with OTEL-compatible span data.
151
+ """
152
+ from truthound.observability.tracing.span import StatusCode, SpanKind
153
+
154
+ # Map status code
155
+ status_map = {
156
+ StatusCode.UNSET: 0,
157
+ StatusCode.OK: 1,
158
+ StatusCode.ERROR: 2,
159
+ }
160
+
161
+ # Map span kind
162
+ kind_map = {
163
+ SpanKind.INTERNAL: 0,
164
+ SpanKind.SERVER: 1,
165
+ SpanKind.CLIENT: 2,
166
+ SpanKind.PRODUCER: 3,
167
+ SpanKind.CONSUMER: 4,
168
+ }
169
+
170
+ status = span.status if hasattr(span, "status") else (StatusCode.UNSET, "")
171
+ if isinstance(status, tuple):
172
+ status_code, status_message = status
173
+ else:
174
+ status_code = status
175
+ status_message = ""
176
+
177
+ return {
178
+ "trace_id": span.context.trace_id if hasattr(span, "context") else "",
179
+ "span_id": span.context.span_id if hasattr(span, "context") else "",
180
+ "parent_span_id": span.parent.span_id if hasattr(span, "parent") and span.parent else None,
181
+ "name": span.name,
182
+ "kind": kind_map.get(span.kind, 0) if hasattr(span, "kind") else 0,
183
+ "start_time_ns": int(span.start_time * 1_000_000_000) if hasattr(span, "start_time") else 0,
184
+ "end_time_ns": int(span.end_time * 1_000_000_000) if hasattr(span, "end_time") and span.end_time else 0,
185
+ "attributes": dict(span.attributes) if hasattr(span, "attributes") else {},
186
+ "events": [
187
+ {
188
+ "name": e.name,
189
+ "timestamp_ns": int(e.timestamp * 1_000_000_000),
190
+ "attributes": dict(e.attributes),
191
+ }
192
+ for e in (span.events if hasattr(span, "events") else [])
193
+ ],
194
+ "links": [
195
+ {
196
+ "trace_id": l.context.trace_id,
197
+ "span_id": l.context.span_id,
198
+ "attributes": dict(l.attributes),
199
+ }
200
+ for l in (span.links if hasattr(span, "links") else [])
201
+ ],
202
+ "status": {
203
+ "code": status_map.get(status_code, 0),
204
+ "message": status_message,
205
+ },
206
+ }
207
+
208
+ def otel_to_truthound_data(self, span_data: dict[str, Any]) -> dict[str, Any]:
209
+ """Convert OTEL span data to Truthound-compatible format.
210
+
211
+ Args:
212
+ span_data: OTEL span data dictionary.
213
+
214
+ Returns:
215
+ Truthound-compatible span data.
216
+ """
217
+ # Map status code back
218
+ status_code_map = {0: "UNSET", 1: "OK", 2: "ERROR"}
219
+
220
+ # Map kind back
221
+ kind_map = {0: "INTERNAL", 1: "SERVER", 2: "CLIENT", 3: "PRODUCER", 4: "CONSUMER"}
222
+
223
+ status = span_data.get("status", {})
224
+
225
+ return {
226
+ "trace_id": span_data.get("trace_id", ""),
227
+ "span_id": span_data.get("span_id", ""),
228
+ "parent_span_id": span_data.get("parent_span_id"),
229
+ "name": span_data.get("name", ""),
230
+ "kind": kind_map.get(span_data.get("kind", 0), "INTERNAL"),
231
+ "start_time": span_data.get("start_time_ns", 0) / 1_000_000_000,
232
+ "end_time": span_data.get("end_time_ns", 0) / 1_000_000_000 if span_data.get("end_time_ns") else None,
233
+ "attributes": span_data.get("attributes", {}),
234
+ "events": span_data.get("events", []),
235
+ "links": span_data.get("links", []),
236
+ "status_code": status_code_map.get(status.get("code", 0), "UNSET"),
237
+ "status_message": status.get("message", ""),
238
+ }
239
+
240
+
241
+ # =============================================================================
242
+ # Span Processor Bridge
243
+ # =============================================================================
244
+
245
+
246
+ class SpanProcessorBridge:
247
+ """Bridges span processors between backends.
248
+
249
+ Allows using OTEL span processors with Truthound spans and vice versa.
250
+
251
+ Example:
252
+ >>> # Use OTEL processor with Truthound
253
+ >>> bridge = SpanProcessorBridge()
254
+ >>> truthound_processor = bridge.wrap_otel_processor(otel_processor)
255
+ >>> provider.add_processor(truthound_processor)
256
+ """
257
+
258
+ def __init__(self) -> None:
259
+ """Initialize processor bridge."""
260
+ self._span_bridge = SpanBridge()
261
+ self._context_bridge = SpanContextBridge()
262
+
263
+ def wrap_otel_processor(self, otel_processor: Any) -> "TruthoundProcessorWrapper":
264
+ """Wrap an OTEL processor for use with Truthound.
265
+
266
+ Args:
267
+ otel_processor: OpenTelemetry SpanProcessor.
268
+
269
+ Returns:
270
+ Truthound-compatible processor wrapper.
271
+ """
272
+ return TruthoundProcessorWrapper(otel_processor, self._span_bridge, self._context_bridge)
273
+
274
+ def wrap_truthound_processor(self, truthound_processor: Any) -> "OTELProcessorWrapper":
275
+ """Wrap a Truthound processor for use with OTEL SDK.
276
+
277
+ Args:
278
+ truthound_processor: Truthound SpanProcessor.
279
+
280
+ Returns:
281
+ OTEL-compatible processor wrapper.
282
+ """
283
+ return OTELProcessorWrapper(truthound_processor, self._span_bridge, self._context_bridge)
284
+
285
+
286
+ class TruthoundProcessorWrapper:
287
+ """Wraps an OTEL processor for use with Truthound's TracerProvider.
288
+
289
+ Translates Truthound spans to OTEL format before passing to the
290
+ wrapped processor.
291
+ """
292
+
293
+ def __init__(
294
+ self,
295
+ otel_processor: Any,
296
+ span_bridge: SpanBridge,
297
+ context_bridge: SpanContextBridge,
298
+ ) -> None:
299
+ """Initialize wrapper.
300
+
301
+ Args:
302
+ otel_processor: OTEL processor to wrap.
303
+ span_bridge: Span bridge for conversions.
304
+ context_bridge: Context bridge for conversions.
305
+ """
306
+ self._processor = otel_processor
307
+ self._span_bridge = span_bridge
308
+ self._context_bridge = context_bridge
309
+
310
+ def on_start(self, span: Any, parent_context: Any | None = None) -> None:
311
+ """Called when a span starts.
312
+
313
+ Args:
314
+ span: Truthound span.
315
+ parent_context: Parent context.
316
+ """
317
+ if not is_otel_sdk_available():
318
+ return
319
+
320
+ try:
321
+ # OTEL processor expects OTEL span, but we can try to pass data
322
+ # Most processors don't heavily use on_start
323
+ pass
324
+ except Exception as e:
325
+ logger.debug(f"Error in wrapped on_start: {e}")
326
+
327
+ def on_end(self, span: Any) -> None:
328
+ """Called when a span ends.
329
+
330
+ Args:
331
+ span: Truthound span.
332
+ """
333
+ if not is_otel_sdk_available():
334
+ return
335
+
336
+ try:
337
+ from opentelemetry.sdk.trace import ReadableSpan
338
+
339
+ # Create a readable span from Truthound span
340
+ otel_span = _create_readable_span_from_truthound(
341
+ span, self._span_bridge, self._context_bridge
342
+ )
343
+ self._processor.on_end(otel_span)
344
+ except Exception as e:
345
+ logger.debug(f"Error in wrapped on_end: {e}")
346
+
347
+ def shutdown(self) -> bool:
348
+ """Shutdown the processor."""
349
+ try:
350
+ self._processor.shutdown()
351
+ return True
352
+ except Exception:
353
+ return False
354
+
355
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
356
+ """Force flush the processor."""
357
+ try:
358
+ return self._processor.force_flush(timeout_millis)
359
+ except Exception:
360
+ return False
361
+
362
+
363
+ class OTELProcessorWrapper:
364
+ """Wraps a Truthound processor for use with OTEL SDK's TracerProvider.
365
+
366
+ Translates OTEL spans to Truthound format before passing to the
367
+ wrapped processor.
368
+ """
369
+
370
+ def __init__(
371
+ self,
372
+ truthound_processor: Any,
373
+ span_bridge: SpanBridge,
374
+ context_bridge: SpanContextBridge,
375
+ ) -> None:
376
+ """Initialize wrapper.
377
+
378
+ Args:
379
+ truthound_processor: Truthound processor to wrap.
380
+ span_bridge: Span bridge for conversions.
381
+ context_bridge: Context bridge for conversions.
382
+ """
383
+ self._processor = truthound_processor
384
+ self._span_bridge = span_bridge
385
+ self._context_bridge = context_bridge
386
+
387
+ def on_start(self, span: Any, parent_context: Any = None) -> None:
388
+ """Called when a span starts.
389
+
390
+ Args:
391
+ span: OTEL span.
392
+ parent_context: OTEL context.
393
+ """
394
+ try:
395
+ # Convert parent context if provided
396
+ truthound_parent = None
397
+ if parent_context:
398
+ from opentelemetry.trace import get_current_span
399
+
400
+ current = get_current_span(parent_context)
401
+ if current and hasattr(current, "get_span_context"):
402
+ truthound_parent = self._context_bridge.otel_to_truthound(
403
+ current.get_span_context()
404
+ )
405
+
406
+ # Create minimal Truthound span wrapper
407
+ wrapper = _create_truthound_span_from_otel(span, self._context_bridge)
408
+ self._processor.on_start(wrapper, truthound_parent)
409
+ except Exception as e:
410
+ logger.debug(f"Error in wrapped on_start: {e}")
411
+
412
+ def on_end(self, span: Any) -> None:
413
+ """Called when a span ends.
414
+
415
+ Args:
416
+ span: OTEL span (ReadableSpan).
417
+ """
418
+ try:
419
+ wrapper = _create_truthound_span_from_otel(span, self._context_bridge)
420
+ self._processor.on_end(wrapper)
421
+ except Exception as e:
422
+ logger.debug(f"Error in wrapped on_end: {e}")
423
+
424
+ def shutdown(self) -> bool:
425
+ """Shutdown the processor."""
426
+ try:
427
+ return self._processor.shutdown()
428
+ except Exception:
429
+ return False
430
+
431
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
432
+ """Force flush the processor."""
433
+ try:
434
+ return self._processor.force_flush(timeout_millis)
435
+ except Exception:
436
+ return False
437
+
438
+
439
+ # =============================================================================
440
+ # Span Exporter Bridge
441
+ # =============================================================================
442
+
443
+
444
+ class SpanExporterBridge:
445
+ """Bridges span exporters between backends.
446
+
447
+ Allows using OTEL exporters with Truthound spans and vice versa.
448
+
449
+ Example:
450
+ >>> # Use OTEL OTLP exporter with Truthound
451
+ >>> from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
452
+ >>>
453
+ >>> bridge = SpanExporterBridge()
454
+ >>> truthound_exporter = bridge.wrap_otel_exporter(OTLPSpanExporter())
455
+ >>> provider.add_processor(SimpleSpanProcessor(truthound_exporter))
456
+ """
457
+
458
+ def __init__(self) -> None:
459
+ """Initialize exporter bridge."""
460
+ self._span_bridge = SpanBridge()
461
+ self._context_bridge = SpanContextBridge()
462
+
463
+ def wrap_otel_exporter(self, otel_exporter: Any) -> "TruthoundExporterWrapper":
464
+ """Wrap an OTEL exporter for use with Truthound.
465
+
466
+ Args:
467
+ otel_exporter: OpenTelemetry SpanExporter.
468
+
469
+ Returns:
470
+ Truthound-compatible exporter wrapper.
471
+ """
472
+ return TruthoundExporterWrapper(otel_exporter, self._span_bridge, self._context_bridge)
473
+
474
+ def wrap_truthound_exporter(self, truthound_exporter: Any) -> "OTELExporterWrapper":
475
+ """Wrap a Truthound exporter for use with OTEL SDK.
476
+
477
+ Args:
478
+ truthound_exporter: Truthound SpanExporter.
479
+
480
+ Returns:
481
+ OTEL-compatible exporter wrapper.
482
+ """
483
+ return OTELExporterWrapper(truthound_exporter, self._span_bridge, self._context_bridge)
484
+
485
+
486
+ class TruthoundExporterWrapper:
487
+ """Wraps an OTEL exporter for use with Truthound.
488
+
489
+ Converts Truthound spans to OTEL ReadableSpan format before exporting.
490
+ """
491
+
492
+ def __init__(
493
+ self,
494
+ otel_exporter: Any,
495
+ span_bridge: SpanBridge,
496
+ context_bridge: SpanContextBridge,
497
+ ) -> None:
498
+ """Initialize wrapper.
499
+
500
+ Args:
501
+ otel_exporter: OTEL exporter to wrap.
502
+ span_bridge: Span bridge for conversions.
503
+ context_bridge: Context bridge for conversions.
504
+ """
505
+ self._exporter = otel_exporter
506
+ self._span_bridge = span_bridge
507
+ self._context_bridge = context_bridge
508
+ self._shutdown = False
509
+
510
+ def export(self, spans: Sequence[Any]) -> Any:
511
+ """Export spans.
512
+
513
+ Args:
514
+ spans: Truthound spans to export.
515
+
516
+ Returns:
517
+ Export result.
518
+ """
519
+ if self._shutdown:
520
+ from truthound.observability.tracing.exporter import ExportResult
521
+ return ExportResult.FAILURE
522
+
523
+ if not is_otel_sdk_available():
524
+ from truthound.observability.tracing.exporter import ExportResult
525
+ return ExportResult.FAILURE
526
+
527
+ try:
528
+ # Convert to OTEL readable spans
529
+ otel_spans = [
530
+ _create_readable_span_from_truthound(s, self._span_bridge, self._context_bridge)
531
+ for s in spans
532
+ ]
533
+
534
+ # Export using OTEL exporter
535
+ result = self._exporter.export(otel_spans)
536
+
537
+ # Convert result
538
+ from opentelemetry.sdk.trace.export import SpanExportResult
539
+ from truthound.observability.tracing.exporter import ExportResult
540
+
541
+ result_map = {
542
+ SpanExportResult.SUCCESS: ExportResult.SUCCESS,
543
+ SpanExportResult.FAILURE: ExportResult.FAILURE,
544
+ }
545
+ return result_map.get(result, ExportResult.FAILURE)
546
+
547
+ except Exception as e:
548
+ logger.error(f"Error exporting spans via OTEL: {e}")
549
+ from truthound.observability.tracing.exporter import ExportResult
550
+ return ExportResult.FAILURE
551
+
552
+ def shutdown(self) -> None:
553
+ """Shutdown the exporter."""
554
+ self._shutdown = True
555
+ try:
556
+ self._exporter.shutdown()
557
+ except Exception:
558
+ pass
559
+
560
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
561
+ """Force flush the exporter."""
562
+ try:
563
+ if hasattr(self._exporter, "force_flush"):
564
+ return self._exporter.force_flush(timeout_millis)
565
+ return True
566
+ except Exception:
567
+ return False
568
+
569
+
570
+ class OTELExporterWrapper:
571
+ """Wraps a Truthound exporter for use with OTEL SDK.
572
+
573
+ Converts OTEL ReadableSpan to Truthound span format before exporting.
574
+ """
575
+
576
+ def __init__(
577
+ self,
578
+ truthound_exporter: Any,
579
+ span_bridge: SpanBridge,
580
+ context_bridge: SpanContextBridge,
581
+ ) -> None:
582
+ """Initialize wrapper.
583
+
584
+ Args:
585
+ truthound_exporter: Truthound exporter to wrap.
586
+ span_bridge: Span bridge for conversions.
587
+ context_bridge: Context bridge for conversions.
588
+ """
589
+ self._exporter = truthound_exporter
590
+ self._span_bridge = span_bridge
591
+ self._context_bridge = context_bridge
592
+ self._shutdown = False
593
+
594
+ def export(self, spans: Sequence[Any]) -> Any:
595
+ """Export spans.
596
+
597
+ Args:
598
+ spans: OTEL spans (ReadableSpan) to export.
599
+
600
+ Returns:
601
+ OTEL SpanExportResult.
602
+ """
603
+ if not is_otel_sdk_available():
604
+ from opentelemetry.sdk.trace.export import SpanExportResult
605
+ return SpanExportResult.FAILURE
606
+
607
+ try:
608
+ # Convert to Truthound spans
609
+ truthound_spans = [
610
+ _create_truthound_span_from_otel(s, self._context_bridge)
611
+ for s in spans
612
+ ]
613
+
614
+ # Export using Truthound exporter
615
+ result = self._exporter.export(truthound_spans)
616
+
617
+ # Convert result
618
+ from opentelemetry.sdk.trace.export import SpanExportResult
619
+ from truthound.observability.tracing.exporter import ExportResult
620
+
621
+ result_map = {
622
+ ExportResult.SUCCESS: SpanExportResult.SUCCESS,
623
+ ExportResult.FAILURE: SpanExportResult.FAILURE,
624
+ ExportResult.RETRY: SpanExportResult.FAILURE,
625
+ }
626
+ return result_map.get(result, SpanExportResult.FAILURE)
627
+
628
+ except Exception as e:
629
+ logger.error(f"Error exporting spans via Truthound: {e}")
630
+ from opentelemetry.sdk.trace.export import SpanExportResult
631
+ return SpanExportResult.FAILURE
632
+
633
+ def shutdown(self) -> None:
634
+ """Shutdown the exporter."""
635
+ self._shutdown = True
636
+ try:
637
+ self._exporter.shutdown()
638
+ except Exception:
639
+ pass
640
+
641
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
642
+ """Force flush the exporter."""
643
+ try:
644
+ if hasattr(self._exporter, "force_flush"):
645
+ return self._exporter.force_flush(timeout_millis)
646
+ return True
647
+ except Exception:
648
+ return False
649
+
650
+
651
+ # =============================================================================
652
+ # Sampler Bridge
653
+ # =============================================================================
654
+
655
+
656
+ class SamplerBridge:
657
+ """Bridges samplers between backends.
658
+
659
+ Allows using OTEL samplers with Truthound and vice versa.
660
+ """
661
+
662
+ def __init__(self) -> None:
663
+ """Initialize sampler bridge."""
664
+ self._context_bridge = SpanContextBridge()
665
+
666
+ def wrap_otel_sampler(self, otel_sampler: Any) -> "TruthoundSamplerWrapper":
667
+ """Wrap an OTEL sampler for use with Truthound.
668
+
669
+ Args:
670
+ otel_sampler: OpenTelemetry Sampler.
671
+
672
+ Returns:
673
+ Truthound-compatible sampler wrapper.
674
+ """
675
+ return TruthoundSamplerWrapper(otel_sampler, self._context_bridge)
676
+
677
+ def wrap_truthound_sampler(self, truthound_sampler: Any) -> "OTELSamplerWrapper":
678
+ """Wrap a Truthound sampler for use with OTEL SDK.
679
+
680
+ Args:
681
+ truthound_sampler: Truthound Sampler.
682
+
683
+ Returns:
684
+ OTEL-compatible sampler wrapper.
685
+ """
686
+ return OTELSamplerWrapper(truthound_sampler, self._context_bridge)
687
+
688
+
689
+ class TruthoundSamplerWrapper:
690
+ """Wraps an OTEL sampler for use with Truthound."""
691
+
692
+ def __init__(self, otel_sampler: Any, context_bridge: SpanContextBridge) -> None:
693
+ self._sampler = otel_sampler
694
+ self._context_bridge = context_bridge
695
+
696
+ def should_sample(
697
+ self,
698
+ parent_context: Any,
699
+ trace_id: str | int,
700
+ name: str,
701
+ kind: Any = None,
702
+ attributes: Mapping[str, Any] | None = None,
703
+ links: Sequence[Any] | None = None,
704
+ ) -> Any:
705
+ """Make sampling decision."""
706
+ from truthound.observability.tracing.sampler import SamplingResult, SamplingDecision
707
+
708
+ if not is_otel_sdk_available():
709
+ return SamplingResult(decision=SamplingDecision.RECORD_AND_SAMPLE)
710
+
711
+ try:
712
+ from opentelemetry.trace import SpanKind
713
+
714
+ # Convert kind
715
+ otel_kind = SpanKind.INTERNAL
716
+ if kind and hasattr(kind, "name"):
717
+ kind_map = {
718
+ "INTERNAL": SpanKind.INTERNAL,
719
+ "SERVER": SpanKind.SERVER,
720
+ "CLIENT": SpanKind.CLIENT,
721
+ "PRODUCER": SpanKind.PRODUCER,
722
+ "CONSUMER": SpanKind.CONSUMER,
723
+ }
724
+ otel_kind = kind_map.get(kind.name, SpanKind.INTERNAL)
725
+
726
+ # Convert trace_id
727
+ if isinstance(trace_id, str):
728
+ trace_id = int(trace_id, 16)
729
+
730
+ # Call OTEL sampler
731
+ otel_result = self._sampler.should_sample(
732
+ parent_context=None, # OTEL uses Context, not SpanContext directly
733
+ trace_id=trace_id,
734
+ name=name,
735
+ kind=otel_kind,
736
+ attributes=dict(attributes) if attributes else None,
737
+ links=links,
738
+ )
739
+
740
+ # Convert result
741
+ from opentelemetry.sdk.trace.sampling import Decision
742
+
743
+ decision_map = {
744
+ Decision.DROP: SamplingDecision.DROP,
745
+ Decision.RECORD_ONLY: SamplingDecision.RECORD_ONLY,
746
+ Decision.RECORD_AND_SAMPLE: SamplingDecision.RECORD_AND_SAMPLE,
747
+ }
748
+
749
+ return SamplingResult(
750
+ decision=decision_map.get(otel_result.decision, SamplingDecision.RECORD_AND_SAMPLE),
751
+ attributes=dict(otel_result.attributes) if otel_result.attributes else {},
752
+ )
753
+
754
+ except Exception as e:
755
+ logger.debug(f"Error in sampler wrapper: {e}")
756
+ return SamplingResult(decision=SamplingDecision.RECORD_AND_SAMPLE)
757
+
758
+ def get_description(self) -> str:
759
+ """Get sampler description."""
760
+ return f"OTELWrapper({self._sampler.get_description()})"
761
+
762
+
763
+ class OTELSamplerWrapper:
764
+ """Wraps a Truthound sampler for use with OTEL SDK."""
765
+
766
+ def __init__(self, truthound_sampler: Any, context_bridge: SpanContextBridge) -> None:
767
+ self._sampler = truthound_sampler
768
+ self._context_bridge = context_bridge
769
+
770
+ def should_sample(
771
+ self,
772
+ parent_context: Any,
773
+ trace_id: int,
774
+ name: str,
775
+ kind: Any = None,
776
+ attributes: Mapping[str, Any] | None = None,
777
+ links: Sequence[Any] | None = None,
778
+ ) -> Any:
779
+ """Make sampling decision."""
780
+ if not is_otel_sdk_available():
781
+ from opentelemetry.sdk.trace.sampling import Decision, SamplingResult
782
+ return SamplingResult(decision=Decision.RECORD_AND_SAMPLE, attributes={})
783
+
784
+ try:
785
+ from opentelemetry.sdk.trace.sampling import Decision, SamplingResult
786
+ from truthound.observability.tracing.sampler import SamplingDecision
787
+
788
+ # Call Truthound sampler
789
+ result = self._sampler.should_sample(
790
+ parent_context=None,
791
+ trace_id=format(trace_id, "032x"),
792
+ name=name,
793
+ kind=kind,
794
+ attributes=attributes,
795
+ links=links,
796
+ )
797
+
798
+ # Convert result
799
+ decision_map = {
800
+ SamplingDecision.DROP: Decision.DROP,
801
+ SamplingDecision.RECORD_ONLY: Decision.RECORD_ONLY,
802
+ SamplingDecision.RECORD_AND_SAMPLE: Decision.RECORD_AND_SAMPLE,
803
+ }
804
+
805
+ return SamplingResult(
806
+ decision=decision_map.get(result.decision, Decision.RECORD_AND_SAMPLE),
807
+ attributes=result.attributes,
808
+ )
809
+
810
+ except Exception as e:
811
+ logger.debug(f"Error in sampler wrapper: {e}")
812
+ from opentelemetry.sdk.trace.sampling import Decision, SamplingResult
813
+ return SamplingResult(decision=Decision.RECORD_AND_SAMPLE, attributes={})
814
+
815
+ def get_description(self) -> str:
816
+ """Get sampler description."""
817
+ return f"TruthoundWrapper({self._sampler.get_description()})"
818
+
819
+
820
+ # =============================================================================
821
+ # Propagator Bridge
822
+ # =============================================================================
823
+
824
+
825
+ class PropagatorBridge:
826
+ """Bridges propagators between backends.
827
+
828
+ Allows using OTEL propagators with Truthound and vice versa.
829
+ """
830
+
831
+ def __init__(self) -> None:
832
+ """Initialize propagator bridge."""
833
+ self._context_bridge = SpanContextBridge()
834
+
835
+ def wrap_otel_propagator(self, otel_propagator: Any) -> "TruthoundPropagatorWrapper":
836
+ """Wrap an OTEL propagator for use with Truthound."""
837
+ return TruthoundPropagatorWrapper(otel_propagator, self._context_bridge)
838
+
839
+ def wrap_truthound_propagator(self, truthound_propagator: Any) -> "OTELPropagatorWrapper":
840
+ """Wrap a Truthound propagator for use with OTEL SDK."""
841
+ return OTELPropagatorWrapper(truthound_propagator, self._context_bridge)
842
+
843
+
844
+ class TruthoundPropagatorWrapper:
845
+ """Wraps an OTEL propagator for use with Truthound."""
846
+
847
+ def __init__(self, otel_propagator: Any, context_bridge: SpanContextBridge) -> None:
848
+ self._propagator = otel_propagator
849
+ self._context_bridge = context_bridge
850
+
851
+ def extract(
852
+ self,
853
+ carrier: Mapping[str, str],
854
+ context: Any = None,
855
+ getter: Any = None,
856
+ ) -> Any:
857
+ """Extract context from carrier."""
858
+ if not is_otel_sdk_available():
859
+ return None
860
+
861
+ try:
862
+ from opentelemetry.propagate import extract
863
+
864
+ otel_context = extract(carrier)
865
+ from opentelemetry.trace import get_current_span
866
+
867
+ span = get_current_span(otel_context)
868
+ if span and hasattr(span, "get_span_context"):
869
+ return self._context_bridge.otel_to_truthound(span.get_span_context())
870
+ except Exception as e:
871
+ logger.debug(f"Error extracting context: {e}")
872
+
873
+ return None
874
+
875
+ def inject(
876
+ self,
877
+ carrier: dict[str, str],
878
+ context: Any = None,
879
+ setter: Any = None,
880
+ ) -> None:
881
+ """Inject context into carrier."""
882
+ if not is_otel_sdk_available() or context is None:
883
+ return
884
+
885
+ try:
886
+ from opentelemetry.propagate import inject
887
+ from opentelemetry.trace import set_span_in_context, NonRecordingSpan
888
+
889
+ otel_ctx = self._context_bridge.truthound_to_otel(context)
890
+ span = NonRecordingSpan(otel_ctx)
891
+ otel_context = set_span_in_context(span)
892
+ inject(carrier, context=otel_context)
893
+ except Exception as e:
894
+ logger.debug(f"Error injecting context: {e}")
895
+
896
+ @property
897
+ def fields(self) -> set[str]:
898
+ """Get propagator fields."""
899
+ if hasattr(self._propagator, "fields"):
900
+ return set(self._propagator.fields)
901
+ return {"traceparent", "tracestate"}
902
+
903
+
904
+ class OTELPropagatorWrapper:
905
+ """Wraps a Truthound propagator for use with OTEL SDK."""
906
+
907
+ def __init__(self, truthound_propagator: Any, context_bridge: SpanContextBridge) -> None:
908
+ self._propagator = truthound_propagator
909
+ self._context_bridge = context_bridge
910
+
911
+ def extract(
912
+ self,
913
+ carrier: Mapping[str, str],
914
+ context: Any = None,
915
+ getter: Any = None,
916
+ ) -> Any:
917
+ """Extract context from carrier."""
918
+ try:
919
+ truthound_ctx = self._propagator.extract(carrier)
920
+ if truthound_ctx:
921
+ from opentelemetry.trace import set_span_in_context, NonRecordingSpan
922
+
923
+ otel_ctx = self._context_bridge.truthound_to_otel(truthound_ctx)
924
+ span = NonRecordingSpan(otel_ctx)
925
+ return set_span_in_context(span, context)
926
+ except Exception as e:
927
+ logger.debug(f"Error extracting context: {e}")
928
+
929
+ return context
930
+
931
+ def inject(
932
+ self,
933
+ carrier: dict[str, str],
934
+ context: Any = None,
935
+ setter: Any = None,
936
+ ) -> None:
937
+ """Inject context into carrier."""
938
+ if not is_otel_sdk_available():
939
+ return
940
+
941
+ try:
942
+ from opentelemetry.trace import get_current_span
943
+
944
+ span = get_current_span(context)
945
+ if span and hasattr(span, "get_span_context"):
946
+ truthound_ctx = self._context_bridge.otel_to_truthound(span.get_span_context())
947
+ self._propagator.inject(carrier, truthound_ctx)
948
+ except Exception as e:
949
+ logger.debug(f"Error injecting context: {e}")
950
+
951
+ @property
952
+ def fields(self) -> set[str]:
953
+ """Get propagator fields."""
954
+ if hasattr(self._propagator, "fields"):
955
+ return self._propagator.fields
956
+ return {"traceparent", "tracestate"}
957
+
958
+
959
+ # =============================================================================
960
+ # Helper Functions
961
+ # =============================================================================
962
+
963
+
964
+ def _create_readable_span_from_truthound(
965
+ span: Any,
966
+ span_bridge: SpanBridge,
967
+ context_bridge: SpanContextBridge,
968
+ ) -> Any:
969
+ """Create an OTEL ReadableSpan from a Truthound span.
970
+
971
+ This creates a minimal ReadableSpan-like object for exporting.
972
+ """
973
+ if not is_otel_sdk_available():
974
+ raise ImportError("OpenTelemetry SDK not available")
975
+
976
+ from opentelemetry.sdk.trace import ReadableSpan
977
+ from opentelemetry.trace import SpanKind, StatusCode, Status
978
+ from opentelemetry.sdk.trace.export import SpanExportResult
979
+
980
+ # Get span data
981
+ data = span_bridge.truthound_to_otel_data(span)
982
+
983
+ # Create SpanContext
984
+ otel_context = context_bridge.truthound_to_otel(span.context)
985
+
986
+ # Create parent context if available
987
+ parent_context = None
988
+ if hasattr(span, "parent") and span.parent:
989
+ parent_context = context_bridge.truthound_to_otel(span.parent)
990
+
991
+ # Map kind
992
+ kind_map = {
993
+ 0: SpanKind.INTERNAL,
994
+ 1: SpanKind.SERVER,
995
+ 2: SpanKind.CLIENT,
996
+ 3: SpanKind.PRODUCER,
997
+ 4: SpanKind.CONSUMER,
998
+ }
999
+ span_kind = kind_map.get(data["kind"], SpanKind.INTERNAL)
1000
+
1001
+ # Map status
1002
+ status_code_map = {0: StatusCode.UNSET, 1: StatusCode.OK, 2: StatusCode.ERROR}
1003
+ status = Status(
1004
+ status_code=status_code_map.get(data["status"]["code"], StatusCode.UNSET),
1005
+ description=data["status"]["message"],
1006
+ )
1007
+
1008
+ # Create a ReadableSpan-like object
1009
+ # Note: This is a simplified version; full implementation would need
1010
+ # to properly implement the ReadableSpan interface
1011
+ class _ReadableSpanAdapter:
1012
+ def __init__(self):
1013
+ self._context = otel_context
1014
+ self._parent = parent_context
1015
+ self._name = data["name"]
1016
+ self._kind = span_kind
1017
+ self._start_time = data["start_time_ns"]
1018
+ self._end_time = data["end_time_ns"]
1019
+ self._attributes = data["attributes"]
1020
+ self._events = tuple()
1021
+ self._links = tuple()
1022
+ self._status = status
1023
+ self._resource = None
1024
+ self._instrumentation_scope = None
1025
+
1026
+ def get_span_context(self):
1027
+ return self._context
1028
+
1029
+ @property
1030
+ def name(self):
1031
+ return self._name
1032
+
1033
+ @property
1034
+ def context(self):
1035
+ return self._context
1036
+
1037
+ @property
1038
+ def parent(self):
1039
+ return self._parent
1040
+
1041
+ @property
1042
+ def kind(self):
1043
+ return self._kind
1044
+
1045
+ @property
1046
+ def start_time(self):
1047
+ return self._start_time
1048
+
1049
+ @property
1050
+ def end_time(self):
1051
+ return self._end_time
1052
+
1053
+ @property
1054
+ def attributes(self):
1055
+ return self._attributes
1056
+
1057
+ @property
1058
+ def events(self):
1059
+ return self._events
1060
+
1061
+ @property
1062
+ def links(self):
1063
+ return self._links
1064
+
1065
+ @property
1066
+ def status(self):
1067
+ return self._status
1068
+
1069
+ @property
1070
+ def resource(self):
1071
+ return self._resource
1072
+
1073
+ @property
1074
+ def instrumentation_scope(self):
1075
+ return self._instrumentation_scope
1076
+
1077
+ return _ReadableSpanAdapter()
1078
+
1079
+
1080
+ def _create_truthound_span_from_otel(
1081
+ otel_span: Any,
1082
+ context_bridge: SpanContextBridge,
1083
+ ) -> Any:
1084
+ """Create a Truthound Span-like object from an OTEL span.
1085
+
1086
+ This creates a minimal span wrapper for Truthound processors.
1087
+ """
1088
+ from truthound.observability.tracing.span import SpanContextData, SpanKind, StatusCode
1089
+
1090
+ # Get context
1091
+ otel_context = otel_span.get_span_context() if hasattr(otel_span, "get_span_context") else otel_span.context
1092
+ truthound_context = context_bridge.otel_to_truthound(otel_context)
1093
+
1094
+ # Map kind
1095
+ kind_map = {
1096
+ "INTERNAL": SpanKind.INTERNAL,
1097
+ "SERVER": SpanKind.SERVER,
1098
+ "CLIENT": SpanKind.CLIENT,
1099
+ "PRODUCER": SpanKind.PRODUCER,
1100
+ "CONSUMER": SpanKind.CONSUMER,
1101
+ }
1102
+
1103
+ span_kind = SpanKind.INTERNAL
1104
+ if hasattr(otel_span, "kind") and otel_span.kind:
1105
+ span_kind = kind_map.get(otel_span.kind.name, SpanKind.INTERNAL)
1106
+
1107
+ # Create wrapper class
1108
+ class _TruthoundSpanAdapter:
1109
+ def __init__(self):
1110
+ self._context = truthound_context
1111
+ self._name = otel_span.name if hasattr(otel_span, "name") else ""
1112
+ self._kind = span_kind
1113
+ self._start_time = otel_span.start_time / 1_000_000_000 if hasattr(otel_span, "start_time") else time.time()
1114
+ self._end_time = otel_span.end_time / 1_000_000_000 if hasattr(otel_span, "end_time") and otel_span.end_time else None
1115
+ self._attributes = dict(otel_span.attributes) if hasattr(otel_span, "attributes") and otel_span.attributes else {}
1116
+ self._events = []
1117
+ self._links = []
1118
+ self._status = (StatusCode.UNSET, "")
1119
+ self._parent = None
1120
+
1121
+ if hasattr(otel_span, "parent") and otel_span.parent:
1122
+ self._parent = context_bridge.otel_to_truthound(otel_span.parent)
1123
+
1124
+ if hasattr(otel_span, "status") and otel_span.status:
1125
+ status_map = {"UNSET": StatusCode.UNSET, "OK": StatusCode.OK, "ERROR": StatusCode.ERROR}
1126
+ self._status = (
1127
+ status_map.get(otel_span.status.status_code.name, StatusCode.UNSET),
1128
+ otel_span.status.description or "",
1129
+ )
1130
+
1131
+ @property
1132
+ def context(self):
1133
+ return self._context
1134
+
1135
+ @property
1136
+ def name(self):
1137
+ return self._name
1138
+
1139
+ @property
1140
+ def kind(self):
1141
+ return self._kind
1142
+
1143
+ @property
1144
+ def start_time(self):
1145
+ return self._start_time
1146
+
1147
+ @property
1148
+ def end_time(self):
1149
+ return self._end_time
1150
+
1151
+ @property
1152
+ def attributes(self):
1153
+ return self._attributes
1154
+
1155
+ @property
1156
+ def events(self):
1157
+ return self._events
1158
+
1159
+ @property
1160
+ def links(self):
1161
+ return self._links
1162
+
1163
+ @property
1164
+ def status(self):
1165
+ return self._status
1166
+
1167
+ @property
1168
+ def parent(self):
1169
+ return self._parent
1170
+
1171
+ @property
1172
+ def duration_ms(self):
1173
+ if self._end_time is None:
1174
+ return None
1175
+ return (self._end_time - self._start_time) * 1000
1176
+
1177
+ return _TruthoundSpanAdapter()