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,784 @@
1
+ """Distributed tracing for store operations.
2
+
3
+ This module provides OpenTelemetry-compatible distributed tracing with
4
+ support for multiple backends (OTLP, Jaeger, Zipkin, etc.).
5
+
6
+ Features:
7
+ - OpenTelemetry-compatible API
8
+ - Context propagation (W3C TraceContext, B3)
9
+ - Multiple exporter support
10
+ - Automatic span attributes
11
+ - Error recording
12
+ - Sampling strategies
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ import threading
19
+ import time
20
+ import uuid
21
+ from abc import ABC, abstractmethod
22
+ from contextlib import contextmanager
23
+ from dataclasses import dataclass, field
24
+ from datetime import datetime
25
+ from enum import Enum
26
+ from typing import Any, Callable, Generator, TypeVar
27
+
28
+ from truthound.stores.observability.config import TracingConfig, TracingSampler
29
+ from truthound.stores.observability.protocols import (
30
+ ObservabilityContext,
31
+ TracingBackend,
32
+ )
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+ T = TypeVar("T")
37
+
38
+
39
+ class SpanKind(str, Enum):
40
+ """Types of spans."""
41
+
42
+ INTERNAL = "internal"
43
+ SERVER = "server"
44
+ CLIENT = "client"
45
+ PRODUCER = "producer"
46
+ CONSUMER = "consumer"
47
+
48
+
49
+ class SpanStatus(str, Enum):
50
+ """Span status codes."""
51
+
52
+ UNSET = "unset"
53
+ OK = "ok"
54
+ ERROR = "error"
55
+
56
+
57
+ @dataclass
58
+ class SpanContext:
59
+ """Context for a span, used for propagation.
60
+
61
+ Follows W3C TraceContext specification.
62
+ """
63
+
64
+ trace_id: str
65
+ span_id: str
66
+ trace_flags: int = 1 # 1 = sampled
67
+ trace_state: str = ""
68
+ is_remote: bool = False
69
+
70
+ @classmethod
71
+ def generate(cls) -> "SpanContext":
72
+ """Generate a new span context."""
73
+ return cls(
74
+ trace_id=uuid.uuid4().hex,
75
+ span_id=uuid.uuid4().hex[:16],
76
+ )
77
+
78
+ @classmethod
79
+ def from_parent(cls, parent: "SpanContext") -> "SpanContext":
80
+ """Create child span context from parent."""
81
+ return cls(
82
+ trace_id=parent.trace_id,
83
+ span_id=uuid.uuid4().hex[:16],
84
+ trace_flags=parent.trace_flags,
85
+ trace_state=parent.trace_state,
86
+ )
87
+
88
+ def to_traceparent(self) -> str:
89
+ """Convert to W3C traceparent header format."""
90
+ return f"00-{self.trace_id}-{self.span_id}-{self.trace_flags:02x}"
91
+
92
+ @classmethod
93
+ def from_traceparent(cls, value: str) -> "SpanContext | None":
94
+ """Parse W3C traceparent header."""
95
+ try:
96
+ parts = value.split("-")
97
+ if len(parts) != 4 or parts[0] != "00":
98
+ return None
99
+ return cls(
100
+ trace_id=parts[1],
101
+ span_id=parts[2],
102
+ trace_flags=int(parts[3], 16),
103
+ is_remote=True,
104
+ )
105
+ except (ValueError, IndexError):
106
+ return None
107
+
108
+
109
+ @dataclass
110
+ class SpanEvent:
111
+ """An event within a span."""
112
+
113
+ name: str
114
+ timestamp: float = field(default_factory=time.time)
115
+ attributes: dict[str, Any] = field(default_factory=dict)
116
+
117
+
118
+ @dataclass
119
+ class SpanLink:
120
+ """A link to another span."""
121
+
122
+ context: SpanContext
123
+ attributes: dict[str, Any] = field(default_factory=dict)
124
+
125
+
126
+ class Span:
127
+ """A span representing a unit of work.
128
+
129
+ Spans can be used as context managers for automatic timing and status.
130
+ """
131
+
132
+ def __init__(
133
+ self,
134
+ name: str,
135
+ context: SpanContext,
136
+ parent_context: SpanContext | None = None,
137
+ kind: SpanKind = SpanKind.INTERNAL,
138
+ attributes: dict[str, Any] | None = None,
139
+ start_time: float | None = None,
140
+ ) -> None:
141
+ self.name = name
142
+ self.context = context
143
+ self.parent_context = parent_context
144
+ self.kind = kind
145
+ self.attributes: dict[str, Any] = attributes or {}
146
+ self.events: list[SpanEvent] = []
147
+ self.links: list[SpanLink] = []
148
+ self.status = SpanStatus.UNSET
149
+ self.status_message: str = ""
150
+ self.start_time = start_time or time.time()
151
+ self.end_time: float | None = None
152
+ self._ended = False
153
+
154
+ @property
155
+ def trace_id(self) -> str:
156
+ return self.context.trace_id
157
+
158
+ @property
159
+ def span_id(self) -> str:
160
+ return self.context.span_id
161
+
162
+ @property
163
+ def duration_seconds(self) -> float | None:
164
+ if self.end_time is None:
165
+ return None
166
+ return self.end_time - self.start_time
167
+
168
+ def set_attribute(self, key: str, value: Any) -> "Span":
169
+ """Set a span attribute."""
170
+ self.attributes[key] = value
171
+ return self
172
+
173
+ def set_attributes(self, attributes: dict[str, Any]) -> "Span":
174
+ """Set multiple span attributes."""
175
+ self.attributes.update(attributes)
176
+ return self
177
+
178
+ def add_event(
179
+ self,
180
+ name: str,
181
+ attributes: dict[str, Any] | None = None,
182
+ timestamp: float | None = None,
183
+ ) -> "Span":
184
+ """Add an event to the span."""
185
+ self.events.append(
186
+ SpanEvent(
187
+ name=name,
188
+ timestamp=timestamp or time.time(),
189
+ attributes=attributes or {},
190
+ )
191
+ )
192
+ return self
193
+
194
+ def add_link(
195
+ self,
196
+ context: SpanContext,
197
+ attributes: dict[str, Any] | None = None,
198
+ ) -> "Span":
199
+ """Add a link to another span."""
200
+ self.links.append(SpanLink(context=context, attributes=attributes or {}))
201
+ return self
202
+
203
+ def set_status(self, status: SpanStatus, message: str = "") -> "Span":
204
+ """Set the span status."""
205
+ self.status = status
206
+ self.status_message = message
207
+ return self
208
+
209
+ def record_exception(
210
+ self,
211
+ exception: Exception,
212
+ attributes: dict[str, Any] | None = None,
213
+ ) -> "Span":
214
+ """Record an exception in the span."""
215
+ exc_attrs = {
216
+ "exception.type": type(exception).__name__,
217
+ "exception.message": str(exception),
218
+ **(attributes or {}),
219
+ }
220
+ self.add_event("exception", exc_attrs)
221
+ self.set_status(SpanStatus.ERROR, str(exception))
222
+ return self
223
+
224
+ def end(self, end_time: float | None = None) -> None:
225
+ """End the span."""
226
+ if not self._ended:
227
+ self.end_time = end_time or time.time()
228
+ self._ended = True
229
+
230
+ def __enter__(self) -> "Span":
231
+ return self
232
+
233
+ def __exit__(
234
+ self,
235
+ exc_type: type[BaseException] | None,
236
+ exc_val: BaseException | None,
237
+ exc_tb: Any,
238
+ ) -> None:
239
+ if exc_val is not None and isinstance(exc_val, Exception):
240
+ self.record_exception(exc_val)
241
+ elif self.status == SpanStatus.UNSET:
242
+ self.set_status(SpanStatus.OK)
243
+ self.end()
244
+
245
+ def to_dict(self) -> dict[str, Any]:
246
+ """Convert span to dictionary for export."""
247
+ return {
248
+ "name": self.name,
249
+ "trace_id": self.trace_id,
250
+ "span_id": self.span_id,
251
+ "parent_span_id": self.parent_context.span_id if self.parent_context else None,
252
+ "kind": self.kind.value,
253
+ "start_time": self.start_time,
254
+ "end_time": self.end_time,
255
+ "duration_seconds": self.duration_seconds,
256
+ "status": self.status.value,
257
+ "status_message": self.status_message,
258
+ "attributes": self.attributes,
259
+ "events": [
260
+ {"name": e.name, "timestamp": e.timestamp, "attributes": e.attributes}
261
+ for e in self.events
262
+ ],
263
+ "links": [
264
+ {
265
+ "trace_id": l.context.trace_id,
266
+ "span_id": l.context.span_id,
267
+ "attributes": l.attributes,
268
+ }
269
+ for l in self.links
270
+ ],
271
+ }
272
+
273
+
274
+ class BaseTracer(ABC):
275
+ """Base class for tracers."""
276
+
277
+ def __init__(self, config: TracingConfig) -> None:
278
+ self.config = config
279
+ self._current_span: threading.local = threading.local()
280
+
281
+ def _should_sample(self, parent_context: SpanContext | None = None) -> bool:
282
+ """Determine if a span should be sampled."""
283
+ sampler = self.config.sampler
284
+
285
+ if sampler == TracingSampler.ALWAYS_ON:
286
+ return True
287
+ elif sampler == TracingSampler.ALWAYS_OFF:
288
+ return False
289
+ elif sampler == TracingSampler.RATIO:
290
+ import random
291
+ return random.random() < self.config.sample_ratio
292
+ elif sampler == TracingSampler.PARENT_BASED:
293
+ if parent_context:
294
+ return parent_context.trace_flags & 1 == 1
295
+ # No parent, use ratio sampling
296
+ import random
297
+ return random.random() < self.config.sample_ratio
298
+
299
+ return True
300
+
301
+ def get_current_span(self) -> Span | None:
302
+ """Get the currently active span."""
303
+ return getattr(self._current_span, "span", None)
304
+
305
+ def _set_current_span(self, span: Span | None) -> None:
306
+ """Set the current span."""
307
+ self._current_span.span = span
308
+
309
+ @abstractmethod
310
+ def start_span(
311
+ self,
312
+ name: str,
313
+ context: ObservabilityContext | None = None,
314
+ kind: str = "internal",
315
+ attributes: dict[str, Any] | None = None,
316
+ ) -> Span:
317
+ """Start a new span."""
318
+ ...
319
+
320
+ @abstractmethod
321
+ @contextmanager
322
+ def trace(
323
+ self,
324
+ name: str,
325
+ context: ObservabilityContext | None = None,
326
+ kind: str = "internal",
327
+ attributes: dict[str, Any] | None = None,
328
+ ) -> Generator[Span, None, None]:
329
+ """Context manager for tracing."""
330
+ ...
331
+
332
+ @abstractmethod
333
+ def inject_context(self, carrier: dict[str, str]) -> None:
334
+ """Inject tracing context into carrier."""
335
+ ...
336
+
337
+ @abstractmethod
338
+ def extract_context(self, carrier: dict[str, str]) -> ObservabilityContext | None:
339
+ """Extract tracing context from carrier."""
340
+ ...
341
+
342
+ @abstractmethod
343
+ def flush(self) -> None:
344
+ """Flush any buffered spans."""
345
+ ...
346
+
347
+ @abstractmethod
348
+ def shutdown(self) -> None:
349
+ """Shutdown the tracer."""
350
+ ...
351
+
352
+
353
+ class NoopTracer(BaseTracer):
354
+ """No-operation tracer for when tracing is disabled."""
355
+
356
+ def __init__(self, config: TracingConfig | None = None) -> None:
357
+ super().__init__(config or TracingConfig(enabled=False))
358
+
359
+ def start_span(
360
+ self,
361
+ name: str,
362
+ context: ObservabilityContext | None = None,
363
+ kind: str = "internal",
364
+ attributes: dict[str, Any] | None = None,
365
+ ) -> Span:
366
+ span_context = SpanContext.generate()
367
+ return Span(name, span_context, kind=SpanKind(kind), attributes=attributes)
368
+
369
+ @contextmanager
370
+ def trace(
371
+ self,
372
+ name: str,
373
+ context: ObservabilityContext | None = None,
374
+ kind: str = "internal",
375
+ attributes: dict[str, Any] | None = None,
376
+ ) -> Generator[Span, None, None]:
377
+ span = self.start_span(name, context, kind, attributes)
378
+ try:
379
+ yield span
380
+ finally:
381
+ span.end()
382
+
383
+ def inject_context(self, carrier: dict[str, str]) -> None:
384
+ pass
385
+
386
+ def extract_context(self, carrier: dict[str, str]) -> ObservabilityContext | None:
387
+ return None
388
+
389
+ def flush(self) -> None:
390
+ pass
391
+
392
+ def shutdown(self) -> None:
393
+ pass
394
+
395
+
396
+ class InMemoryTracer(BaseTracer):
397
+ """In-memory tracer for testing."""
398
+
399
+ def __init__(self, config: TracingConfig | None = None) -> None:
400
+ super().__init__(config or TracingConfig())
401
+ self._spans: list[Span] = []
402
+ self._lock = threading.Lock()
403
+
404
+ def start_span(
405
+ self,
406
+ name: str,
407
+ context: ObservabilityContext | None = None,
408
+ kind: str = "internal",
409
+ attributes: dict[str, Any] | None = None,
410
+ ) -> Span:
411
+ parent = self.get_current_span()
412
+ parent_context = parent.context if parent else None
413
+
414
+ if context and context.trace_id:
415
+ span_context = SpanContext(
416
+ trace_id=context.trace_id,
417
+ span_id=uuid.uuid4().hex[:16],
418
+ )
419
+ elif parent_context:
420
+ span_context = SpanContext.from_parent(parent_context)
421
+ else:
422
+ span_context = SpanContext.generate()
423
+
424
+ if not self._should_sample(parent_context):
425
+ span_context.trace_flags = 0
426
+
427
+ span = Span(
428
+ name=name,
429
+ context=span_context,
430
+ parent_context=parent_context,
431
+ kind=SpanKind(kind),
432
+ attributes=attributes,
433
+ )
434
+
435
+ # Add service info
436
+ span.set_attribute("service.name", self.config.service_name)
437
+
438
+ self._set_current_span(span)
439
+ return span
440
+
441
+ @contextmanager
442
+ def trace(
443
+ self,
444
+ name: str,
445
+ context: ObservabilityContext | None = None,
446
+ kind: str = "internal",
447
+ attributes: dict[str, Any] | None = None,
448
+ ) -> Generator[Span, None, None]:
449
+ span = self.start_span(name, context, kind, attributes)
450
+ previous_span = self.get_current_span()
451
+ self._set_current_span(span)
452
+ try:
453
+ yield span
454
+ except Exception as e:
455
+ span.record_exception(e)
456
+ raise
457
+ finally:
458
+ span.end()
459
+ with self._lock:
460
+ self._spans.append(span)
461
+ self._set_current_span(previous_span)
462
+
463
+ def inject_context(self, carrier: dict[str, str]) -> None:
464
+ span = self.get_current_span()
465
+ if span:
466
+ carrier["traceparent"] = span.context.to_traceparent()
467
+ if span.context.trace_state:
468
+ carrier["tracestate"] = span.context.trace_state
469
+
470
+ def extract_context(self, carrier: dict[str, str]) -> ObservabilityContext | None:
471
+ traceparent = carrier.get("traceparent")
472
+ if traceparent:
473
+ span_context = SpanContext.from_traceparent(traceparent)
474
+ if span_context:
475
+ return ObservabilityContext(
476
+ correlation_id=span_context.trace_id,
477
+ trace_id=span_context.trace_id,
478
+ span_id=span_context.span_id,
479
+ )
480
+ return None
481
+
482
+ def flush(self) -> None:
483
+ pass
484
+
485
+ def shutdown(self) -> None:
486
+ pass
487
+
488
+ @property
489
+ def spans(self) -> list[Span]:
490
+ """Get all recorded spans (for testing)."""
491
+ with self._lock:
492
+ return list(self._spans)
493
+
494
+ def clear(self) -> None:
495
+ """Clear all spans (for testing)."""
496
+ with self._lock:
497
+ self._spans.clear()
498
+
499
+
500
+ class OpenTelemetryTracer(BaseTracer):
501
+ """OpenTelemetry-based tracer with OTLP export.
502
+
503
+ This tracer uses the OpenTelemetry SDK if available, falling back to
504
+ the in-memory tracer if not.
505
+ """
506
+
507
+ def __init__(self, config: TracingConfig | None = None) -> None:
508
+ super().__init__(config or TracingConfig())
509
+ self._otel_tracer: Any = None
510
+ self._provider: Any = None
511
+ self._fallback = InMemoryTracer(config)
512
+ self._initialized = False
513
+
514
+ if self.config.enabled:
515
+ self._try_initialize_otel()
516
+
517
+ def _try_initialize_otel(self) -> None:
518
+ """Try to initialize OpenTelemetry SDK."""
519
+ try:
520
+ from opentelemetry import trace as otel_trace
521
+ from opentelemetry.sdk.trace import TracerProvider
522
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
523
+ from opentelemetry.sdk.resources import Resource, SERVICE_NAME
524
+
525
+ # Create resource
526
+ resource = Resource.create({SERVICE_NAME: self.config.service_name})
527
+
528
+ # Create provider
529
+ self._provider = TracerProvider(resource=resource)
530
+
531
+ # Add exporter based on config
532
+ exporter = self._create_exporter()
533
+ if exporter:
534
+ self._provider.add_span_processor(BatchSpanProcessor(exporter))
535
+
536
+ # Set as global provider
537
+ otel_trace.set_tracer_provider(self._provider)
538
+
539
+ # Get tracer
540
+ self._otel_tracer = otel_trace.get_tracer(
541
+ self.config.service_name,
542
+ schema_url="https://opentelemetry.io/schemas/1.11.0",
543
+ )
544
+
545
+ self._initialized = True
546
+ logger.info("OpenTelemetry tracer initialized")
547
+
548
+ except ImportError:
549
+ logger.warning(
550
+ "OpenTelemetry SDK not installed. Using in-memory tracer. "
551
+ "Install with: pip install opentelemetry-sdk opentelemetry-exporter-otlp"
552
+ )
553
+ except Exception as e:
554
+ logger.error(f"Failed to initialize OpenTelemetry: {e}")
555
+
556
+ def _create_exporter(self) -> Any:
557
+ """Create the appropriate exporter based on config."""
558
+ exporter_type = self.config.exporter.lower()
559
+
560
+ try:
561
+ if exporter_type == "otlp":
562
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
563
+ OTLPSpanExporter,
564
+ )
565
+
566
+ return OTLPSpanExporter(
567
+ endpoint=self.config.endpoint or "http://localhost:4317",
568
+ headers=self.config.headers or None,
569
+ )
570
+
571
+ elif exporter_type == "jaeger":
572
+ from opentelemetry.exporter.jaeger.thrift import JaegerExporter
573
+
574
+ return JaegerExporter(
575
+ agent_host_name=self.config.endpoint or "localhost",
576
+ agent_port=6831,
577
+ )
578
+
579
+ elif exporter_type == "zipkin":
580
+ from opentelemetry.exporter.zipkin.json import ZipkinExporter
581
+
582
+ return ZipkinExporter(
583
+ endpoint=self.config.endpoint or "http://localhost:9411/api/v2/spans",
584
+ )
585
+
586
+ elif exporter_type == "console":
587
+ from opentelemetry.sdk.trace.export import ConsoleSpanExporter
588
+
589
+ return ConsoleSpanExporter()
590
+
591
+ elif exporter_type == "noop":
592
+ return None
593
+
594
+ except ImportError as e:
595
+ logger.warning(f"Exporter {exporter_type} not available: {e}")
596
+ return None
597
+
598
+ return None
599
+
600
+ def start_span(
601
+ self,
602
+ name: str,
603
+ context: ObservabilityContext | None = None,
604
+ kind: str = "internal",
605
+ attributes: dict[str, Any] | None = None,
606
+ ) -> Span:
607
+ if self._initialized and self._otel_tracer:
608
+ return self._start_otel_span(name, context, kind, attributes)
609
+ return self._fallback.start_span(name, context, kind, attributes)
610
+
611
+ def _start_otel_span(
612
+ self,
613
+ name: str,
614
+ context: ObservabilityContext | None = None,
615
+ kind: str = "internal",
616
+ attributes: dict[str, Any] | None = None,
617
+ ) -> Span:
618
+ """Start a span using OpenTelemetry."""
619
+ from opentelemetry import trace as otel_trace
620
+ from opentelemetry.trace import SpanKind as OtelSpanKind
621
+
622
+ kind_map = {
623
+ "internal": OtelSpanKind.INTERNAL,
624
+ "server": OtelSpanKind.SERVER,
625
+ "client": OtelSpanKind.CLIENT,
626
+ "producer": OtelSpanKind.PRODUCER,
627
+ "consumer": OtelSpanKind.CONSUMER,
628
+ }
629
+
630
+ otel_span = self._otel_tracer.start_span(
631
+ name,
632
+ kind=kind_map.get(kind, OtelSpanKind.INTERNAL),
633
+ attributes=attributes,
634
+ )
635
+
636
+ # Wrap in our Span class
637
+ otel_context = otel_span.get_span_context()
638
+ span_context = SpanContext(
639
+ trace_id=format(otel_context.trace_id, "032x"),
640
+ span_id=format(otel_context.span_id, "016x"),
641
+ trace_flags=otel_context.trace_flags,
642
+ )
643
+
644
+ span = Span(
645
+ name=name,
646
+ context=span_context,
647
+ kind=SpanKind(kind),
648
+ attributes=attributes,
649
+ )
650
+ span._otel_span = otel_span # Store reference for proper ending
651
+ return span
652
+
653
+ @contextmanager
654
+ def trace(
655
+ self,
656
+ name: str,
657
+ context: ObservabilityContext | None = None,
658
+ kind: str = "internal",
659
+ attributes: dict[str, Any] | None = None,
660
+ ) -> Generator[Span, None, None]:
661
+ if self._initialized and self._otel_tracer:
662
+ with self._trace_otel(name, context, kind, attributes) as span:
663
+ yield span
664
+ else:
665
+ with self._fallback.trace(name, context, kind, attributes) as span:
666
+ yield span
667
+
668
+ def _trace_otel(
669
+ self,
670
+ name: str,
671
+ context: ObservabilityContext | None = None,
672
+ kind: str = "internal",
673
+ attributes: dict[str, Any] | None = None,
674
+ ) -> Generator[Span, None, None]:
675
+ """Trace using OpenTelemetry."""
676
+ from opentelemetry import trace as otel_trace
677
+ from opentelemetry.trace import SpanKind as OtelSpanKind
678
+
679
+ kind_map = {
680
+ "internal": OtelSpanKind.INTERNAL,
681
+ "server": OtelSpanKind.SERVER,
682
+ "client": OtelSpanKind.CLIENT,
683
+ "producer": OtelSpanKind.PRODUCER,
684
+ "consumer": OtelSpanKind.CONSUMER,
685
+ }
686
+
687
+ with self._otel_tracer.start_as_current_span(
688
+ name,
689
+ kind=kind_map.get(kind, OtelSpanKind.INTERNAL),
690
+ attributes=attributes,
691
+ ) as otel_span:
692
+ otel_context = otel_span.get_span_context()
693
+ span_context = SpanContext(
694
+ trace_id=format(otel_context.trace_id, "032x"),
695
+ span_id=format(otel_context.span_id, "016x"),
696
+ trace_flags=otel_context.trace_flags,
697
+ )
698
+
699
+ span = Span(
700
+ name=name,
701
+ context=span_context,
702
+ kind=SpanKind(kind),
703
+ attributes=attributes,
704
+ )
705
+ span._otel_span = otel_span
706
+
707
+ try:
708
+ yield span
709
+ except Exception as e:
710
+ otel_span.record_exception(e)
711
+ otel_span.set_status(
712
+ otel_trace.Status(otel_trace.StatusCode.ERROR, str(e))
713
+ )
714
+ raise
715
+
716
+ def inject_context(self, carrier: dict[str, str]) -> None:
717
+ if self._initialized:
718
+ try:
719
+ from opentelemetry import propagate
720
+
721
+ propagate.inject(carrier)
722
+ return
723
+ except Exception:
724
+ pass
725
+ self._fallback.inject_context(carrier)
726
+
727
+ def extract_context(self, carrier: dict[str, str]) -> ObservabilityContext | None:
728
+ if self._initialized:
729
+ try:
730
+ from opentelemetry import propagate, trace as otel_trace
731
+
732
+ ctx = propagate.extract(carrier)
733
+ span = otel_trace.get_current_span(ctx)
734
+ if span:
735
+ span_context = span.get_span_context()
736
+ return ObservabilityContext(
737
+ correlation_id=format(span_context.trace_id, "032x"),
738
+ trace_id=format(span_context.trace_id, "032x"),
739
+ span_id=format(span_context.span_id, "016x"),
740
+ )
741
+ except Exception:
742
+ pass
743
+ return self._fallback.extract_context(carrier)
744
+
745
+ def flush(self) -> None:
746
+ if self._provider:
747
+ try:
748
+ self._provider.force_flush()
749
+ except Exception as e:
750
+ logger.error(f"Failed to flush traces: {e}")
751
+
752
+ def shutdown(self) -> None:
753
+ if self._provider:
754
+ try:
755
+ self._provider.shutdown()
756
+ except Exception as e:
757
+ logger.error(f"Failed to shutdown tracer: {e}")
758
+
759
+
760
+ class Tracer:
761
+ """Factory for creating tracers based on configuration."""
762
+
763
+ _instance: BaseTracer | None = None
764
+ _lock = threading.Lock()
765
+
766
+ @classmethod
767
+ def get_tracer(cls, config: TracingConfig | None = None) -> BaseTracer:
768
+ """Get or create a tracer instance."""
769
+ with cls._lock:
770
+ if cls._instance is None:
771
+ config = config or TracingConfig()
772
+ if not config.enabled:
773
+ cls._instance = NoopTracer(config)
774
+ else:
775
+ cls._instance = OpenTelemetryTracer(config)
776
+ return cls._instance
777
+
778
+ @classmethod
779
+ def reset(cls) -> None:
780
+ """Reset the tracer instance (for testing)."""
781
+ with cls._lock:
782
+ if cls._instance:
783
+ cls._instance.shutdown()
784
+ cls._instance = None