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,1133 @@
1
+ """Enterprise configuration management system for Truthound.
2
+
3
+ This module provides environment-aware configuration management with support for:
4
+ - Multiple environments (dev, staging, prod)
5
+ - Multiple configuration sources (files, env vars, Vault, AWS Secrets)
6
+ - Configuration validation
7
+ - Hot reloading
8
+ - Type-safe configuration access
9
+
10
+ Architecture:
11
+ ConfigSource[] (ordered by priority)
12
+ |
13
+ +---> EnvConfigSource (environment variables)
14
+ +---> FileConfigSource (YAML, JSON, TOML)
15
+ +---> VaultConfigSource (HashiCorp Vault)
16
+ +---> AwsSecretsSource (AWS Secrets Manager)
17
+ |
18
+ v
19
+ ConfigManager
20
+ |
21
+ +---> Merge & Validate
22
+ |
23
+ v
24
+ ConfigProfile (typed access)
25
+
26
+ Usage:
27
+ >>> from truthound.infrastructure.config import (
28
+ ... get_config, load_config, Environment,
29
+ ... )
30
+ >>>
31
+ >>> # Load configuration for production
32
+ >>> config = load_config(
33
+ ... environment=Environment.PRODUCTION,
34
+ ... config_path="config/",
35
+ ... use_vault=True,
36
+ ... )
37
+ >>>
38
+ >>> # Access configuration
39
+ >>> db_host = config.get("database.host")
40
+ >>> log_level = config.get("logging.level", default="INFO")
41
+ >>>
42
+ >>> # Type-safe access
43
+ >>> max_workers = config.get_int("workers.max", default=4)
44
+ >>> debug = config.get_bool("debug", default=False)
45
+ """
46
+
47
+ from __future__ import annotations
48
+
49
+ import json
50
+ import os
51
+ import re
52
+ import threading
53
+ import time
54
+ from abc import ABC, abstractmethod
55
+ from dataclasses import dataclass, field
56
+ from datetime import datetime, timezone
57
+ from enum import Enum
58
+ from pathlib import Path
59
+ from typing import Any, Callable, Generic, Iterator, TypeVar, overload
60
+
61
+ try:
62
+ import yaml
63
+ HAS_YAML = True
64
+ except ImportError:
65
+ HAS_YAML = False
66
+
67
+ try:
68
+ import tomllib
69
+ HAS_TOML = True
70
+ except ImportError:
71
+ try:
72
+ import tomli as tomllib # type: ignore
73
+ HAS_TOML = True
74
+ except ImportError:
75
+ HAS_TOML = False
76
+
77
+
78
+ # =============================================================================
79
+ # Environment Types
80
+ # =============================================================================
81
+
82
+
83
+ class Environment(Enum):
84
+ """Application environments."""
85
+
86
+ DEVELOPMENT = "development"
87
+ TESTING = "testing"
88
+ STAGING = "staging"
89
+ PRODUCTION = "production"
90
+ LOCAL = "local"
91
+
92
+ @classmethod
93
+ def from_string(cls, value: str) -> "Environment":
94
+ """Convert string to Environment.
95
+
96
+ Args:
97
+ value: Environment string (case-insensitive).
98
+
99
+ Returns:
100
+ Environment enum value.
101
+ """
102
+ mapping = {
103
+ "dev": cls.DEVELOPMENT,
104
+ "development": cls.DEVELOPMENT,
105
+ "test": cls.TESTING,
106
+ "testing": cls.TESTING,
107
+ "stage": cls.STAGING,
108
+ "staging": cls.STAGING,
109
+ "prod": cls.PRODUCTION,
110
+ "production": cls.PRODUCTION,
111
+ "local": cls.LOCAL,
112
+ }
113
+ return mapping.get(value.lower(), cls.DEVELOPMENT)
114
+
115
+ @classmethod
116
+ def current(cls) -> "Environment":
117
+ """Get current environment from ENV variable.
118
+
119
+ Checks: ENVIRONMENT, ENV, TRUTHOUND_ENV
120
+ """
121
+ for var in ("TRUTHOUND_ENV", "ENVIRONMENT", "ENV"):
122
+ value = os.getenv(var)
123
+ if value:
124
+ return cls.from_string(value)
125
+ return cls.DEVELOPMENT
126
+
127
+ @property
128
+ def is_production(self) -> bool:
129
+ """Check if this is a production environment."""
130
+ return self in (Environment.PRODUCTION, Environment.STAGING)
131
+
132
+ @property
133
+ def is_development(self) -> bool:
134
+ """Check if this is a development environment."""
135
+ return self in (Environment.DEVELOPMENT, Environment.LOCAL, Environment.TESTING)
136
+
137
+
138
+ # =============================================================================
139
+ # Configuration Errors
140
+ # =============================================================================
141
+
142
+
143
+ class ConfigError(Exception):
144
+ """Base configuration error."""
145
+
146
+ pass
147
+
148
+
149
+ class ConfigValidationError(ConfigError):
150
+ """Configuration validation error."""
151
+
152
+ def __init__(self, errors: list[str]) -> None:
153
+ self.errors = errors
154
+ super().__init__(f"Configuration validation failed: {', '.join(errors)}")
155
+
156
+
157
+ class ConfigSourceError(ConfigError):
158
+ """Configuration source error."""
159
+
160
+ pass
161
+
162
+
163
+ # =============================================================================
164
+ # Configuration Sources
165
+ # =============================================================================
166
+
167
+
168
+ class ConfigSource(ABC):
169
+ """Abstract base class for configuration sources.
170
+
171
+ Configuration sources provide key-value pairs from various backends.
172
+ Sources are processed in priority order (highest first).
173
+ """
174
+
175
+ def __init__(self, priority: int = 0) -> None:
176
+ """Initialize config source.
177
+
178
+ Args:
179
+ priority: Source priority (higher = processed later, overrides earlier).
180
+ """
181
+ self._priority = priority
182
+
183
+ @property
184
+ def priority(self) -> int:
185
+ """Get source priority."""
186
+ return self._priority
187
+
188
+ @abstractmethod
189
+ def load(self) -> dict[str, Any]:
190
+ """Load configuration from source.
191
+
192
+ Returns:
193
+ Dictionary of configuration values.
194
+ """
195
+ pass
196
+
197
+ def reload(self) -> dict[str, Any]:
198
+ """Reload configuration (default: same as load)."""
199
+ return self.load()
200
+
201
+ @property
202
+ def supports_reload(self) -> bool:
203
+ """Check if source supports hot reload."""
204
+ return False
205
+
206
+
207
+ class EnvConfigSource(ConfigSource):
208
+ """Environment variable configuration source.
209
+
210
+ Reads configuration from environment variables with prefix.
211
+
212
+ Example:
213
+ TRUTHOUND_DATABASE_HOST=localhost
214
+ TRUTHOUND_DATABASE_PORT=5432
215
+
216
+ Will produce:
217
+ {"database": {"host": "localhost", "port": "5432"}}
218
+ """
219
+
220
+ def __init__(
221
+ self,
222
+ prefix: str = "TRUTHOUND",
223
+ separator: str = "_",
224
+ priority: int = 100,
225
+ ) -> None:
226
+ """Initialize environment source.
227
+
228
+ Args:
229
+ prefix: Environment variable prefix.
230
+ separator: Separator for nested keys.
231
+ priority: Source priority.
232
+ """
233
+ super().__init__(priority)
234
+ self._prefix = prefix
235
+ self._separator = separator
236
+
237
+ def load(self) -> dict[str, Any]:
238
+ """Load configuration from environment."""
239
+ result: dict[str, Any] = {}
240
+ prefix = f"{self._prefix}{self._separator}"
241
+
242
+ for key, value in os.environ.items():
243
+ if key.startswith(prefix):
244
+ # Remove prefix and convert to nested dict
245
+ config_key = key[len(prefix) :].lower()
246
+ parts = config_key.split(self._separator)
247
+
248
+ # Navigate/create nested structure
249
+ current = result
250
+ for part in parts[:-1]:
251
+ if part not in current:
252
+ current[part] = {}
253
+ current = current[part]
254
+
255
+ # Set value (with type inference)
256
+ current[parts[-1]] = self._parse_value(value)
257
+
258
+ return result
259
+
260
+ def _parse_value(self, value: str) -> Any:
261
+ """Parse string value to appropriate type."""
262
+ # Boolean
263
+ if value.lower() in ("true", "yes", "1", "on"):
264
+ return True
265
+ if value.lower() in ("false", "no", "0", "off"):
266
+ return False
267
+
268
+ # None
269
+ if value.lower() in ("null", "none", ""):
270
+ return None
271
+
272
+ # Number
273
+ try:
274
+ if "." in value:
275
+ return float(value)
276
+ return int(value)
277
+ except ValueError:
278
+ pass
279
+
280
+ # JSON array/object
281
+ if value.startswith(("[", "{")):
282
+ try:
283
+ return json.loads(value)
284
+ except json.JSONDecodeError:
285
+ pass
286
+
287
+ return value
288
+
289
+
290
+ class FileConfigSource(ConfigSource):
291
+ """File-based configuration source.
292
+
293
+ Supports YAML, JSON, and TOML formats.
294
+ Automatically detects format from file extension.
295
+ """
296
+
297
+ def __init__(
298
+ self,
299
+ path: str | Path,
300
+ *,
301
+ required: bool = False,
302
+ priority: int = 50,
303
+ watch: bool = False,
304
+ ) -> None:
305
+ """Initialize file source.
306
+
307
+ Args:
308
+ path: Path to configuration file.
309
+ required: Raise error if file not found.
310
+ priority: Source priority.
311
+ watch: Enable file watching for hot reload.
312
+ """
313
+ super().__init__(priority)
314
+ self._path = Path(path)
315
+ self._required = required
316
+ self._watch = watch
317
+ self._last_modified: float = 0
318
+ self._cached: dict[str, Any] = {}
319
+
320
+ def load(self) -> dict[str, Any]:
321
+ """Load configuration from file."""
322
+ if not self._path.exists():
323
+ if self._required:
324
+ raise ConfigSourceError(f"Configuration file not found: {self._path}")
325
+ return {}
326
+
327
+ try:
328
+ content = self._path.read_text(encoding="utf-8")
329
+ self._last_modified = self._path.stat().st_mtime
330
+
331
+ suffix = self._path.suffix.lower()
332
+
333
+ if suffix in (".yaml", ".yml"):
334
+ if not HAS_YAML:
335
+ raise ConfigSourceError("PyYAML not installed")
336
+ self._cached = yaml.safe_load(content) or {}
337
+
338
+ elif suffix == ".json":
339
+ self._cached = json.loads(content)
340
+
341
+ elif suffix == ".toml":
342
+ if not HAS_TOML:
343
+ raise ConfigSourceError("tomllib/tomli not installed")
344
+ self._cached = tomllib.loads(content)
345
+
346
+ else:
347
+ raise ConfigSourceError(f"Unsupported file format: {suffix}")
348
+
349
+ return self._cached
350
+
351
+ except Exception as e:
352
+ if self._required:
353
+ raise ConfigSourceError(f"Failed to load config: {e}")
354
+ return {}
355
+
356
+ def reload(self) -> dict[str, Any]:
357
+ """Reload if file has changed."""
358
+ if self._path.exists():
359
+ mtime = self._path.stat().st_mtime
360
+ if mtime > self._last_modified:
361
+ return self.load()
362
+ return self._cached
363
+
364
+ @property
365
+ def supports_reload(self) -> bool:
366
+ return True
367
+
368
+
369
+ class VaultConfigSource(ConfigSource):
370
+ """HashiCorp Vault configuration source.
371
+
372
+ Reads secrets from Vault KV v2 engine.
373
+ """
374
+
375
+ def __init__(
376
+ self,
377
+ url: str,
378
+ path: str,
379
+ *,
380
+ token: str | None = None,
381
+ role: str | None = None,
382
+ mount_point: str = "secret",
383
+ priority: int = 200,
384
+ ) -> None:
385
+ """Initialize Vault source.
386
+
387
+ Args:
388
+ url: Vault server URL.
389
+ path: Secret path.
390
+ token: Vault token (or use VAULT_TOKEN env var).
391
+ role: AppRole for authentication.
392
+ mount_point: KV mount point.
393
+ priority: Source priority.
394
+ """
395
+ super().__init__(priority)
396
+ self._url = url.rstrip("/")
397
+ self._path = path
398
+ self._token = token or os.getenv("VAULT_TOKEN")
399
+ self._role = role
400
+ self._mount_point = mount_point
401
+ self._cached: dict[str, Any] = {}
402
+
403
+ def load(self) -> dict[str, Any]:
404
+ """Load secrets from Vault."""
405
+ try:
406
+ import urllib.request
407
+ import urllib.error
408
+
409
+ url = f"{self._url}/v1/{self._mount_point}/data/{self._path}"
410
+ headers = {"X-Vault-Token": self._token}
411
+
412
+ request = urllib.request.Request(url, headers=headers)
413
+
414
+ with urllib.request.urlopen(request, timeout=30) as response:
415
+ data = json.loads(response.read().decode("utf-8"))
416
+ self._cached = data.get("data", {}).get("data", {})
417
+ return self._cached
418
+
419
+ except Exception:
420
+ return {}
421
+
422
+ def reload(self) -> dict[str, Any]:
423
+ """Reload from Vault."""
424
+ return self.load()
425
+
426
+ @property
427
+ def supports_reload(self) -> bool:
428
+ return True
429
+
430
+
431
+ class AwsSecretsSource(ConfigSource):
432
+ """AWS Secrets Manager configuration source.
433
+
434
+ Reads secrets from AWS Secrets Manager.
435
+ """
436
+
437
+ def __init__(
438
+ self,
439
+ secret_name: str,
440
+ *,
441
+ region: str | None = None,
442
+ priority: int = 200,
443
+ ) -> None:
444
+ """Initialize AWS Secrets source.
445
+
446
+ Args:
447
+ secret_name: Secret name or ARN.
448
+ region: AWS region.
449
+ priority: Source priority.
450
+ """
451
+ super().__init__(priority)
452
+ self._secret_name = secret_name
453
+ self._region = region or os.getenv("AWS_REGION", "us-east-1")
454
+ self._cached: dict[str, Any] = {}
455
+
456
+ def load(self) -> dict[str, Any]:
457
+ """Load secrets from AWS Secrets Manager."""
458
+ try:
459
+ import boto3
460
+
461
+ client = boto3.client(
462
+ "secretsmanager",
463
+ region_name=self._region,
464
+ )
465
+
466
+ response = client.get_secret_value(SecretId=self._secret_name)
467
+ secret_string = response.get("SecretString", "{}")
468
+ self._cached = json.loads(secret_string)
469
+ return self._cached
470
+
471
+ except Exception:
472
+ return {}
473
+
474
+ def reload(self) -> dict[str, Any]:
475
+ """Reload from AWS."""
476
+ return self.load()
477
+
478
+ @property
479
+ def supports_reload(self) -> bool:
480
+ return True
481
+
482
+
483
+ # =============================================================================
484
+ # Configuration Schema & Validation
485
+ # =============================================================================
486
+
487
+
488
+ @dataclass
489
+ class ConfigField:
490
+ """Configuration field definition for validation."""
491
+
492
+ name: str
493
+ type: type | tuple[type, ...] = str
494
+ required: bool = False
495
+ default: Any = None
496
+ min_value: float | None = None
497
+ max_value: float | None = None
498
+ pattern: str | None = None
499
+ choices: list[Any] | None = None
500
+ description: str = ""
501
+
502
+
503
+ @dataclass
504
+ class ConfigSchema:
505
+ """Configuration schema for validation.
506
+
507
+ Example:
508
+ >>> schema = ConfigSchema(
509
+ ... fields=[
510
+ ... ConfigField("database.host", str, required=True),
511
+ ... ConfigField("database.port", int, default=5432, min_value=1, max_value=65535),
512
+ ... ConfigField("logging.level", str, choices=["DEBUG", "INFO", "WARNING", "ERROR"]),
513
+ ... ]
514
+ ... )
515
+ """
516
+
517
+ fields: list[ConfigField] = field(default_factory=list)
518
+
519
+ def add_field(
520
+ self,
521
+ name: str,
522
+ type: type = str,
523
+ **kwargs: Any,
524
+ ) -> "ConfigSchema":
525
+ """Add a field to the schema."""
526
+ self.fields.append(ConfigField(name=name, type=type, **kwargs))
527
+ return self
528
+
529
+
530
+ class ConfigValidator:
531
+ """Configuration validator.
532
+
533
+ Validates configuration against a schema.
534
+ """
535
+
536
+ def __init__(self, schema: ConfigSchema) -> None:
537
+ """Initialize validator.
538
+
539
+ Args:
540
+ schema: Configuration schema.
541
+ """
542
+ self._schema = schema
543
+
544
+ def validate(self, config: dict[str, Any]) -> list[str]:
545
+ """Validate configuration.
546
+
547
+ Args:
548
+ config: Configuration dictionary.
549
+
550
+ Returns:
551
+ List of validation errors (empty if valid).
552
+ """
553
+ errors: list[str] = []
554
+
555
+ for field_def in self._schema.fields:
556
+ value = self._get_nested(config, field_def.name)
557
+
558
+ # Required check
559
+ if value is None:
560
+ if field_def.required:
561
+ errors.append(f"Required field '{field_def.name}' is missing")
562
+ continue
563
+
564
+ # Type check
565
+ if not isinstance(value, field_def.type):
566
+ errors.append(
567
+ f"Field '{field_def.name}' should be {field_def.type.__name__}, "
568
+ f"got {type(value).__name__}"
569
+ )
570
+ continue
571
+
572
+ # Range check
573
+ if isinstance(value, (int, float)):
574
+ if field_def.min_value is not None and value < field_def.min_value:
575
+ errors.append(
576
+ f"Field '{field_def.name}' must be >= {field_def.min_value}"
577
+ )
578
+ if field_def.max_value is not None and value > field_def.max_value:
579
+ errors.append(
580
+ f"Field '{field_def.name}' must be <= {field_def.max_value}"
581
+ )
582
+
583
+ # Pattern check
584
+ if isinstance(value, str) and field_def.pattern:
585
+ if not re.match(field_def.pattern, value):
586
+ errors.append(
587
+ f"Field '{field_def.name}' must match pattern '{field_def.pattern}'"
588
+ )
589
+
590
+ # Choices check
591
+ if field_def.choices and value not in field_def.choices:
592
+ errors.append(
593
+ f"Field '{field_def.name}' must be one of {field_def.choices}"
594
+ )
595
+
596
+ return errors
597
+
598
+ def _get_nested(self, config: dict[str, Any], key: str) -> Any:
599
+ """Get nested configuration value."""
600
+ parts = key.split(".")
601
+ current = config
602
+ for part in parts:
603
+ if not isinstance(current, dict):
604
+ return None
605
+ current = current.get(part)
606
+ if current is None:
607
+ return None
608
+ return current
609
+
610
+
611
+ # =============================================================================
612
+ # Configuration Profile
613
+ # =============================================================================
614
+
615
+
616
+ class ConfigProfile:
617
+ """Typed configuration access with environment awareness.
618
+
619
+ Provides type-safe access to configuration values with defaults.
620
+
621
+ Example:
622
+ >>> profile = ConfigProfile(config_dict, environment=Environment.PRODUCTION)
623
+ >>> host = profile.get("database.host", default="localhost")
624
+ >>> port = profile.get_int("database.port", default=5432)
625
+ >>> debug = profile.get_bool("debug", default=False)
626
+ """
627
+
628
+ def __init__(
629
+ self,
630
+ config: dict[str, Any],
631
+ *,
632
+ environment: Environment = Environment.DEVELOPMENT,
633
+ ) -> None:
634
+ """Initialize configuration profile.
635
+
636
+ Args:
637
+ config: Configuration dictionary.
638
+ environment: Current environment.
639
+ """
640
+ self._config = config
641
+ self._environment = environment
642
+ self._cache: dict[str, Any] = {}
643
+
644
+ @property
645
+ def environment(self) -> Environment:
646
+ """Get current environment."""
647
+ return self._environment
648
+
649
+ @property
650
+ def is_production(self) -> bool:
651
+ """Check if production environment."""
652
+ return self._environment.is_production
653
+
654
+ @property
655
+ def is_development(self) -> bool:
656
+ """Check if development environment."""
657
+ return self._environment.is_development
658
+
659
+ def get(
660
+ self,
661
+ key: str,
662
+ default: Any = None,
663
+ *,
664
+ required: bool = False,
665
+ ) -> Any:
666
+ """Get configuration value.
667
+
668
+ Args:
669
+ key: Configuration key (dot-separated for nesting).
670
+ default: Default value if not found.
671
+ required: Raise error if not found.
672
+
673
+ Returns:
674
+ Configuration value.
675
+ """
676
+ if key in self._cache:
677
+ return self._cache[key]
678
+
679
+ value = self._get_nested(key)
680
+
681
+ if value is None:
682
+ if required:
683
+ raise ConfigError(f"Required configuration '{key}' not found")
684
+ return default
685
+
686
+ self._cache[key] = value
687
+ return value
688
+
689
+ def get_str(self, key: str, default: str = "") -> str:
690
+ """Get string configuration value."""
691
+ value = self.get(key, default)
692
+ return str(value) if value is not None else default
693
+
694
+ def get_int(self, key: str, default: int = 0) -> int:
695
+ """Get integer configuration value."""
696
+ value = self.get(key, default)
697
+ if isinstance(value, int):
698
+ return value
699
+ try:
700
+ return int(value)
701
+ except (TypeError, ValueError):
702
+ return default
703
+
704
+ def get_float(self, key: str, default: float = 0.0) -> float:
705
+ """Get float configuration value."""
706
+ value = self.get(key, default)
707
+ if isinstance(value, float):
708
+ return value
709
+ try:
710
+ return float(value)
711
+ except (TypeError, ValueError):
712
+ return default
713
+
714
+ def get_bool(self, key: str, default: bool = False) -> bool:
715
+ """Get boolean configuration value."""
716
+ value = self.get(key, default)
717
+ if isinstance(value, bool):
718
+ return value
719
+ if isinstance(value, str):
720
+ return value.lower() in ("true", "yes", "1", "on")
721
+ return bool(value)
722
+
723
+ def get_list(self, key: str, default: list[Any] | None = None) -> list[Any]:
724
+ """Get list configuration value."""
725
+ value = self.get(key, default)
726
+ if isinstance(value, list):
727
+ return value
728
+ if value is None:
729
+ return default or []
730
+ return [value]
731
+
732
+ def get_dict(
733
+ self, key: str, default: dict[str, Any] | None = None
734
+ ) -> dict[str, Any]:
735
+ """Get dictionary configuration value."""
736
+ value = self.get(key, default)
737
+ if isinstance(value, dict):
738
+ return value
739
+ return default or {}
740
+
741
+ def _get_nested(self, key: str) -> Any:
742
+ """Get nested configuration value."""
743
+ parts = key.split(".")
744
+ current = self._config
745
+
746
+ for part in parts:
747
+ if not isinstance(current, dict):
748
+ return None
749
+ current = current.get(part)
750
+ if current is None:
751
+ return None
752
+
753
+ return current
754
+
755
+ def to_dict(self) -> dict[str, Any]:
756
+ """Get full configuration as dictionary."""
757
+ return self._config.copy()
758
+
759
+ def __contains__(self, key: str) -> bool:
760
+ """Check if key exists."""
761
+ return self._get_nested(key) is not None
762
+
763
+ def __getitem__(self, key: str) -> Any:
764
+ """Get configuration value by key."""
765
+ value = self.get(key)
766
+ if value is None:
767
+ raise KeyError(key)
768
+ return value
769
+
770
+
771
+ # =============================================================================
772
+ # Configuration Manager
773
+ # =============================================================================
774
+
775
+
776
+ class ConfigManager:
777
+ """Central configuration manager.
778
+
779
+ Manages multiple configuration sources and provides unified access.
780
+
781
+ Example:
782
+ >>> manager = ConfigManager(environment=Environment.PRODUCTION)
783
+ >>> manager.add_source(FileConfigSource("config/base.yaml"))
784
+ >>> manager.add_source(FileConfigSource("config/production.yaml"))
785
+ >>> manager.add_source(EnvConfigSource())
786
+ >>>
787
+ >>> config = manager.load()
788
+ >>> print(config.get("database.host"))
789
+ """
790
+
791
+ def __init__(
792
+ self,
793
+ environment: Environment | None = None,
794
+ *,
795
+ auto_reload: bool = False,
796
+ reload_interval: float = 60.0,
797
+ ) -> None:
798
+ """Initialize configuration manager.
799
+
800
+ Args:
801
+ environment: Application environment.
802
+ auto_reload: Enable automatic reloading.
803
+ reload_interval: Reload check interval in seconds.
804
+ """
805
+ self._environment = environment or Environment.current()
806
+ self._sources: list[ConfigSource] = []
807
+ self._config: dict[str, Any] = {}
808
+ self._profile: ConfigProfile | None = None
809
+ self._schema: ConfigSchema | None = None
810
+ self._lock = threading.RLock()
811
+ self._auto_reload = auto_reload
812
+ self._reload_interval = reload_interval
813
+ self._reload_thread: threading.Thread | None = None
814
+ self._running = False
815
+ self._callbacks: list[Callable[[ConfigProfile], None]] = []
816
+
817
+ @property
818
+ def environment(self) -> Environment:
819
+ """Get current environment."""
820
+ return self._environment
821
+
822
+ def add_source(self, source: ConfigSource) -> "ConfigManager":
823
+ """Add a configuration source.
824
+
825
+ Args:
826
+ source: Configuration source.
827
+
828
+ Returns:
829
+ Self for chaining.
830
+ """
831
+ with self._lock:
832
+ self._sources.append(source)
833
+ self._sources.sort(key=lambda s: s.priority)
834
+ return self
835
+
836
+ def set_schema(self, schema: ConfigSchema) -> "ConfigManager":
837
+ """Set configuration schema for validation.
838
+
839
+ Args:
840
+ schema: Configuration schema.
841
+
842
+ Returns:
843
+ Self for chaining.
844
+ """
845
+ self._schema = schema
846
+ return self
847
+
848
+ def on_reload(self, callback: Callable[[ConfigProfile], None]) -> "ConfigManager":
849
+ """Register reload callback.
850
+
851
+ Args:
852
+ callback: Function to call on reload.
853
+
854
+ Returns:
855
+ Self for chaining.
856
+ """
857
+ self._callbacks.append(callback)
858
+ return self
859
+
860
+ def load(self, validate: bool = True) -> ConfigProfile:
861
+ """Load configuration from all sources.
862
+
863
+ Args:
864
+ validate: Validate against schema.
865
+
866
+ Returns:
867
+ ConfigProfile instance.
868
+ """
869
+ with self._lock:
870
+ self._config = {}
871
+
872
+ # Load from sources in priority order
873
+ for source in self._sources:
874
+ try:
875
+ source_config = source.load()
876
+ self._merge_config(self._config, source_config)
877
+ except Exception:
878
+ pass # Skip failed sources
879
+
880
+ # Validate
881
+ if validate and self._schema:
882
+ validator = ConfigValidator(self._schema)
883
+ errors = validator.validate(self._config)
884
+ if errors:
885
+ raise ConfigValidationError(errors)
886
+
887
+ self._profile = ConfigProfile(
888
+ self._config,
889
+ environment=self._environment,
890
+ )
891
+
892
+ # Start auto-reload if enabled
893
+ if self._auto_reload and not self._running:
894
+ self._start_reload_thread()
895
+
896
+ return self._profile
897
+
898
+ def reload(self) -> ConfigProfile:
899
+ """Reload configuration from sources that support it.
900
+
901
+ Returns:
902
+ Updated ConfigProfile.
903
+ """
904
+ with self._lock:
905
+ changed = False
906
+
907
+ for source in self._sources:
908
+ if source.supports_reload:
909
+ try:
910
+ old_config = self._config.copy()
911
+ source_config = source.reload()
912
+ self._merge_config(self._config, source_config)
913
+ if self._config != old_config:
914
+ changed = True
915
+ except Exception:
916
+ pass
917
+
918
+ if changed:
919
+ self._profile = ConfigProfile(
920
+ self._config,
921
+ environment=self._environment,
922
+ )
923
+
924
+ # Notify callbacks
925
+ for callback in self._callbacks:
926
+ try:
927
+ callback(self._profile)
928
+ except Exception:
929
+ pass
930
+
931
+ return self._profile or ConfigProfile({})
932
+
933
+ def _merge_config(self, base: dict[str, Any], override: dict[str, Any]) -> None:
934
+ """Deep merge configuration dictionaries."""
935
+ for key, value in override.items():
936
+ if (
937
+ key in base
938
+ and isinstance(base[key], dict)
939
+ and isinstance(value, dict)
940
+ ):
941
+ self._merge_config(base[key], value)
942
+ else:
943
+ base[key] = value
944
+
945
+ def _start_reload_thread(self) -> None:
946
+ """Start background reload thread."""
947
+ self._running = True
948
+ self._reload_thread = threading.Thread(
949
+ target=self._reload_loop,
950
+ daemon=True,
951
+ name="config-reload",
952
+ )
953
+ self._reload_thread.start()
954
+
955
+ def _reload_loop(self) -> None:
956
+ """Background reload loop."""
957
+ while self._running:
958
+ time.sleep(self._reload_interval)
959
+ try:
960
+ self.reload()
961
+ except Exception:
962
+ pass
963
+
964
+ def stop(self) -> None:
965
+ """Stop auto-reload."""
966
+ self._running = False
967
+ if self._reload_thread:
968
+ self._reload_thread.join(timeout=5)
969
+ self._reload_thread = None
970
+
971
+ @property
972
+ def config(self) -> ConfigProfile:
973
+ """Get current configuration profile."""
974
+ if self._profile is None:
975
+ return self.load()
976
+ return self._profile
977
+
978
+
979
+ # =============================================================================
980
+ # Default Schema
981
+ # =============================================================================
982
+
983
+
984
+ def create_default_schema() -> ConfigSchema:
985
+ """Create default Truthound configuration schema."""
986
+ schema = ConfigSchema()
987
+
988
+ # Logging
989
+ schema.add_field("logging.level", str, default="INFO",
990
+ choices=["TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
991
+ schema.add_field("logging.format", str, default="console",
992
+ choices=["console", "json", "logfmt"])
993
+
994
+ # Metrics
995
+ schema.add_field("metrics.enabled", bool, default=True)
996
+ schema.add_field("metrics.port", int, default=9090, min_value=1, max_value=65535)
997
+
998
+ # Database
999
+ schema.add_field("database.host", str, default="localhost")
1000
+ schema.add_field("database.port", int, default=5432, min_value=1, max_value=65535)
1001
+ schema.add_field("database.pool_size", int, default=10, min_value=1, max_value=100)
1002
+
1003
+ # Validation
1004
+ schema.add_field("validation.timeout", int, default=300, min_value=1)
1005
+ schema.add_field("validation.max_workers", int, default=4, min_value=1, max_value=32)
1006
+
1007
+ return schema
1008
+
1009
+
1010
+ # =============================================================================
1011
+ # Global Configuration
1012
+ # =============================================================================
1013
+
1014
+ _global_manager: ConfigManager | None = None
1015
+ _lock = threading.Lock()
1016
+
1017
+
1018
+ def load_config(
1019
+ *,
1020
+ environment: Environment | str | None = None,
1021
+ config_path: str | Path | None = None,
1022
+ env_prefix: str = "TRUTHOUND",
1023
+ use_vault: bool = False,
1024
+ vault_url: str = "",
1025
+ vault_path: str = "",
1026
+ use_aws_secrets: bool = False,
1027
+ aws_secret_name: str = "",
1028
+ auto_reload: bool = False,
1029
+ validate: bool = True,
1030
+ ) -> ConfigProfile:
1031
+ """Load configuration.
1032
+
1033
+ Args:
1034
+ environment: Application environment.
1035
+ config_path: Path to configuration files.
1036
+ env_prefix: Environment variable prefix.
1037
+ use_vault: Enable HashiCorp Vault.
1038
+ vault_url: Vault server URL.
1039
+ vault_path: Vault secret path.
1040
+ use_aws_secrets: Enable AWS Secrets Manager.
1041
+ aws_secret_name: AWS secret name.
1042
+ auto_reload: Enable auto-reload.
1043
+ validate: Validate configuration.
1044
+
1045
+ Returns:
1046
+ ConfigProfile instance.
1047
+ """
1048
+ global _global_manager
1049
+
1050
+ with _lock:
1051
+ if isinstance(environment, str):
1052
+ environment = Environment.from_string(environment)
1053
+ elif environment is None:
1054
+ environment = Environment.current()
1055
+
1056
+ manager = ConfigManager(
1057
+ environment=environment,
1058
+ auto_reload=auto_reload,
1059
+ )
1060
+
1061
+ # Add file sources
1062
+ if config_path:
1063
+ path = Path(config_path)
1064
+
1065
+ # Base config
1066
+ for ext in (".yaml", ".yml", ".json", ".toml"):
1067
+ base_file = path / f"base{ext}"
1068
+ if base_file.exists():
1069
+ manager.add_source(FileConfigSource(base_file, priority=10))
1070
+ break
1071
+
1072
+ # Environment-specific config
1073
+ for ext in (".yaml", ".yml", ".json", ".toml"):
1074
+ env_file = path / f"{environment.value}{ext}"
1075
+ if env_file.exists():
1076
+ manager.add_source(FileConfigSource(env_file, priority=20))
1077
+ break
1078
+
1079
+ # Local overrides
1080
+ for ext in (".yaml", ".yml", ".json", ".toml"):
1081
+ local_file = path / f"local{ext}"
1082
+ if local_file.exists():
1083
+ manager.add_source(FileConfigSource(local_file, priority=30))
1084
+ break
1085
+
1086
+ # Add environment variables
1087
+ manager.add_source(EnvConfigSource(prefix=env_prefix, priority=100))
1088
+
1089
+ # Add Vault
1090
+ if use_vault and vault_url and vault_path:
1091
+ manager.add_source(
1092
+ VaultConfigSource(vault_url, vault_path, priority=200)
1093
+ )
1094
+
1095
+ # Add AWS Secrets
1096
+ if use_aws_secrets and aws_secret_name:
1097
+ manager.add_source(
1098
+ AwsSecretsSource(aws_secret_name, priority=200)
1099
+ )
1100
+
1101
+ # Set default schema
1102
+ manager.set_schema(create_default_schema())
1103
+
1104
+ _global_manager = manager
1105
+ return manager.load(validate=validate)
1106
+
1107
+
1108
+ def get_config() -> ConfigProfile:
1109
+ """Get the global configuration.
1110
+
1111
+ Returns:
1112
+ ConfigProfile instance.
1113
+ """
1114
+ global _global_manager
1115
+
1116
+ with _lock:
1117
+ if _global_manager is None:
1118
+ return load_config()
1119
+ return _global_manager.config
1120
+
1121
+
1122
+ def reload_config() -> ConfigProfile:
1123
+ """Reload the global configuration.
1124
+
1125
+ Returns:
1126
+ Updated ConfigProfile.
1127
+ """
1128
+ global _global_manager
1129
+
1130
+ with _lock:
1131
+ if _global_manager is None:
1132
+ return load_config()
1133
+ return _global_manager.reload()