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,887 @@
1
+ """Structured logging system for Truthound.
2
+
3
+ This module provides a structured logging implementation that supports:
4
+ - Multiple output formats (JSON, logfmt, console)
5
+ - Contextual logging with automatic field propagation
6
+ - Log levels with filtering
7
+ - Multiple handlers with routing
8
+ - Integration with standard logging module
9
+
10
+ Design Principles:
11
+ 1. Structured by default: All logs are key-value pairs
12
+ 2. Context propagation: Fields automatically inherited
13
+ 3. Format agnostic: Same API for all output formats
14
+ 4. Zero-cost when disabled: Minimal overhead when not logging
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import json
20
+ import logging
21
+ import os
22
+ import sys
23
+ import threading
24
+ import traceback
25
+ from abc import ABC, abstractmethod
26
+ from contextlib import contextmanager
27
+ from dataclasses import dataclass, field
28
+ from datetime import datetime, timezone
29
+ from enum import IntEnum
30
+ from pathlib import Path
31
+ from typing import Any, Callable, Iterator, TextIO
32
+
33
+
34
+ # =============================================================================
35
+ # Log Levels
36
+ # =============================================================================
37
+
38
+
39
+ class LogLevel(IntEnum):
40
+ """Log severity levels."""
41
+
42
+ TRACE = 5
43
+ DEBUG = 10
44
+ INFO = 20
45
+ WARNING = 30
46
+ ERROR = 40
47
+ CRITICAL = 50
48
+
49
+ @classmethod
50
+ def from_string(cls, level: str) -> "LogLevel":
51
+ """Convert string to LogLevel."""
52
+ mapping = {
53
+ "trace": cls.TRACE,
54
+ "debug": cls.DEBUG,
55
+ "info": cls.INFO,
56
+ "warning": cls.WARNING,
57
+ "warn": cls.WARNING,
58
+ "error": cls.ERROR,
59
+ "critical": cls.CRITICAL,
60
+ "fatal": cls.CRITICAL,
61
+ }
62
+ return mapping.get(level.lower(), cls.INFO)
63
+
64
+
65
+ # =============================================================================
66
+ # Log Record
67
+ # =============================================================================
68
+
69
+
70
+ @dataclass
71
+ class LogRecord:
72
+ """Immutable log record with structured data.
73
+
74
+ Attributes:
75
+ timestamp: When the log was created (UTC).
76
+ level: Log severity level.
77
+ message: Human-readable log message.
78
+ logger_name: Name of the logger.
79
+ fields: Structured key-value data.
80
+ exception: Exception info if present.
81
+ trace_id: Distributed trace ID.
82
+ span_id: Current span ID.
83
+ caller: Source location (file:line).
84
+ """
85
+
86
+ timestamp: datetime
87
+ level: LogLevel
88
+ message: str
89
+ logger_name: str
90
+ fields: dict[str, Any] = field(default_factory=dict)
91
+ exception: BaseException | None = None
92
+ trace_id: str | None = None
93
+ span_id: str | None = None
94
+ caller: str | None = None
95
+
96
+ def to_dict(self) -> dict[str, Any]:
97
+ """Convert to dictionary."""
98
+ data = {
99
+ "timestamp": self.timestamp.isoformat(),
100
+ "level": self.level.name.lower(),
101
+ "message": self.message,
102
+ "logger": self.logger_name,
103
+ **self.fields,
104
+ }
105
+
106
+ if self.trace_id:
107
+ data["trace_id"] = self.trace_id
108
+ if self.span_id:
109
+ data["span_id"] = self.span_id
110
+ if self.caller:
111
+ data["caller"] = self.caller
112
+ if self.exception:
113
+ data["exception"] = {
114
+ "type": type(self.exception).__name__,
115
+ "message": str(self.exception),
116
+ "traceback": traceback.format_exception(
117
+ type(self.exception),
118
+ self.exception,
119
+ self.exception.__traceback__,
120
+ ),
121
+ }
122
+
123
+ return data
124
+
125
+
126
+ # =============================================================================
127
+ # Log Context
128
+ # =============================================================================
129
+
130
+
131
+ class LogContext:
132
+ """Thread-local context for structured logging.
133
+
134
+ LogContext provides automatic field propagation for structured logs.
135
+ Fields added to the context are automatically included in all logs
136
+ within that context.
137
+
138
+ Example:
139
+ >>> with log_context(request_id="abc123", user_id=42):
140
+ ... logger.info("Processing request") # includes request_id, user_id
141
+ ... with log_context(action="validate"):
142
+ ... logger.info("Validating") # includes all three fields
143
+ """
144
+
145
+ _local = threading.local()
146
+
147
+ @classmethod
148
+ def get_current(cls) -> dict[str, Any]:
149
+ """Get current context fields."""
150
+ if not hasattr(cls._local, "stack"):
151
+ cls._local.stack = [{}]
152
+ # Merge all context levels
153
+ result: dict[str, Any] = {}
154
+ for ctx in cls._local.stack:
155
+ result.update(ctx)
156
+ return result
157
+
158
+ @classmethod
159
+ def push(cls, **fields: Any) -> None:
160
+ """Push new context level."""
161
+ if not hasattr(cls._local, "stack"):
162
+ cls._local.stack = [{}]
163
+ cls._local.stack.append(fields)
164
+
165
+ @classmethod
166
+ def pop(cls) -> dict[str, Any]:
167
+ """Pop context level."""
168
+ if hasattr(cls._local, "stack") and len(cls._local.stack) > 1:
169
+ return cls._local.stack.pop()
170
+ return {}
171
+
172
+ @classmethod
173
+ def clear(cls) -> None:
174
+ """Clear all context."""
175
+ cls._local.stack = [{}]
176
+
177
+
178
+ @contextmanager
179
+ def log_context(**fields: Any) -> Iterator[None]:
180
+ """Context manager for adding fields to log context.
181
+
182
+ Args:
183
+ **fields: Key-value pairs to add to context.
184
+
185
+ Example:
186
+ >>> with log_context(checkpoint="daily", run_id="abc"):
187
+ ... logger.info("Starting validation")
188
+ """
189
+ LogContext.push(**fields)
190
+ try:
191
+ yield
192
+ finally:
193
+ LogContext.pop()
194
+
195
+
196
+ # =============================================================================
197
+ # Log Formatters
198
+ # =============================================================================
199
+
200
+
201
+ class LogFormatter(ABC):
202
+ """Abstract base class for log formatters.
203
+
204
+ Formatters convert LogRecord objects to string output.
205
+ """
206
+
207
+ @abstractmethod
208
+ def format(self, record: LogRecord) -> str:
209
+ """Format a log record.
210
+
211
+ Args:
212
+ record: The log record to format.
213
+
214
+ Returns:
215
+ Formatted string.
216
+ """
217
+ pass
218
+
219
+
220
+ class JSONFormatter(LogFormatter):
221
+ """JSON log formatter.
222
+
223
+ Outputs logs as JSON objects, one per line.
224
+ Ideal for log aggregation systems like ELK, Splunk, etc.
225
+
226
+ Example output:
227
+ {"timestamp":"2024-01-15T10:30:00Z","level":"info","message":"Starting",...}
228
+ """
229
+
230
+ def __init__(
231
+ self,
232
+ *,
233
+ indent: int | None = None,
234
+ sort_keys: bool = False,
235
+ ensure_ascii: bool = False,
236
+ ) -> None:
237
+ """Initialize JSON formatter.
238
+
239
+ Args:
240
+ indent: JSON indentation (None for compact).
241
+ sort_keys: Sort keys alphabetically.
242
+ ensure_ascii: Escape non-ASCII characters.
243
+ """
244
+ self._indent = indent
245
+ self._sort_keys = sort_keys
246
+ self._ensure_ascii = ensure_ascii
247
+
248
+ def format(self, record: LogRecord) -> str:
249
+ """Format record as JSON."""
250
+ data = record.to_dict()
251
+ return json.dumps(
252
+ data,
253
+ indent=self._indent,
254
+ sort_keys=self._sort_keys,
255
+ ensure_ascii=self._ensure_ascii,
256
+ default=str,
257
+ )
258
+
259
+
260
+ class LogfmtFormatter(LogFormatter):
261
+ """Logfmt formatter.
262
+
263
+ Outputs logs in logfmt format (key=value pairs).
264
+ Popular with Prometheus/Grafana ecosystem.
265
+
266
+ Example output:
267
+ ts=2024-01-15T10:30:00Z level=info msg="Starting validation" checkpoint=daily
268
+ """
269
+
270
+ def __init__(self, *, timestamp_key: str = "ts") -> None:
271
+ """Initialize logfmt formatter.
272
+
273
+ Args:
274
+ timestamp_key: Key name for timestamp field.
275
+ """
276
+ self._timestamp_key = timestamp_key
277
+
278
+ def format(self, record: LogRecord) -> str:
279
+ """Format record as logfmt."""
280
+ parts = [
281
+ f'{self._timestamp_key}={record.timestamp.isoformat()}',
282
+ f'level={record.level.name.lower()}',
283
+ f'msg="{self._escape(record.message)}"',
284
+ f'logger={record.logger_name}',
285
+ ]
286
+
287
+ if record.trace_id:
288
+ parts.append(f"trace_id={record.trace_id}")
289
+ if record.span_id:
290
+ parts.append(f"span_id={record.span_id}")
291
+ if record.caller:
292
+ parts.append(f"caller={record.caller}")
293
+
294
+ for key, value in record.fields.items():
295
+ formatted = self._format_value(value)
296
+ parts.append(f"{key}={formatted}")
297
+
298
+ if record.exception:
299
+ parts.append(f'error="{self._escape(str(record.exception))}"')
300
+
301
+ return " ".join(parts)
302
+
303
+ def _escape(self, value: str) -> str:
304
+ """Escape special characters in value."""
305
+ return value.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")
306
+
307
+ def _format_value(self, value: Any) -> str:
308
+ """Format a value for logfmt."""
309
+ if isinstance(value, bool):
310
+ return "true" if value else "false"
311
+ elif isinstance(value, (int, float)):
312
+ return str(value)
313
+ elif isinstance(value, str):
314
+ if " " in value or '"' in value or "=" in value:
315
+ return f'"{self._escape(value)}"'
316
+ return value
317
+ else:
318
+ return f'"{self._escape(str(value))}"'
319
+
320
+
321
+ class ConsoleFormatter(LogFormatter):
322
+ """Human-readable console formatter.
323
+
324
+ Outputs colored, readable logs for development.
325
+
326
+ Example output:
327
+ 2024-01-15 10:30:00 INFO [my.module] Starting validation checkpoint=daily
328
+ """
329
+
330
+ # ANSI color codes
331
+ COLORS = {
332
+ LogLevel.TRACE: "\033[90m", # Gray
333
+ LogLevel.DEBUG: "\033[36m", # Cyan
334
+ LogLevel.INFO: "\033[32m", # Green
335
+ LogLevel.WARNING: "\033[33m", # Yellow
336
+ LogLevel.ERROR: "\033[31m", # Red
337
+ LogLevel.CRITICAL: "\033[35m", # Magenta
338
+ }
339
+ RESET = "\033[0m"
340
+
341
+ def __init__(
342
+ self,
343
+ *,
344
+ color: bool = True,
345
+ show_timestamp: bool = True,
346
+ show_caller: bool = False,
347
+ timestamp_format: str = "%Y-%m-%d %H:%M:%S",
348
+ ) -> None:
349
+ """Initialize console formatter.
350
+
351
+ Args:
352
+ color: Use ANSI colors.
353
+ show_timestamp: Show timestamp.
354
+ show_caller: Show caller location.
355
+ timestamp_format: strftime format for timestamp.
356
+ """
357
+ self._color = color and sys.stdout.isatty()
358
+ self._show_timestamp = show_timestamp
359
+ self._show_caller = show_caller
360
+ self._timestamp_format = timestamp_format
361
+
362
+ def format(self, record: LogRecord) -> str:
363
+ """Format record for console."""
364
+ parts = []
365
+
366
+ if self._show_timestamp:
367
+ ts = record.timestamp.strftime(self._timestamp_format)
368
+ parts.append(ts)
369
+
370
+ level = record.level.name.ljust(5)
371
+ if self._color:
372
+ color = self.COLORS.get(record.level, "")
373
+ level = f"{color}{level}{self.RESET}"
374
+ parts.append(level)
375
+
376
+ parts.append(f"[{record.logger_name}]")
377
+ parts.append(record.message)
378
+
379
+ # Add fields
380
+ if record.fields:
381
+ field_strs = [f"{k}={v}" for k, v in record.fields.items()]
382
+ parts.append(" ".join(field_strs))
383
+
384
+ if self._show_caller and record.caller:
385
+ parts.append(f"({record.caller})")
386
+
387
+ result = " ".join(parts)
388
+
389
+ # Add exception if present
390
+ if record.exception:
391
+ tb = "".join(
392
+ traceback.format_exception(
393
+ type(record.exception),
394
+ record.exception,
395
+ record.exception.__traceback__,
396
+ )
397
+ )
398
+ result = f"{result}\n{tb}"
399
+
400
+ return result
401
+
402
+
403
+ # =============================================================================
404
+ # Log Handlers
405
+ # =============================================================================
406
+
407
+
408
+ class LogHandler(ABC):
409
+ """Abstract base class for log handlers.
410
+
411
+ Handlers are responsible for outputting formatted log records.
412
+ """
413
+
414
+ def __init__(
415
+ self,
416
+ formatter: LogFormatter | None = None,
417
+ level: LogLevel = LogLevel.DEBUG,
418
+ ) -> None:
419
+ """Initialize handler.
420
+
421
+ Args:
422
+ formatter: Log formatter to use.
423
+ level: Minimum log level to handle.
424
+ """
425
+ self._formatter = formatter or ConsoleFormatter()
426
+ self._level = level
427
+ self._lock = threading.Lock()
428
+
429
+ @property
430
+ def formatter(self) -> LogFormatter:
431
+ """Get the formatter."""
432
+ return self._formatter
433
+
434
+ @formatter.setter
435
+ def formatter(self, value: LogFormatter) -> None:
436
+ """Set the formatter."""
437
+ self._formatter = value
438
+
439
+ def should_handle(self, record: LogRecord) -> bool:
440
+ """Check if this handler should process the record."""
441
+ return record.level >= self._level
442
+
443
+ @abstractmethod
444
+ def emit(self, record: LogRecord) -> None:
445
+ """Emit a log record.
446
+
447
+ Args:
448
+ record: The log record to emit.
449
+ """
450
+ pass
451
+
452
+ def handle(self, record: LogRecord) -> None:
453
+ """Handle a log record (thread-safe).
454
+
455
+ Args:
456
+ record: The log record to handle.
457
+ """
458
+ if self.should_handle(record):
459
+ with self._lock:
460
+ self.emit(record)
461
+
462
+ def close(self) -> None:
463
+ """Clean up handler resources."""
464
+ pass
465
+
466
+
467
+ class ConsoleHandler(LogHandler):
468
+ """Handler that writes to console (stdout/stderr).
469
+
470
+ Writes INFO and below to stdout, WARNING and above to stderr.
471
+ """
472
+
473
+ def __init__(
474
+ self,
475
+ *,
476
+ stream: TextIO | None = None,
477
+ split_stderr: bool = True,
478
+ **kwargs: Any,
479
+ ) -> None:
480
+ """Initialize console handler.
481
+
482
+ Args:
483
+ stream: Output stream (None for auto stdout/stderr).
484
+ split_stderr: Send WARNING+ to stderr.
485
+ **kwargs: Arguments passed to LogHandler.
486
+ """
487
+ super().__init__(**kwargs)
488
+ self._stream = stream
489
+ self._split_stderr = split_stderr
490
+
491
+ def emit(self, record: LogRecord) -> None:
492
+ """Write log to console."""
493
+ message = self._formatter.format(record)
494
+
495
+ if self._stream:
496
+ stream = self._stream
497
+ elif self._split_stderr and record.level >= LogLevel.WARNING:
498
+ stream = sys.stderr
499
+ else:
500
+ stream = sys.stdout
501
+
502
+ try:
503
+ stream.write(message + "\n")
504
+ stream.flush()
505
+ except Exception:
506
+ pass # Don't fail on logging errors
507
+
508
+
509
+ class FileHandler(LogHandler):
510
+ """Handler that writes to a file."""
511
+
512
+ def __init__(
513
+ self,
514
+ path: str | Path,
515
+ *,
516
+ mode: str = "a",
517
+ encoding: str = "utf-8",
518
+ **kwargs: Any,
519
+ ) -> None:
520
+ """Initialize file handler.
521
+
522
+ Args:
523
+ path: Path to log file.
524
+ mode: File open mode.
525
+ encoding: File encoding.
526
+ **kwargs: Arguments passed to LogHandler.
527
+ """
528
+ super().__init__(**kwargs)
529
+ self._path = Path(path)
530
+ self._mode = mode
531
+ self._encoding = encoding
532
+ self._file: TextIO | None = None
533
+
534
+ def _ensure_file(self) -> TextIO:
535
+ """Ensure file is open."""
536
+ if self._file is None:
537
+ self._path.parent.mkdir(parents=True, exist_ok=True)
538
+ self._file = open(
539
+ self._path, self._mode, encoding=self._encoding
540
+ )
541
+ return self._file
542
+
543
+ def emit(self, record: LogRecord) -> None:
544
+ """Write log to file."""
545
+ message = self._formatter.format(record)
546
+ try:
547
+ f = self._ensure_file()
548
+ f.write(message + "\n")
549
+ f.flush()
550
+ except Exception:
551
+ pass
552
+
553
+ def close(self) -> None:
554
+ """Close the file."""
555
+ if self._file:
556
+ try:
557
+ self._file.close()
558
+ except Exception:
559
+ pass
560
+ self._file = None
561
+
562
+
563
+ class RotatingFileHandler(FileHandler):
564
+ """Handler that rotates log files based on size.
565
+
566
+ Creates backup files (log.1, log.2, etc.) when max size reached.
567
+ """
568
+
569
+ def __init__(
570
+ self,
571
+ path: str | Path,
572
+ *,
573
+ max_bytes: int = 10 * 1024 * 1024, # 10MB
574
+ backup_count: int = 5,
575
+ **kwargs: Any,
576
+ ) -> None:
577
+ """Initialize rotating file handler.
578
+
579
+ Args:
580
+ path: Path to log file.
581
+ max_bytes: Maximum file size before rotation.
582
+ backup_count: Number of backup files to keep.
583
+ **kwargs: Arguments passed to FileHandler.
584
+ """
585
+ super().__init__(path, **kwargs)
586
+ self._max_bytes = max_bytes
587
+ self._backup_count = backup_count
588
+
589
+ def _should_rotate(self) -> bool:
590
+ """Check if rotation is needed."""
591
+ try:
592
+ return self._path.stat().st_size >= self._max_bytes
593
+ except FileNotFoundError:
594
+ return False
595
+
596
+ def _rotate(self) -> None:
597
+ """Rotate log files."""
598
+ if self._file:
599
+ self._file.close()
600
+ self._file = None
601
+
602
+ # Rotate existing backups
603
+ for i in range(self._backup_count - 1, 0, -1):
604
+ src = self._path.with_suffix(f"{self._path.suffix}.{i}")
605
+ dst = self._path.with_suffix(f"{self._path.suffix}.{i + 1}")
606
+ if src.exists():
607
+ src.rename(dst)
608
+
609
+ # Move current to .1
610
+ if self._path.exists():
611
+ self._path.rename(
612
+ self._path.with_suffix(f"{self._path.suffix}.1")
613
+ )
614
+
615
+ def emit(self, record: LogRecord) -> None:
616
+ """Write log with rotation check."""
617
+ if self._should_rotate():
618
+ self._rotate()
619
+ super().emit(record)
620
+
621
+
622
+ # =============================================================================
623
+ # Structured Logger
624
+ # =============================================================================
625
+
626
+
627
+ class StructuredLogger:
628
+ """Structured logger with context and multiple handlers.
629
+
630
+ StructuredLogger provides structured logging with automatic field
631
+ propagation, multiple output handlers, and format flexibility.
632
+
633
+ Example:
634
+ >>> logger = StructuredLogger("my.module")
635
+ >>> logger.add_handler(ConsoleHandler(formatter=JSONFormatter()))
636
+ >>>
637
+ >>> logger.info("User logged in", user_id=123, ip="192.168.1.1")
638
+ >>> # Output: {"timestamp":"...","level":"info","message":"User logged in","user_id":123,"ip":"192.168.1.1"}
639
+ """
640
+
641
+ def __init__(
642
+ self,
643
+ name: str,
644
+ *,
645
+ level: LogLevel = LogLevel.INFO,
646
+ handlers: list[LogHandler] | None = None,
647
+ include_caller: bool = False,
648
+ ) -> None:
649
+ """Initialize structured logger.
650
+
651
+ Args:
652
+ name: Logger name (usually module name).
653
+ level: Minimum log level.
654
+ handlers: Initial handlers.
655
+ include_caller: Include caller location in logs.
656
+ """
657
+ self._name = name
658
+ self._level = level
659
+ self._handlers: list[LogHandler] = handlers or []
660
+ self._include_caller = include_caller
661
+ self._bound_fields: dict[str, Any] = {}
662
+
663
+ @property
664
+ def name(self) -> str:
665
+ """Get logger name."""
666
+ return self._name
667
+
668
+ @property
669
+ def level(self) -> LogLevel:
670
+ """Get log level."""
671
+ return self._level
672
+
673
+ @level.setter
674
+ def level(self, value: LogLevel) -> None:
675
+ """Set log level."""
676
+ self._level = value
677
+
678
+ def add_handler(self, handler: LogHandler) -> None:
679
+ """Add a log handler."""
680
+ self._handlers.append(handler)
681
+
682
+ def remove_handler(self, handler: LogHandler) -> None:
683
+ """Remove a log handler."""
684
+ self._handlers.remove(handler)
685
+
686
+ def bind(self, **fields: Any) -> "StructuredLogger":
687
+ """Create a child logger with bound fields.
688
+
689
+ Args:
690
+ **fields: Fields to bind.
691
+
692
+ Returns:
693
+ New logger with bound fields.
694
+ """
695
+ new_logger = StructuredLogger(
696
+ self._name,
697
+ level=self._level,
698
+ handlers=self._handlers,
699
+ include_caller=self._include_caller,
700
+ )
701
+ new_logger._bound_fields = {**self._bound_fields, **fields}
702
+ return new_logger
703
+
704
+ def _get_caller(self) -> str | None:
705
+ """Get caller location."""
706
+ if not self._include_caller:
707
+ return None
708
+
709
+ import inspect
710
+ frame = inspect.currentframe()
711
+ if frame:
712
+ # Go up the stack to find the actual caller
713
+ for _ in range(4): # Skip internal frames
714
+ if frame.f_back:
715
+ frame = frame.f_back
716
+ return f"{frame.f_code.co_filename}:{frame.f_lineno}"
717
+ return None
718
+
719
+ def _log(
720
+ self,
721
+ level: LogLevel,
722
+ message: str,
723
+ exception: BaseException | None = None,
724
+ **fields: Any,
725
+ ) -> None:
726
+ """Internal log method."""
727
+ if level < self._level:
728
+ return
729
+
730
+ # Get context from LogContext
731
+ from truthound.observability.context import current_context
732
+
733
+ ctx = current_context()
734
+
735
+ # Merge fields: bound -> context -> call-time
736
+ merged_fields = {
737
+ **self._bound_fields,
738
+ **LogContext.get_current(),
739
+ **fields,
740
+ }
741
+
742
+ record = LogRecord(
743
+ timestamp=datetime.now(timezone.utc),
744
+ level=level,
745
+ message=message,
746
+ logger_name=self._name,
747
+ fields=merged_fields,
748
+ exception=exception,
749
+ trace_id=ctx.trace_id if ctx else None,
750
+ span_id=ctx.span_id if ctx else None,
751
+ caller=self._get_caller(),
752
+ )
753
+
754
+ for handler in self._handlers:
755
+ try:
756
+ handler.handle(record)
757
+ except Exception:
758
+ pass # Don't fail on handler errors
759
+
760
+ def trace(self, message: str, **fields: Any) -> None:
761
+ """Log at TRACE level."""
762
+ self._log(LogLevel.TRACE, message, **fields)
763
+
764
+ def debug(self, message: str, **fields: Any) -> None:
765
+ """Log at DEBUG level."""
766
+ self._log(LogLevel.DEBUG, message, **fields)
767
+
768
+ def info(self, message: str, **fields: Any) -> None:
769
+ """Log at INFO level."""
770
+ self._log(LogLevel.INFO, message, **fields)
771
+
772
+ def warning(self, message: str, **fields: Any) -> None:
773
+ """Log at WARNING level."""
774
+ self._log(LogLevel.WARNING, message, **fields)
775
+
776
+ def warn(self, message: str, **fields: Any) -> None:
777
+ """Alias for warning."""
778
+ self.warning(message, **fields)
779
+
780
+ def error(self, message: str, **fields: Any) -> None:
781
+ """Log at ERROR level."""
782
+ self._log(LogLevel.ERROR, message, **fields)
783
+
784
+ def critical(self, message: str, **fields: Any) -> None:
785
+ """Log at CRITICAL level."""
786
+ self._log(LogLevel.CRITICAL, message, **fields)
787
+
788
+ def fatal(self, message: str, **fields: Any) -> None:
789
+ """Alias for critical."""
790
+ self.critical(message, **fields)
791
+
792
+ def exception(
793
+ self,
794
+ message: str,
795
+ exc: BaseException | None = None,
796
+ **fields: Any,
797
+ ) -> None:
798
+ """Log exception with traceback."""
799
+ if exc is None:
800
+ exc = sys.exc_info()[1]
801
+ self._log(LogLevel.ERROR, message, exception=exc, **fields)
802
+
803
+
804
+ # =============================================================================
805
+ # Global Logger Management
806
+ # =============================================================================
807
+
808
+ _loggers: dict[str, StructuredLogger] = {}
809
+ _default_handlers: list[LogHandler] = []
810
+ _default_level: LogLevel = LogLevel.INFO
811
+ _lock = threading.Lock()
812
+
813
+
814
+ def configure_logging(
815
+ *,
816
+ level: LogLevel | str = LogLevel.INFO,
817
+ format: str = "console",
818
+ handlers: list[LogHandler] | None = None,
819
+ json_output: bool = False,
820
+ ) -> None:
821
+ """Configure global logging settings.
822
+
823
+ Args:
824
+ level: Default log level.
825
+ format: Output format ("console", "json", "logfmt").
826
+ handlers: Custom handlers (overrides format).
827
+ json_output: Shortcut for JSON format.
828
+ """
829
+ global _default_handlers, _default_level
830
+
831
+ if isinstance(level, str):
832
+ level = LogLevel.from_string(level)
833
+ _default_level = level
834
+
835
+ if handlers:
836
+ _default_handlers = handlers
837
+ else:
838
+ # Create default handler based on format
839
+ if json_output or format == "json":
840
+ formatter = JSONFormatter()
841
+ elif format == "logfmt":
842
+ formatter = LogfmtFormatter()
843
+ else:
844
+ formatter = ConsoleFormatter()
845
+
846
+ _default_handlers = [ConsoleHandler(formatter=formatter, level=level)]
847
+
848
+
849
+ def get_logger(name: str) -> StructuredLogger:
850
+ """Get or create a logger.
851
+
852
+ Args:
853
+ name: Logger name (usually __name__).
854
+
855
+ Returns:
856
+ StructuredLogger instance.
857
+ """
858
+ global _loggers
859
+
860
+ with _lock:
861
+ if name not in _loggers:
862
+ logger = StructuredLogger(
863
+ name,
864
+ level=_default_level,
865
+ handlers=_default_handlers.copy() if _default_handlers else [
866
+ ConsoleHandler(formatter=ConsoleFormatter())
867
+ ],
868
+ )
869
+ _loggers[name] = logger
870
+
871
+ return _loggers[name]
872
+
873
+
874
+ def set_default_logger(logger: StructuredLogger) -> None:
875
+ """Set the default logger for a name.
876
+
877
+ Args:
878
+ logger: Logger to register.
879
+ """
880
+ global _loggers
881
+
882
+ with _lock:
883
+ _loggers[logger.name] = logger
884
+
885
+
886
+ # Initialize default configuration
887
+ configure_logging()