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,911 @@
1
+ """Pattern-based cache invalidation system.
2
+
3
+ This module provides advanced cache invalidation capabilities using
4
+ glob-style pattern matching, supporting flexible cache management.
5
+
6
+ Key Features:
7
+ - Glob pattern matching (*, **, ?)
8
+ - Regex pattern support
9
+ - Tag-based invalidation
10
+ - Dependency tracking
11
+ - Cascading invalidation
12
+ - Batch operations
13
+
14
+ Example:
15
+ from truthound.profiler.cache_patterns import (
16
+ PatternInvalidator,
17
+ InvalidationPattern,
18
+ PatternType,
19
+ )
20
+
21
+ # Create invalidator
22
+ invalidator = PatternInvalidator(cache_backend)
23
+
24
+ # Invalidate by glob pattern
25
+ count = invalidator.invalidate("profile:user:*")
26
+
27
+ # Invalidate by regex
28
+ count = invalidator.invalidate(r"profile:user:\d+", pattern_type=PatternType.REGEX)
29
+
30
+ # Invalidate by tags
31
+ count = invalidator.invalidate_by_tags(["user", "session"])
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import fnmatch
37
+ import re
38
+ import threading
39
+ import time
40
+ from abc import ABC, abstractmethod
41
+ from dataclasses import dataclass, field
42
+ from datetime import datetime, timedelta
43
+ from enum import Enum
44
+ from typing import Any, Callable, Iterator, Protocol
45
+
46
+
47
+ # =============================================================================
48
+ # Types and Enums
49
+ # =============================================================================
50
+
51
+
52
+ class PatternType(str, Enum):
53
+ """Types of invalidation patterns."""
54
+
55
+ GLOB = "glob" # fnmatch-style: *, **, ?
56
+ REGEX = "regex" # Regular expressions
57
+ PREFIX = "prefix" # Simple prefix matching
58
+ EXACT = "exact" # Exact key match
59
+ TAG = "tag" # Tag-based matching
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class InvalidationPattern:
64
+ """Represents a cache invalidation pattern.
65
+
66
+ Attributes:
67
+ pattern: The pattern string
68
+ pattern_type: Type of pattern matching
69
+ compiled: Pre-compiled pattern (for regex)
70
+ """
71
+
72
+ pattern: str
73
+ pattern_type: PatternType = PatternType.GLOB
74
+ compiled: re.Pattern | None = field(default=None, compare=False)
75
+
76
+ def __post_init__(self) -> None:
77
+ """Compile regex patterns."""
78
+ if self.pattern_type == PatternType.REGEX and self.compiled is None:
79
+ try:
80
+ object.__setattr__(self, "compiled", re.compile(self.pattern))
81
+ except re.error as e:
82
+ raise ValueError(f"Invalid regex pattern: {self.pattern}") from e
83
+
84
+ def matches(self, key: str) -> bool:
85
+ """Check if key matches this pattern.
86
+
87
+ Args:
88
+ key: Cache key to check
89
+
90
+ Returns:
91
+ True if key matches the pattern
92
+ """
93
+ if self.pattern_type == PatternType.EXACT:
94
+ return key == self.pattern
95
+
96
+ elif self.pattern_type == PatternType.PREFIX:
97
+ return key.startswith(self.pattern)
98
+
99
+ elif self.pattern_type == PatternType.GLOB:
100
+ return self._glob_match(key)
101
+
102
+ elif self.pattern_type == PatternType.REGEX:
103
+ if self.compiled:
104
+ return bool(self.compiled.match(key))
105
+ return bool(re.match(self.pattern, key))
106
+
107
+ return False
108
+
109
+ def _glob_match(self, key: str) -> bool:
110
+ """Match using glob-style patterns.
111
+
112
+ Supports:
113
+ - * : matches any characters except :
114
+ - ** : matches any characters including :
115
+ - ? : matches single character
116
+ """
117
+ # Convert glob pattern to regex
118
+ regex = self._glob_to_regex(self.pattern)
119
+ return bool(re.match(regex, key))
120
+
121
+ def _glob_to_regex(self, pattern: str) -> str:
122
+ """Convert glob pattern to regex.
123
+
124
+ Args:
125
+ pattern: Glob pattern
126
+
127
+ Returns:
128
+ Equivalent regex pattern
129
+ """
130
+ # Escape special regex chars except * and ?
131
+ regex = ""
132
+ i = 0
133
+
134
+ while i < len(pattern):
135
+ c = pattern[i]
136
+
137
+ if c == "*":
138
+ # Check for **
139
+ if i + 1 < len(pattern) and pattern[i + 1] == "*":
140
+ regex += ".*" # Match everything
141
+ i += 2
142
+ continue
143
+ else:
144
+ regex += "[^:]*" # Match except separator
145
+
146
+ elif c == "?":
147
+ regex += "." # Match single char
148
+
149
+ elif c in ".^$+{}[]|()":
150
+ regex += "\\" + c # Escape
151
+
152
+ else:
153
+ regex += c
154
+
155
+ i += 1
156
+
157
+ return f"^{regex}$"
158
+
159
+
160
+ @dataclass
161
+ class InvalidationResult:
162
+ """Result of cache invalidation operation.
163
+
164
+ Attributes:
165
+ pattern: Pattern used for invalidation
166
+ keys_matched: Number of keys that matched
167
+ keys_invalidated: Number of keys successfully invalidated
168
+ duration_ms: Operation duration in milliseconds
169
+ errors: List of error messages
170
+ """
171
+
172
+ pattern: str
173
+ keys_matched: int = 0
174
+ keys_invalidated: int = 0
175
+ duration_ms: float = 0.0
176
+ errors: list[str] = field(default_factory=list)
177
+
178
+ @property
179
+ def success(self) -> bool:
180
+ """Check if invalidation was successful."""
181
+ return len(self.errors) == 0 and self.keys_matched == self.keys_invalidated
182
+
183
+ def to_dict(self) -> dict[str, Any]:
184
+ """Convert to dictionary."""
185
+ return {
186
+ "pattern": self.pattern,
187
+ "keys_matched": self.keys_matched,
188
+ "keys_invalidated": self.keys_invalidated,
189
+ "duration_ms": self.duration_ms,
190
+ "success": self.success,
191
+ "errors": self.errors,
192
+ }
193
+
194
+
195
+ # =============================================================================
196
+ # Cache Key Protocol
197
+ # =============================================================================
198
+
199
+
200
+ class CacheKeyProvider(Protocol):
201
+ """Protocol for cache backends that can list keys."""
202
+
203
+ def keys(self, pattern: str = "*") -> Iterator[str]:
204
+ """Iterate over cache keys matching pattern."""
205
+ ...
206
+
207
+ def delete(self, key: str) -> bool:
208
+ """Delete a single key."""
209
+ ...
210
+
211
+ def delete_many(self, keys: list[str]) -> int:
212
+ """Delete multiple keys."""
213
+ ...
214
+
215
+
216
+ # =============================================================================
217
+ # Tag Registry
218
+ # =============================================================================
219
+
220
+
221
+ @dataclass
222
+ class TagEntry:
223
+ """Entry in tag registry.
224
+
225
+ Attributes:
226
+ key: Cache key
227
+ tags: Set of tags
228
+ created_at: When entry was created
229
+ """
230
+
231
+ key: str
232
+ tags: set[str]
233
+ created_at: datetime = field(default_factory=datetime.now)
234
+
235
+
236
+ class TagRegistry:
237
+ """Registry for cache key tags.
238
+
239
+ Tracks which cache keys are associated with which tags,
240
+ enabling tag-based invalidation.
241
+
242
+ Example:
243
+ registry = TagRegistry()
244
+
245
+ # Register tags for a key
246
+ registry.register("user:123:profile", {"user", "profile"})
247
+
248
+ # Find keys by tag
249
+ keys = registry.find_by_tag("user")
250
+
251
+ # Find by multiple tags (intersection)
252
+ keys = registry.find_by_tags(["user", "active"])
253
+ """
254
+
255
+ def __init__(self, max_entries: int = 100000):
256
+ """Initialize registry.
257
+
258
+ Args:
259
+ max_entries: Maximum number of entries to track
260
+ """
261
+ self.max_entries = max_entries
262
+ self._entries: dict[str, TagEntry] = {}
263
+ self._tag_index: dict[str, set[str]] = {} # tag -> set of keys
264
+ self._lock = threading.RLock()
265
+
266
+ def register(self, key: str, tags: set[str] | list[str]) -> None:
267
+ """Register tags for a cache key.
268
+
269
+ Args:
270
+ key: Cache key
271
+ tags: Tags to associate with key
272
+ """
273
+ tags = set(tags)
274
+
275
+ with self._lock:
276
+ # Update or create entry
277
+ if key in self._entries:
278
+ old_tags = self._entries[key].tags
279
+ self._entries[key].tags = tags
280
+
281
+ # Update tag index for removed tags
282
+ for tag in old_tags - tags:
283
+ if tag in self._tag_index:
284
+ self._tag_index[tag].discard(key)
285
+ else:
286
+ # Check capacity
287
+ if len(self._entries) >= self.max_entries:
288
+ self._evict_oldest()
289
+
290
+ self._entries[key] = TagEntry(key=key, tags=tags)
291
+
292
+ # Update tag index for added tags
293
+ for tag in tags:
294
+ if tag not in self._tag_index:
295
+ self._tag_index[tag] = set()
296
+ self._tag_index[tag].add(key)
297
+
298
+ def unregister(self, key: str) -> set[str]:
299
+ """Unregister a cache key.
300
+
301
+ Args:
302
+ key: Cache key to unregister
303
+
304
+ Returns:
305
+ Tags that were associated with the key
306
+ """
307
+ with self._lock:
308
+ if key not in self._entries:
309
+ return set()
310
+
311
+ entry = self._entries.pop(key)
312
+
313
+ # Update tag index
314
+ for tag in entry.tags:
315
+ if tag in self._tag_index:
316
+ self._tag_index[tag].discard(key)
317
+ if not self._tag_index[tag]:
318
+ del self._tag_index[tag]
319
+
320
+ return entry.tags
321
+
322
+ def get_tags(self, key: str) -> set[str]:
323
+ """Get tags for a cache key.
324
+
325
+ Args:
326
+ key: Cache key
327
+
328
+ Returns:
329
+ Set of tags
330
+ """
331
+ with self._lock:
332
+ if key in self._entries:
333
+ return self._entries[key].tags.copy()
334
+ return set()
335
+
336
+ def find_by_tag(self, tag: str) -> set[str]:
337
+ """Find all keys with a specific tag.
338
+
339
+ Args:
340
+ tag: Tag to search for
341
+
342
+ Returns:
343
+ Set of cache keys
344
+ """
345
+ with self._lock:
346
+ return self._tag_index.get(tag, set()).copy()
347
+
348
+ def find_by_tags(
349
+ self,
350
+ tags: list[str],
351
+ match_all: bool = True,
352
+ ) -> set[str]:
353
+ """Find keys matching multiple tags.
354
+
355
+ Args:
356
+ tags: Tags to search for
357
+ match_all: If True, keys must have all tags (AND)
358
+ If False, keys must have any tag (OR)
359
+
360
+ Returns:
361
+ Set of cache keys
362
+ """
363
+ if not tags:
364
+ return set()
365
+
366
+ with self._lock:
367
+ tag_sets = [
368
+ self._tag_index.get(tag, set())
369
+ for tag in tags
370
+ ]
371
+
372
+ if match_all:
373
+ # Intersection (AND)
374
+ result = tag_sets[0].copy()
375
+ for s in tag_sets[1:]:
376
+ result &= s
377
+ else:
378
+ # Union (OR)
379
+ result = set()
380
+ for s in tag_sets:
381
+ result |= s
382
+
383
+ return result
384
+
385
+ def list_tags(self) -> list[str]:
386
+ """List all known tags.
387
+
388
+ Returns:
389
+ List of tag names
390
+ """
391
+ with self._lock:
392
+ return list(self._tag_index.keys())
393
+
394
+ def tag_counts(self) -> dict[str, int]:
395
+ """Get count of keys for each tag.
396
+
397
+ Returns:
398
+ Dictionary mapping tags to key counts
399
+ """
400
+ with self._lock:
401
+ return {
402
+ tag: len(keys)
403
+ for tag, keys in self._tag_index.items()
404
+ }
405
+
406
+ def clear(self) -> int:
407
+ """Clear all entries.
408
+
409
+ Returns:
410
+ Number of entries cleared
411
+ """
412
+ with self._lock:
413
+ count = len(self._entries)
414
+ self._entries.clear()
415
+ self._tag_index.clear()
416
+ return count
417
+
418
+ def _evict_oldest(self) -> None:
419
+ """Evict oldest entries when at capacity."""
420
+ if not self._entries:
421
+ return
422
+
423
+ # Find oldest entry
424
+ oldest_key = min(
425
+ self._entries.keys(),
426
+ key=lambda k: self._entries[k].created_at,
427
+ )
428
+ self.unregister(oldest_key)
429
+
430
+
431
+ # =============================================================================
432
+ # Dependency Tracker
433
+ # =============================================================================
434
+
435
+
436
+ class DependencyTracker:
437
+ """Tracks cache key dependencies for cascading invalidation.
438
+
439
+ When a key is invalidated, all keys that depend on it
440
+ are also invalidated.
441
+
442
+ Example:
443
+ tracker = DependencyTracker()
444
+
445
+ # Key B depends on key A
446
+ tracker.add_dependency("keyB", "keyA")
447
+
448
+ # When A is invalidated, B should also be invalidated
449
+ deps = tracker.get_dependents("keyA") # Returns {"keyB"}
450
+ """
451
+
452
+ def __init__(self):
453
+ """Initialize tracker."""
454
+ self._dependencies: dict[str, set[str]] = {} # key -> keys it depends on
455
+ self._dependents: dict[str, set[str]] = {} # key -> keys that depend on it
456
+ self._lock = threading.RLock()
457
+
458
+ def add_dependency(self, key: str, depends_on: str | list[str]) -> None:
459
+ """Add a dependency.
460
+
461
+ Args:
462
+ key: Cache key that has the dependency
463
+ depends_on: Key(s) that this key depends on
464
+ """
465
+ if isinstance(depends_on, str):
466
+ depends_on = [depends_on]
467
+
468
+ with self._lock:
469
+ if key not in self._dependencies:
470
+ self._dependencies[key] = set()
471
+
472
+ for dep in depends_on:
473
+ self._dependencies[key].add(dep)
474
+
475
+ if dep not in self._dependents:
476
+ self._dependents[dep] = set()
477
+ self._dependents[dep].add(key)
478
+
479
+ def remove_dependency(self, key: str, depends_on: str | None = None) -> None:
480
+ """Remove dependency.
481
+
482
+ Args:
483
+ key: Cache key
484
+ depends_on: Specific dependency to remove, or None for all
485
+ """
486
+ with self._lock:
487
+ if key not in self._dependencies:
488
+ return
489
+
490
+ if depends_on:
491
+ self._dependencies[key].discard(depends_on)
492
+ if depends_on in self._dependents:
493
+ self._dependents[depends_on].discard(key)
494
+ else:
495
+ # Remove all dependencies
496
+ for dep in self._dependencies[key]:
497
+ if dep in self._dependents:
498
+ self._dependents[dep].discard(key)
499
+ del self._dependencies[key]
500
+
501
+ def get_dependencies(self, key: str) -> set[str]:
502
+ """Get keys that this key depends on.
503
+
504
+ Args:
505
+ key: Cache key
506
+
507
+ Returns:
508
+ Set of dependency keys
509
+ """
510
+ with self._lock:
511
+ return self._dependencies.get(key, set()).copy()
512
+
513
+ def get_dependents(self, key: str, recursive: bool = True) -> set[str]:
514
+ """Get keys that depend on this key.
515
+
516
+ Args:
517
+ key: Cache key
518
+ recursive: If True, include transitive dependents
519
+
520
+ Returns:
521
+ Set of dependent keys
522
+ """
523
+ with self._lock:
524
+ direct = self._dependents.get(key, set()).copy()
525
+
526
+ if not recursive:
527
+ return direct
528
+
529
+ # Find transitive dependents
530
+ all_dependents = direct.copy()
531
+ to_process = list(direct)
532
+
533
+ while to_process:
534
+ current = to_process.pop()
535
+ new_deps = self._dependents.get(current, set()) - all_dependents
536
+ all_dependents |= new_deps
537
+ to_process.extend(new_deps)
538
+
539
+ return all_dependents
540
+
541
+ def unregister(self, key: str) -> None:
542
+ """Unregister a key and all its relationships.
543
+
544
+ Args:
545
+ key: Cache key to unregister
546
+ """
547
+ with self._lock:
548
+ # Remove dependencies
549
+ self.remove_dependency(key)
550
+
551
+ # Remove from dependents index
552
+ if key in self._dependents:
553
+ del self._dependents[key]
554
+
555
+ # Remove from other keys' dependencies
556
+ for k in list(self._dependencies.keys()):
557
+ self._dependencies[k].discard(key)
558
+
559
+ def clear(self) -> None:
560
+ """Clear all dependencies."""
561
+ with self._lock:
562
+ self._dependencies.clear()
563
+ self._dependents.clear()
564
+
565
+
566
+ # =============================================================================
567
+ # Pattern Invalidator
568
+ # =============================================================================
569
+
570
+
571
+ class PatternInvalidator:
572
+ """Pattern-based cache invalidation.
573
+
574
+ Provides flexible cache invalidation using various pattern types
575
+ including glob, regex, prefix, and tag-based matching.
576
+
577
+ Example:
578
+ from truthound.profiler.caching import ProfileCache
579
+
580
+ cache = ProfileCache()
581
+ invalidator = PatternInvalidator(cache.backend)
582
+
583
+ # Invalidate all user profiles
584
+ result = invalidator.invalidate("profile:user:*")
585
+ print(f"Invalidated {result.keys_invalidated} keys")
586
+
587
+ # Invalidate by regex
588
+ result = invalidator.invalidate(
589
+ r"profile:user:\d+:session",
590
+ pattern_type=PatternType.REGEX,
591
+ )
592
+
593
+ # Invalidate with cascade
594
+ result = invalidator.invalidate_with_cascade("user:123")
595
+
596
+ Attributes:
597
+ backend: Cache backend that supports key listing
598
+ tag_registry: Optional tag registry for tag-based invalidation
599
+ dependency_tracker: Optional dependency tracker for cascading
600
+ batch_size: Batch size for delete operations
601
+ """
602
+
603
+ def __init__(
604
+ self,
605
+ backend: Any,
606
+ tag_registry: TagRegistry | None = None,
607
+ dependency_tracker: DependencyTracker | None = None,
608
+ batch_size: int = 1000,
609
+ ):
610
+ """Initialize invalidator.
611
+
612
+ Args:
613
+ backend: Cache backend
614
+ tag_registry: Tag registry instance
615
+ dependency_tracker: Dependency tracker instance
616
+ batch_size: Batch size for deletions
617
+ """
618
+ self.backend = backend
619
+ self.tag_registry = tag_registry or TagRegistry()
620
+ self.dependency_tracker = dependency_tracker or DependencyTracker()
621
+ self.batch_size = batch_size
622
+ self._lock = threading.RLock()
623
+
624
+ def invalidate(
625
+ self,
626
+ pattern: str,
627
+ pattern_type: PatternType = PatternType.GLOB,
628
+ dry_run: bool = False,
629
+ ) -> InvalidationResult:
630
+ """Invalidate cache entries matching pattern.
631
+
632
+ Args:
633
+ pattern: Invalidation pattern
634
+ pattern_type: Type of pattern matching
635
+ dry_run: If True, only count matches without deleting
636
+
637
+ Returns:
638
+ InvalidationResult with statistics
639
+ """
640
+ start_time = time.time()
641
+ result = InvalidationResult(pattern=pattern)
642
+
643
+ try:
644
+ inv_pattern = InvalidationPattern(pattern, pattern_type)
645
+
646
+ # Find matching keys
647
+ matching_keys = self._find_matching_keys(inv_pattern)
648
+ result.keys_matched = len(matching_keys)
649
+
650
+ if not dry_run:
651
+ # Delete in batches
652
+ deleted = self._delete_keys(list(matching_keys))
653
+ result.keys_invalidated = deleted
654
+
655
+ # Unregister from tag registry
656
+ for key in matching_keys:
657
+ self.tag_registry.unregister(key)
658
+ self.dependency_tracker.unregister(key)
659
+
660
+ except Exception as e:
661
+ result.errors.append(str(e))
662
+
663
+ result.duration_ms = (time.time() - start_time) * 1000
664
+ return result
665
+
666
+ def invalidate_by_tags(
667
+ self,
668
+ tags: list[str],
669
+ match_all: bool = True,
670
+ dry_run: bool = False,
671
+ ) -> InvalidationResult:
672
+ """Invalidate cache entries by tags.
673
+
674
+ Args:
675
+ tags: Tags to match
676
+ match_all: If True, keys must have all tags
677
+ dry_run: If True, only count matches
678
+
679
+ Returns:
680
+ InvalidationResult
681
+ """
682
+ start_time = time.time()
683
+ pattern_str = f"tags:[{','.join(tags)}]"
684
+ result = InvalidationResult(pattern=pattern_str)
685
+
686
+ try:
687
+ matching_keys = self.tag_registry.find_by_tags(tags, match_all)
688
+ result.keys_matched = len(matching_keys)
689
+
690
+ if not dry_run:
691
+ deleted = self._delete_keys(list(matching_keys))
692
+ result.keys_invalidated = deleted
693
+
694
+ for key in matching_keys:
695
+ self.tag_registry.unregister(key)
696
+ self.dependency_tracker.unregister(key)
697
+
698
+ except Exception as e:
699
+ result.errors.append(str(e))
700
+
701
+ result.duration_ms = (time.time() - start_time) * 1000
702
+ return result
703
+
704
+ def invalidate_with_cascade(
705
+ self,
706
+ key: str,
707
+ dry_run: bool = False,
708
+ ) -> InvalidationResult:
709
+ """Invalidate a key and all its dependents.
710
+
711
+ Args:
712
+ key: Cache key to invalidate
713
+ dry_run: If True, only count matches
714
+
715
+ Returns:
716
+ InvalidationResult
717
+ """
718
+ start_time = time.time()
719
+ result = InvalidationResult(pattern=f"cascade:{key}")
720
+
721
+ try:
722
+ # Get all dependents
723
+ dependents = self.dependency_tracker.get_dependents(key, recursive=True)
724
+ all_keys = {key} | dependents
725
+ result.keys_matched = len(all_keys)
726
+
727
+ if not dry_run:
728
+ deleted = self._delete_keys(list(all_keys))
729
+ result.keys_invalidated = deleted
730
+
731
+ for k in all_keys:
732
+ self.tag_registry.unregister(k)
733
+ self.dependency_tracker.unregister(k)
734
+
735
+ except Exception as e:
736
+ result.errors.append(str(e))
737
+
738
+ result.duration_ms = (time.time() - start_time) * 1000
739
+ return result
740
+
741
+ def invalidate_multiple(
742
+ self,
743
+ patterns: list[str],
744
+ pattern_type: PatternType = PatternType.GLOB,
745
+ dry_run: bool = False,
746
+ ) -> list[InvalidationResult]:
747
+ """Invalidate multiple patterns.
748
+
749
+ Args:
750
+ patterns: List of patterns to invalidate
751
+ pattern_type: Type of pattern matching
752
+ dry_run: If True, only count matches
753
+
754
+ Returns:
755
+ List of InvalidationResult for each pattern
756
+ """
757
+ return [
758
+ self.invalidate(pattern, pattern_type, dry_run)
759
+ for pattern in patterns
760
+ ]
761
+
762
+ def _find_matching_keys(self, pattern: InvalidationPattern) -> set[str]:
763
+ """Find all keys matching pattern.
764
+
765
+ Args:
766
+ pattern: Invalidation pattern
767
+
768
+ Returns:
769
+ Set of matching keys
770
+ """
771
+ matching = set()
772
+
773
+ # Try to use backend's native pattern support
774
+ if hasattr(self.backend, "keys"):
775
+ try:
776
+ if pattern.pattern_type == PatternType.GLOB:
777
+ # Some backends support glob patterns natively
778
+ for key in self.backend.keys(pattern.pattern):
779
+ matching.add(key)
780
+ return matching
781
+ elif pattern.pattern_type == PatternType.PREFIX:
782
+ for key in self.backend.keys(f"{pattern.pattern}*"):
783
+ matching.add(key)
784
+ return matching
785
+ except (TypeError, NotImplementedError):
786
+ pass
787
+
788
+ # Fall back to scanning all keys
789
+ try:
790
+ for key in self.backend.keys("*"):
791
+ if pattern.matches(key):
792
+ matching.add(key)
793
+ except (TypeError, NotImplementedError):
794
+ pass
795
+
796
+ # For memory backend, scan internal cache
797
+ if hasattr(self.backend, "_cache"):
798
+ for key in list(self.backend._cache.keys()):
799
+ if pattern.matches(key):
800
+ matching.add(key)
801
+
802
+ return matching
803
+
804
+ def _delete_keys(self, keys: list[str]) -> int:
805
+ """Delete keys from cache.
806
+
807
+ Args:
808
+ keys: Keys to delete
809
+
810
+ Returns:
811
+ Number of keys deleted
812
+ """
813
+ if not keys:
814
+ return 0
815
+
816
+ # Try batch delete
817
+ if hasattr(self.backend, "delete_many"):
818
+ try:
819
+ total = 0
820
+ for i in range(0, len(keys), self.batch_size):
821
+ batch = keys[i:i + self.batch_size]
822
+ total += self.backend.delete_many(batch)
823
+ return total
824
+ except (TypeError, NotImplementedError):
825
+ pass
826
+
827
+ # Fall back to individual deletes
828
+ deleted = 0
829
+ for key in keys:
830
+ try:
831
+ if self.backend.delete(key):
832
+ deleted += 1
833
+ except Exception:
834
+ pass
835
+
836
+ return deleted
837
+
838
+ def register_with_tags(
839
+ self,
840
+ key: str,
841
+ tags: set[str] | list[str],
842
+ ) -> None:
843
+ """Register a cache key with tags.
844
+
845
+ Args:
846
+ key: Cache key
847
+ tags: Tags to associate
848
+ """
849
+ self.tag_registry.register(key, tags)
850
+
851
+ def register_dependency(
852
+ self,
853
+ key: str,
854
+ depends_on: str | list[str],
855
+ ) -> None:
856
+ """Register a cache key dependency.
857
+
858
+ Args:
859
+ key: Cache key
860
+ depends_on: Key(s) this key depends on
861
+ """
862
+ self.dependency_tracker.add_dependency(key, depends_on)
863
+
864
+ def get_stats(self) -> dict[str, Any]:
865
+ """Get invalidator statistics.
866
+
867
+ Returns:
868
+ Statistics dictionary
869
+ """
870
+ return {
871
+ "tag_count": len(self.tag_registry.list_tags()),
872
+ "tag_entries": len(self.tag_registry._entries),
873
+ "tag_counts": self.tag_registry.tag_counts(),
874
+ "dependency_count": len(self.dependency_tracker._dependencies),
875
+ }
876
+
877
+
878
+ # =============================================================================
879
+ # Convenience Functions
880
+ # =============================================================================
881
+
882
+
883
+ def create_pattern(
884
+ pattern: str,
885
+ pattern_type: PatternType | str = PatternType.GLOB,
886
+ ) -> InvalidationPattern:
887
+ """Create an invalidation pattern.
888
+
889
+ Args:
890
+ pattern: Pattern string
891
+ pattern_type: Type of pattern
892
+
893
+ Returns:
894
+ InvalidationPattern instance
895
+ """
896
+ if isinstance(pattern_type, str):
897
+ pattern_type = PatternType(pattern_type.lower())
898
+
899
+ return InvalidationPattern(pattern=pattern, pattern_type=pattern_type)
900
+
901
+
902
+ def glob_to_regex(pattern: str) -> str:
903
+ """Convert glob pattern to regex pattern.
904
+
905
+ Args:
906
+ pattern: Glob pattern
907
+
908
+ Returns:
909
+ Equivalent regex pattern
910
+ """
911
+ return InvalidationPattern(pattern, PatternType.GLOB)._glob_to_regex(pattern)