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,982 @@
1
+ """OpenTelemetry adapter implementation.
2
+
3
+ This module provides adapter classes that wrap both Truthound native
4
+ and OpenTelemetry SDK implementations behind a unified interface.
5
+
6
+ The adapter pattern allows:
7
+ - Seamless switching between backends
8
+ - Gradual migration from Truthound to OTEL SDK
9
+ - Using OTEL exporters with Truthound spans
10
+ - Using Truthound spans with OTEL processors
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ import threading
17
+ from contextlib import contextmanager
18
+ from dataclasses import dataclass, field
19
+ from enum import Enum, auto
20
+ from typing import Any, Iterator, Mapping, Sequence
21
+
22
+ from truthound.observability.tracing.otel.detection import (
23
+ detect_otel_availability,
24
+ is_otel_sdk_available,
25
+ )
26
+ from truthound.observability.tracing.otel.protocols import (
27
+ SpanContextProtocol,
28
+ SpanProtocol,
29
+ TracerProtocol,
30
+ TracerProviderProtocol,
31
+ SpanProcessorProtocol,
32
+ SpanExporterProtocol,
33
+ )
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ # =============================================================================
39
+ # Backend Selection
40
+ # =============================================================================
41
+
42
+
43
+ class TracingBackend(str, Enum):
44
+ """Available tracing backends."""
45
+
46
+ AUTO = "auto" # Auto-detect best available
47
+ TRUTHOUND = "truthound" # Force Truthound native
48
+ OPENTELEMETRY = "opentelemetry" # Force OpenTelemetry SDK
49
+
50
+
51
+ @dataclass
52
+ class AdapterConfig:
53
+ """Configuration for the tracing adapter.
54
+
55
+ Attributes:
56
+ backend: Which backend to use.
57
+ service_name: Service name for traces.
58
+ service_version: Service version.
59
+ environment: Deployment environment.
60
+ exporter_type: Type of exporter (console, otlp, jaeger, zipkin).
61
+ exporter_endpoint: Exporter endpoint URL.
62
+ exporter_headers: Additional headers for exporter.
63
+ sampling_ratio: Sampling ratio (0.0 to 1.0).
64
+ batch_export: Use batch span processor.
65
+ propagators: List of propagator types.
66
+ auto_instrument: Enable auto-instrumentation (if OTEL SDK).
67
+ """
68
+
69
+ backend: TracingBackend = TracingBackend.AUTO
70
+ service_name: str = "truthound"
71
+ service_version: str = ""
72
+ environment: str = ""
73
+ exporter_type: str = "console"
74
+ exporter_endpoint: str = ""
75
+ exporter_headers: dict[str, str] = field(default_factory=dict)
76
+ sampling_ratio: float = 1.0
77
+ batch_export: bool = True
78
+ propagators: list[str] = field(default_factory=lambda: ["w3c"])
79
+ auto_instrument: bool = False
80
+
81
+ @classmethod
82
+ def from_env(cls) -> "AdapterConfig":
83
+ """Create configuration from environment variables."""
84
+ import os
85
+
86
+ backend_str = os.environ.get("TRUTHOUND_TRACING_BACKEND", "auto").lower()
87
+ backend = TracingBackend(backend_str) if backend_str in [b.value for b in TracingBackend] else TracingBackend.AUTO
88
+
89
+ # Parse headers
90
+ headers = {}
91
+ headers_str = os.environ.get("OTEL_EXPORTER_OTLP_HEADERS", "")
92
+ if headers_str:
93
+ for pair in headers_str.split(","):
94
+ if "=" in pair:
95
+ key, value = pair.split("=", 1)
96
+ headers[key.strip()] = value.strip()
97
+
98
+ # Determine exporter type
99
+ exporter_type = "console"
100
+ endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "")
101
+ if endpoint:
102
+ exporter_type = "otlp"
103
+
104
+ # Parse sampling ratio
105
+ try:
106
+ sampling_ratio = float(os.environ.get("OTEL_TRACES_SAMPLER_ARG", "1.0"))
107
+ except ValueError:
108
+ sampling_ratio = 1.0
109
+
110
+ return cls(
111
+ backend=backend,
112
+ service_name=os.environ.get("OTEL_SERVICE_NAME", "truthound"),
113
+ service_version=os.environ.get("OTEL_SERVICE_VERSION", ""),
114
+ environment=os.environ.get("DEPLOYMENT_ENVIRONMENT", ""),
115
+ exporter_type=exporter_type,
116
+ exporter_endpoint=endpoint,
117
+ exporter_headers=headers,
118
+ sampling_ratio=sampling_ratio,
119
+ )
120
+
121
+
122
+ # =============================================================================
123
+ # Span Context Adapter
124
+ # =============================================================================
125
+
126
+
127
+ class SpanContextAdapter:
128
+ """Adapter for span context that works with both backends.
129
+
130
+ Normalizes span context data from either Truthound or OTEL format.
131
+ """
132
+
133
+ def __init__(
134
+ self,
135
+ trace_id: str | int,
136
+ span_id: str | int,
137
+ trace_flags: int = 1,
138
+ trace_state: str = "",
139
+ is_remote: bool = False,
140
+ _native: Any = None,
141
+ ) -> None:
142
+ """Initialize span context adapter.
143
+
144
+ Args:
145
+ trace_id: Trace ID (hex string or int).
146
+ span_id: Span ID (hex string or int).
147
+ trace_flags: Trace flags (0=not sampled, 1=sampled).
148
+ trace_state: W3C trace state string.
149
+ is_remote: Whether context is from remote parent.
150
+ _native: Native span context object (for unwrapping).
151
+ """
152
+ # Normalize to hex strings
153
+ if isinstance(trace_id, int):
154
+ self._trace_id = format(trace_id, "032x")
155
+ else:
156
+ self._trace_id = str(trace_id)
157
+
158
+ if isinstance(span_id, int):
159
+ self._span_id = format(span_id, "016x")
160
+ else:
161
+ self._span_id = str(span_id)
162
+
163
+ self._trace_flags = trace_flags
164
+ self._trace_state = trace_state
165
+ self._is_remote = is_remote
166
+ self._native = _native
167
+
168
+ @property
169
+ def trace_id(self) -> str:
170
+ """Get trace ID as hex string."""
171
+ return self._trace_id
172
+
173
+ @property
174
+ def trace_id_int(self) -> int:
175
+ """Get trace ID as integer."""
176
+ return int(self._trace_id, 16)
177
+
178
+ @property
179
+ def span_id(self) -> str:
180
+ """Get span ID as hex string."""
181
+ return self._span_id
182
+
183
+ @property
184
+ def span_id_int(self) -> int:
185
+ """Get span ID as integer."""
186
+ return int(self._span_id, 16)
187
+
188
+ @property
189
+ def trace_flags(self) -> int:
190
+ """Get trace flags."""
191
+ return self._trace_flags
192
+
193
+ @property
194
+ def trace_state(self) -> str:
195
+ """Get trace state."""
196
+ return self._trace_state
197
+
198
+ @property
199
+ def is_valid(self) -> bool:
200
+ """Check if context is valid."""
201
+ return bool(self._trace_id and self._span_id and
202
+ self._trace_id != "0" * 32 and self._span_id != "0" * 16)
203
+
204
+ @property
205
+ def is_remote(self) -> bool:
206
+ """Check if context is from remote parent."""
207
+ return self._is_remote
208
+
209
+ @property
210
+ def is_sampled(self) -> bool:
211
+ """Check if span is sampled."""
212
+ return bool(self._trace_flags & 0x01)
213
+
214
+ def to_w3c_traceparent(self) -> str:
215
+ """Convert to W3C traceparent header."""
216
+ return f"00-{self._trace_id}-{self._span_id}-{self._trace_flags:02x}"
217
+
218
+ @classmethod
219
+ def from_w3c_traceparent(cls, header: str) -> "SpanContextAdapter | None":
220
+ """Parse from W3C traceparent header."""
221
+ try:
222
+ parts = header.split("-")
223
+ if len(parts) != 4 or parts[0] != "00":
224
+ return None
225
+ return cls(
226
+ trace_id=parts[1],
227
+ span_id=parts[2],
228
+ trace_flags=int(parts[3], 16),
229
+ is_remote=True,
230
+ )
231
+ except (ValueError, IndexError):
232
+ return None
233
+
234
+ @classmethod
235
+ def from_truthound(cls, ctx: Any) -> "SpanContextAdapter":
236
+ """Create from Truthound SpanContextData."""
237
+ return cls(
238
+ trace_id=ctx.trace_id,
239
+ span_id=ctx.span_id,
240
+ trace_flags=ctx.trace_flags,
241
+ trace_state=getattr(ctx, "trace_state", ""),
242
+ is_remote=getattr(ctx, "is_remote", False),
243
+ _native=ctx,
244
+ )
245
+
246
+ @classmethod
247
+ def from_otel(cls, ctx: Any) -> "SpanContextAdapter":
248
+ """Create from OpenTelemetry SpanContext."""
249
+ return cls(
250
+ trace_id=ctx.trace_id,
251
+ span_id=ctx.span_id,
252
+ trace_flags=ctx.trace_flags,
253
+ trace_state=str(ctx.trace_state) if ctx.trace_state else "",
254
+ is_remote=ctx.is_remote,
255
+ _native=ctx,
256
+ )
257
+
258
+ def to_truthound(self) -> Any:
259
+ """Convert to Truthound SpanContextData."""
260
+ from truthound.observability.tracing.span import SpanContextData
261
+
262
+ return SpanContextData(
263
+ trace_id=self._trace_id,
264
+ span_id=self._span_id,
265
+ trace_flags=self._trace_flags,
266
+ trace_state=self._trace_state,
267
+ is_remote=self._is_remote,
268
+ )
269
+
270
+ def to_otel(self) -> Any:
271
+ """Convert to OpenTelemetry SpanContext."""
272
+ if not is_otel_sdk_available():
273
+ raise ImportError("OpenTelemetry SDK not available")
274
+
275
+ from opentelemetry.trace import SpanContext, TraceState
276
+
277
+ trace_state = TraceState()
278
+ if self._trace_state:
279
+ for pair in self._trace_state.split(","):
280
+ if "=" in pair:
281
+ key, value = pair.split("=", 1)
282
+ trace_state = trace_state.add(key.strip(), value.strip())
283
+
284
+ return SpanContext(
285
+ trace_id=self.trace_id_int,
286
+ span_id=self.span_id_int,
287
+ is_remote=self._is_remote,
288
+ trace_flags=self._trace_flags,
289
+ trace_state=trace_state,
290
+ )
291
+
292
+ def unwrap(self) -> Any:
293
+ """Get the native span context object."""
294
+ return self._native
295
+
296
+
297
+ # =============================================================================
298
+ # Span Adapter
299
+ # =============================================================================
300
+
301
+
302
+ class SpanAdapter:
303
+ """Adapter for spans that works with both backends.
304
+
305
+ Provides a unified interface regardless of whether the underlying
306
+ span is from Truthound or OpenTelemetry SDK.
307
+ """
308
+
309
+ def __init__(self, span: Any, backend: TracingBackend) -> None:
310
+ """Initialize span adapter.
311
+
312
+ Args:
313
+ span: Native span object.
314
+ backend: Backend type.
315
+ """
316
+ self._span = span
317
+ self._backend = backend
318
+
319
+ @property
320
+ def context(self) -> SpanContextAdapter:
321
+ """Get span context."""
322
+ if self._backend == TracingBackend.OPENTELEMETRY:
323
+ return SpanContextAdapter.from_otel(self._span.get_span_context())
324
+ else:
325
+ return SpanContextAdapter.from_truthound(self._span.context)
326
+
327
+ @property
328
+ def name(self) -> str:
329
+ """Get span name."""
330
+ return self._span.name
331
+
332
+ def get_span_context(self) -> SpanContextAdapter:
333
+ """Get span context (OTEL API compatible)."""
334
+ return self.context
335
+
336
+ def set_attribute(self, key: str, value: Any) -> "SpanAdapter":
337
+ """Set a span attribute."""
338
+ self._span.set_attribute(key, value)
339
+ return self
340
+
341
+ def set_attributes(self, attributes: Mapping[str, Any]) -> "SpanAdapter":
342
+ """Set multiple attributes."""
343
+ if self._backend == TracingBackend.OPENTELEMETRY:
344
+ self._span.set_attributes(dict(attributes))
345
+ else:
346
+ self._span.set_attributes(attributes)
347
+ return self
348
+
349
+ def add_event(
350
+ self,
351
+ name: str,
352
+ attributes: Mapping[str, Any] | None = None,
353
+ timestamp: int | float | None = None,
354
+ ) -> "SpanAdapter":
355
+ """Add an event to the span."""
356
+ self._span.add_event(name, attributes=attributes, timestamp=timestamp)
357
+ return self
358
+
359
+ def set_status(self, status: Any, description: str | None = None) -> "SpanAdapter":
360
+ """Set span status."""
361
+ if self._backend == TracingBackend.OPENTELEMETRY:
362
+ from opentelemetry.trace import StatusCode
363
+
364
+ if isinstance(status, str):
365
+ status_map = {"ok": StatusCode.OK, "error": StatusCode.ERROR}
366
+ status = status_map.get(status.lower(), StatusCode.UNSET)
367
+ self._span.set_status(status, description)
368
+ else:
369
+ from truthound.observability.tracing.span import StatusCode
370
+
371
+ if isinstance(status, str):
372
+ status_map = {"ok": StatusCode.OK, "error": StatusCode.ERROR}
373
+ status = status_map.get(status.lower(), StatusCode.UNSET)
374
+ self._span.set_status(status, description or "")
375
+ return self
376
+
377
+ def record_exception(
378
+ self,
379
+ exception: BaseException,
380
+ attributes: Mapping[str, Any] | None = None,
381
+ timestamp: int | float | None = None,
382
+ escaped: bool = False,
383
+ ) -> "SpanAdapter":
384
+ """Record an exception."""
385
+ if self._backend == TracingBackend.OPENTELEMETRY:
386
+ self._span.record_exception(exception, attributes=attributes)
387
+ else:
388
+ self._span.record_exception(exception, attributes=attributes, escaped=escaped)
389
+ return self
390
+
391
+ def update_name(self, name: str) -> "SpanAdapter":
392
+ """Update span name."""
393
+ self._span.update_name(name)
394
+ return self
395
+
396
+ def end(self, end_time: int | float | None = None) -> None:
397
+ """End the span."""
398
+ self._span.end(end_time)
399
+
400
+ def is_recording(self) -> bool:
401
+ """Check if span is recording."""
402
+ return self._span.is_recording()
403
+
404
+ def unwrap(self) -> Any:
405
+ """Get the native span object."""
406
+ return self._span
407
+
408
+ def __enter__(self) -> "SpanAdapter":
409
+ """Enter context manager."""
410
+ return self
411
+
412
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
413
+ """Exit context manager."""
414
+ if exc_val is not None:
415
+ self.record_exception(exc_val, escaped=True)
416
+ self.set_status("error", str(exc_val))
417
+ self.end()
418
+
419
+
420
+ # =============================================================================
421
+ # Tracer Adapter
422
+ # =============================================================================
423
+
424
+
425
+ class TracerAdapter:
426
+ """Adapter for tracers that works with both backends.
427
+
428
+ Provides a unified interface for creating spans regardless of
429
+ the underlying implementation.
430
+ """
431
+
432
+ def __init__(self, tracer: Any, backend: TracingBackend) -> None:
433
+ """Initialize tracer adapter.
434
+
435
+ Args:
436
+ tracer: Native tracer object.
437
+ backend: Backend type.
438
+ """
439
+ self._tracer = tracer
440
+ self._backend = backend
441
+
442
+ @property
443
+ def name(self) -> str:
444
+ """Get tracer name."""
445
+ if hasattr(self._tracer, "name"):
446
+ return self._tracer.name
447
+ if hasattr(self._tracer, "_name"):
448
+ return self._tracer._name
449
+ return "unknown"
450
+
451
+ def start_span(
452
+ self,
453
+ name: str,
454
+ context: SpanContextAdapter | None = None,
455
+ kind: Any = None,
456
+ attributes: Mapping[str, Any] | None = None,
457
+ links: Sequence[Any] | None = None,
458
+ start_time: int | float | None = None,
459
+ record_exception: bool = True,
460
+ set_status_on_exception: bool = True,
461
+ ) -> SpanAdapter:
462
+ """Start a new span.
463
+
464
+ Args:
465
+ name: Span name.
466
+ context: Parent context (optional).
467
+ kind: Span kind.
468
+ attributes: Initial attributes.
469
+ links: Links to other spans.
470
+ start_time: Start time.
471
+ record_exception: Whether to record exceptions.
472
+ set_status_on_exception: Whether to set error status on exception.
473
+
474
+ Returns:
475
+ SpanAdapter wrapping the created span.
476
+ """
477
+ if self._backend == TracingBackend.OPENTELEMETRY:
478
+ return self._start_span_otel(
479
+ name, context, kind, attributes, links, start_time
480
+ )
481
+ else:
482
+ return self._start_span_truthound(
483
+ name, context, kind, attributes, links, start_time
484
+ )
485
+
486
+ def _start_span_truthound(
487
+ self,
488
+ name: str,
489
+ context: SpanContextAdapter | None,
490
+ kind: Any,
491
+ attributes: Mapping[str, Any] | None,
492
+ links: Sequence[Any] | None,
493
+ start_time: int | float | None,
494
+ ) -> SpanAdapter:
495
+ """Start span using Truthound backend."""
496
+ from truthound.observability.tracing.span import SpanKind
497
+
498
+ # Convert kind
499
+ span_kind = SpanKind.INTERNAL
500
+ if kind is not None:
501
+ if isinstance(kind, SpanKind):
502
+ span_kind = kind
503
+ elif hasattr(kind, "name"):
504
+ kind_map = {
505
+ "INTERNAL": SpanKind.INTERNAL,
506
+ "SERVER": SpanKind.SERVER,
507
+ "CLIENT": SpanKind.CLIENT,
508
+ "PRODUCER": SpanKind.PRODUCER,
509
+ "CONSUMER": SpanKind.CONSUMER,
510
+ }
511
+ span_kind = kind_map.get(kind.name, SpanKind.INTERNAL)
512
+
513
+ # Convert parent context
514
+ parent = context.to_truthound() if context else None
515
+
516
+ span = self._tracer.start_span(
517
+ name=name,
518
+ kind=span_kind,
519
+ attributes=attributes,
520
+ links=links,
521
+ start_time=start_time,
522
+ parent=parent,
523
+ )
524
+
525
+ return SpanAdapter(span, self._backend)
526
+
527
+ def _start_span_otel(
528
+ self,
529
+ name: str,
530
+ context: SpanContextAdapter | None,
531
+ kind: Any,
532
+ attributes: Mapping[str, Any] | None,
533
+ links: Sequence[Any] | None,
534
+ start_time: int | float | None,
535
+ ) -> SpanAdapter:
536
+ """Start span using OpenTelemetry backend."""
537
+ from opentelemetry.trace import SpanKind
538
+
539
+ # Convert kind
540
+ span_kind = SpanKind.INTERNAL
541
+ if kind is not None:
542
+ if isinstance(kind, SpanKind):
543
+ span_kind = kind
544
+ elif hasattr(kind, "name"):
545
+ kind_map = {
546
+ "INTERNAL": SpanKind.INTERNAL,
547
+ "SERVER": SpanKind.SERVER,
548
+ "CLIENT": SpanKind.CLIENT,
549
+ "PRODUCER": SpanKind.PRODUCER,
550
+ "CONSUMER": SpanKind.CONSUMER,
551
+ }
552
+ span_kind = kind_map.get(kind.name, SpanKind.INTERNAL)
553
+
554
+ # Start span (context handling differs in OTEL)
555
+ span = self._tracer.start_span(
556
+ name=name,
557
+ kind=span_kind,
558
+ attributes=dict(attributes) if attributes else None,
559
+ start_time=start_time,
560
+ )
561
+
562
+ return SpanAdapter(span, self._backend)
563
+
564
+ @contextmanager
565
+ def start_as_current_span(
566
+ self,
567
+ name: str,
568
+ context: SpanContextAdapter | None = None,
569
+ kind: Any = None,
570
+ attributes: Mapping[str, Any] | None = None,
571
+ links: Sequence[Any] | None = None,
572
+ start_time: int | float | None = None,
573
+ record_exception: bool = True,
574
+ set_status_on_exception: bool = True,
575
+ end_on_exit: bool = True,
576
+ ) -> Iterator[SpanAdapter]:
577
+ """Start a span and set it as current.
578
+
579
+ Context manager that creates a span, makes it the current span,
580
+ and ends it when the context exits.
581
+
582
+ Yields:
583
+ SpanAdapter for the created span.
584
+ """
585
+ if self._backend == TracingBackend.OPENTELEMETRY:
586
+ yield from self._start_as_current_otel(
587
+ name, kind, attributes, links, start_time,
588
+ record_exception, set_status_on_exception, end_on_exit
589
+ )
590
+ else:
591
+ yield from self._start_as_current_truthound(
592
+ name, context, kind, attributes, links, start_time,
593
+ record_exception, set_status_on_exception, end_on_exit
594
+ )
595
+
596
+ def _start_as_current_truthound(
597
+ self,
598
+ name: str,
599
+ context: SpanContextAdapter | None,
600
+ kind: Any,
601
+ attributes: Mapping[str, Any] | None,
602
+ links: Sequence[Any] | None,
603
+ start_time: int | float | None,
604
+ record_exception: bool,
605
+ set_status_on_exception: bool,
606
+ end_on_exit: bool,
607
+ ) -> Iterator[SpanAdapter]:
608
+ """Start as current using Truthound backend."""
609
+ with self._tracer.start_as_current_span(
610
+ name=name,
611
+ kind=kind,
612
+ attributes=attributes,
613
+ links=links,
614
+ start_time=start_time,
615
+ record_exception=record_exception,
616
+ set_status_on_exception=set_status_on_exception,
617
+ end_on_exit=end_on_exit,
618
+ ) as span:
619
+ yield SpanAdapter(span, self._backend)
620
+
621
+ def _start_as_current_otel(
622
+ self,
623
+ name: str,
624
+ kind: Any,
625
+ attributes: Mapping[str, Any] | None,
626
+ links: Sequence[Any] | None,
627
+ start_time: int | float | None,
628
+ record_exception: bool,
629
+ set_status_on_exception: bool,
630
+ end_on_exit: bool,
631
+ ) -> Iterator[SpanAdapter]:
632
+ """Start as current using OpenTelemetry backend."""
633
+ from opentelemetry.trace import SpanKind
634
+
635
+ span_kind = SpanKind.INTERNAL
636
+ if kind is not None:
637
+ if hasattr(kind, "name"):
638
+ kind_map = {
639
+ "INTERNAL": SpanKind.INTERNAL,
640
+ "SERVER": SpanKind.SERVER,
641
+ "CLIENT": SpanKind.CLIENT,
642
+ "PRODUCER": SpanKind.PRODUCER,
643
+ "CONSUMER": SpanKind.CONSUMER,
644
+ }
645
+ span_kind = kind_map.get(kind.name, SpanKind.INTERNAL)
646
+
647
+ with self._tracer.start_as_current_span(
648
+ name=name,
649
+ kind=span_kind,
650
+ attributes=dict(attributes) if attributes else None,
651
+ start_time=start_time,
652
+ record_exception=record_exception,
653
+ set_status_on_exception=set_status_on_exception,
654
+ end_on_exit=end_on_exit,
655
+ ) as span:
656
+ yield SpanAdapter(span, self._backend)
657
+
658
+ def unwrap(self) -> Any:
659
+ """Get the native tracer object."""
660
+ return self._tracer
661
+
662
+
663
+ # =============================================================================
664
+ # TracerProvider Adapter
665
+ # =============================================================================
666
+
667
+
668
+ class TracerProviderAdapter:
669
+ """Adapter for tracer providers that works with both backends.
670
+
671
+ Provides a unified interface for obtaining tracers regardless of
672
+ the underlying implementation.
673
+ """
674
+
675
+ def __init__(self, provider: Any, backend: TracingBackend) -> None:
676
+ """Initialize tracer provider adapter.
677
+
678
+ Args:
679
+ provider: Native tracer provider object.
680
+ backend: Backend type.
681
+ """
682
+ self._provider = provider
683
+ self._backend = backend
684
+ self._tracers: dict[tuple[str, str], TracerAdapter] = {}
685
+ self._lock = threading.Lock()
686
+
687
+ @property
688
+ def backend(self) -> TracingBackend:
689
+ """Get the backend type."""
690
+ return self._backend
691
+
692
+ def get_tracer(
693
+ self,
694
+ name: str,
695
+ version: str = "",
696
+ schema_url: str = "",
697
+ ) -> TracerAdapter:
698
+ """Get a tracer.
699
+
700
+ Args:
701
+ name: Instrumentation library name.
702
+ version: Instrumentation library version.
703
+ schema_url: Schema URL.
704
+
705
+ Returns:
706
+ TracerAdapter wrapping the tracer.
707
+ """
708
+ key = (name, version)
709
+
710
+ with self._lock:
711
+ if key not in self._tracers:
712
+ if self._backend == TracingBackend.OPENTELEMETRY:
713
+ tracer = self._provider.get_tracer(name, version, schema_url)
714
+ else:
715
+ tracer = self._provider.get_tracer(name, version, schema_url)
716
+ self._tracers[key] = TracerAdapter(tracer, self._backend)
717
+ return self._tracers[key]
718
+
719
+ def add_span_processor(self, processor: Any) -> None:
720
+ """Add a span processor.
721
+
722
+ Args:
723
+ processor: Span processor to add.
724
+ """
725
+ if hasattr(self._provider, "add_span_processor"):
726
+ self._provider.add_span_processor(processor)
727
+ elif hasattr(self._provider, "add_processor"):
728
+ self._provider.add_processor(processor)
729
+
730
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
731
+ """Force flush all processors.
732
+
733
+ Args:
734
+ timeout_millis: Timeout in milliseconds.
735
+
736
+ Returns:
737
+ True if successful.
738
+ """
739
+ return self._provider.force_flush(timeout_millis)
740
+
741
+ def shutdown(self) -> bool:
742
+ """Shutdown the provider.
743
+
744
+ Returns:
745
+ True if successful.
746
+ """
747
+ return self._provider.shutdown()
748
+
749
+ def unwrap(self) -> Any:
750
+ """Get the native provider object."""
751
+ return self._provider
752
+
753
+
754
+ # =============================================================================
755
+ # Global State
756
+ # =============================================================================
757
+
758
+
759
+ _global_adapter: TracerProviderAdapter | None = None
760
+ _adapter_lock = threading.Lock()
761
+ _current_config: AdapterConfig | None = None
762
+
763
+
764
+ def _create_truthound_provider(config: AdapterConfig) -> TracerProviderAdapter:
765
+ """Create a Truthound-based provider."""
766
+ from truthound.observability.tracing.config import configure_tracing, TracingConfig
767
+
768
+ tracing_config = TracingConfig(
769
+ service_name=config.service_name,
770
+ service_version=config.service_version,
771
+ environment=config.environment,
772
+ exporter=config.exporter_type,
773
+ endpoint=config.exporter_endpoint,
774
+ headers=config.exporter_headers,
775
+ sampling_ratio=config.sampling_ratio,
776
+ batch_export=config.batch_export,
777
+ )
778
+
779
+ provider = configure_tracing(tracing_config, set_global=False)
780
+ return TracerProviderAdapter(provider, TracingBackend.TRUTHOUND)
781
+
782
+
783
+ def _create_otel_provider(config: AdapterConfig) -> TracerProviderAdapter:
784
+ """Create an OpenTelemetry SDK-based provider."""
785
+ from opentelemetry.sdk.trace import TracerProvider
786
+ from opentelemetry.sdk.resources import Resource, SERVICE_NAME, SERVICE_VERSION
787
+ from opentelemetry.sdk.trace.sampling import TraceIdRatioBased, ParentBased
788
+
789
+ # Create resource
790
+ resource_attrs = {SERVICE_NAME: config.service_name}
791
+ if config.service_version:
792
+ resource_attrs[SERVICE_VERSION] = config.service_version
793
+ if config.environment:
794
+ resource_attrs["deployment.environment"] = config.environment
795
+
796
+ resource = Resource.create(resource_attrs)
797
+
798
+ # Create sampler
799
+ sampler = ParentBased(root=TraceIdRatioBased(config.sampling_ratio))
800
+
801
+ # Create provider
802
+ provider = TracerProvider(resource=resource, sampler=sampler)
803
+
804
+ # Add exporter
805
+ if config.exporter_type == "otlp":
806
+ try:
807
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
808
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
809
+
810
+ exporter = OTLPSpanExporter(
811
+ endpoint=config.exporter_endpoint or "http://localhost:4317",
812
+ )
813
+ processor = BatchSpanProcessor(exporter)
814
+ provider.add_span_processor(processor)
815
+ except ImportError:
816
+ logger.warning("OTLP exporter not available, using console")
817
+ _add_console_processor(provider)
818
+
819
+ elif config.exporter_type == "jaeger":
820
+ try:
821
+ from opentelemetry.exporter.jaeger.thrift import JaegerExporter
822
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
823
+
824
+ exporter = JaegerExporter()
825
+ processor = BatchSpanProcessor(exporter)
826
+ provider.add_span_processor(processor)
827
+ except ImportError:
828
+ logger.warning("Jaeger exporter not available, using console")
829
+ _add_console_processor(provider)
830
+
831
+ elif config.exporter_type == "zipkin":
832
+ try:
833
+ from opentelemetry.exporter.zipkin.json import ZipkinExporter
834
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
835
+
836
+ exporter = ZipkinExporter(endpoint=config.exporter_endpoint)
837
+ processor = BatchSpanProcessor(exporter)
838
+ provider.add_span_processor(processor)
839
+ except ImportError:
840
+ logger.warning("Zipkin exporter not available, using console")
841
+ _add_console_processor(provider)
842
+
843
+ else:
844
+ _add_console_processor(provider)
845
+
846
+ return TracerProviderAdapter(provider, TracingBackend.OPENTELEMETRY)
847
+
848
+
849
+ def _add_console_processor(provider: Any) -> None:
850
+ """Add console span processor to provider."""
851
+ from opentelemetry.sdk.trace.export import SimpleSpanProcessor, ConsoleSpanExporter
852
+
853
+ provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
854
+
855
+
856
+ def configure(
857
+ config: AdapterConfig | None = None,
858
+ *,
859
+ backend: TracingBackend | str | None = None,
860
+ service_name: str | None = None,
861
+ exporter_type: str | None = None,
862
+ exporter_endpoint: str | None = None,
863
+ sampling_ratio: float | None = None,
864
+ ) -> TracerProviderAdapter:
865
+ """Configure the global tracing adapter.
866
+
867
+ This is the main entry point for setting up tracing with automatic
868
+ backend selection.
869
+
870
+ Args:
871
+ config: Full configuration object.
872
+ backend: Backend to use (overrides config).
873
+ service_name: Service name (overrides config).
874
+ exporter_type: Exporter type (overrides config).
875
+ exporter_endpoint: Exporter endpoint (overrides config).
876
+ sampling_ratio: Sampling ratio (overrides config).
877
+
878
+ Returns:
879
+ Configured TracerProviderAdapter.
880
+
881
+ Example:
882
+ >>> # Auto-detect backend
883
+ >>> provider = configure(service_name="my-service")
884
+ >>> tracer = provider.get_tracer("my-component")
885
+
886
+ >>> # Force specific backend
887
+ >>> provider = configure(backend="truthound")
888
+ """
889
+ global _global_adapter, _current_config
890
+
891
+ with _adapter_lock:
892
+ # Start with config or defaults
893
+ if config is None:
894
+ config = AdapterConfig()
895
+
896
+ # Apply overrides
897
+ if backend is not None:
898
+ if isinstance(backend, str):
899
+ backend = TracingBackend(backend)
900
+ config.backend = backend
901
+ if service_name:
902
+ config.service_name = service_name
903
+ if exporter_type:
904
+ config.exporter_type = exporter_type
905
+ if exporter_endpoint:
906
+ config.exporter_endpoint = exporter_endpoint
907
+ if sampling_ratio is not None:
908
+ config.sampling_ratio = sampling_ratio
909
+
910
+ # Determine actual backend
911
+ actual_backend = config.backend
912
+ if actual_backend == TracingBackend.AUTO:
913
+ if is_otel_sdk_available():
914
+ logger.info("OpenTelemetry SDK detected, using OTEL backend")
915
+ actual_backend = TracingBackend.OPENTELEMETRY
916
+ else:
917
+ logger.info("OpenTelemetry SDK not found, using Truthound backend")
918
+ actual_backend = TracingBackend.TRUTHOUND
919
+
920
+ # Create provider
921
+ if actual_backend == TracingBackend.OPENTELEMETRY:
922
+ _global_adapter = _create_otel_provider(config)
923
+ else:
924
+ _global_adapter = _create_truthound_provider(config)
925
+
926
+ _current_config = config
927
+ return _global_adapter
928
+
929
+
930
+ def get_tracer_provider() -> TracerProviderAdapter:
931
+ """Get the global tracer provider adapter.
932
+
933
+ Automatically configures with defaults if not already configured.
934
+
935
+ Returns:
936
+ Global TracerProviderAdapter.
937
+ """
938
+ global _global_adapter
939
+
940
+ with _adapter_lock:
941
+ if _global_adapter is None:
942
+ return configure()
943
+ return _global_adapter
944
+
945
+
946
+ def get_tracer(name: str, version: str = "") -> TracerAdapter:
947
+ """Get a tracer from the global provider.
948
+
949
+ Convenience function for quick access.
950
+
951
+ Args:
952
+ name: Instrumentation name.
953
+ version: Instrumentation version.
954
+
955
+ Returns:
956
+ TracerAdapter.
957
+ """
958
+ return get_tracer_provider().get_tracer(name, version)
959
+
960
+
961
+ def get_current_backend() -> TracingBackend:
962
+ """Get the currently active backend.
963
+
964
+ Returns:
965
+ Current TracingBackend.
966
+ """
967
+ provider = get_tracer_provider()
968
+ return provider.backend
969
+
970
+
971
+ def reset_global_adapter() -> None:
972
+ """Reset the global adapter (mainly for testing)."""
973
+ global _global_adapter, _current_config
974
+
975
+ with _adapter_lock:
976
+ if _global_adapter is not None:
977
+ try:
978
+ _global_adapter.shutdown()
979
+ except Exception:
980
+ pass
981
+ _global_adapter = None
982
+ _current_config = None