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,1037 @@
1
+ """Docstring parsing with support for multiple docstring styles.
2
+
3
+ This module provides robust docstring parsing capabilities supporting
4
+ Google, NumPy, Sphinx/reStructuredText, and Epytext docstring styles.
5
+
6
+ Key Features:
7
+ - Automatic style detection
8
+ - Consistent output format regardless of input style
9
+ - Parameter, return, exception, and example extraction
10
+ - Markdown and RST support
11
+
12
+ Example:
13
+ from truthound.docs.parser import DocstringParser, DocstringStyle
14
+
15
+ parser = DocstringParser(style=DocstringStyle.AUTO)
16
+ parsed = parser.parse('''
17
+ Calculate the sum of two numbers.
18
+
19
+ Args:
20
+ a: First number
21
+ b: Second number
22
+
23
+ Returns:
24
+ Sum of a and b
25
+ ''')
26
+
27
+ print(parsed.description)
28
+ print(parsed.params)
29
+ print(parsed.returns)
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import re
35
+ from dataclasses import dataclass, field
36
+ from enum import Enum, auto
37
+ from typing import Any
38
+
39
+
40
+ class DocstringStyle(Enum):
41
+ """Supported docstring styles."""
42
+
43
+ AUTO = auto() # Auto-detect style
44
+ GOOGLE = auto() # Google style
45
+ NUMPY = auto() # NumPy style
46
+ SPHINX = auto() # Sphinx/reST style
47
+ EPYTEXT = auto() # Epytext style
48
+
49
+
50
+ @dataclass
51
+ class ParamInfo:
52
+ """Information about a function parameter.
53
+
54
+ Attributes:
55
+ name: Parameter name
56
+ type_hint: Type annotation string
57
+ description: Parameter description
58
+ default: Default value if any
59
+ optional: Whether parameter is optional
60
+ """
61
+
62
+ name: str
63
+ type_hint: str = ""
64
+ description: str = ""
65
+ default: str | None = None
66
+ optional: bool = False
67
+
68
+ def to_dict(self) -> dict[str, Any]:
69
+ """Convert to dictionary."""
70
+ return {
71
+ "name": self.name,
72
+ "type_hint": self.type_hint,
73
+ "description": self.description,
74
+ "default": self.default,
75
+ "optional": self.optional,
76
+ }
77
+
78
+
79
+ @dataclass
80
+ class ReturnInfo:
81
+ """Information about function return value.
82
+
83
+ Attributes:
84
+ type_hint: Return type annotation
85
+ description: Return value description
86
+ """
87
+
88
+ type_hint: str = ""
89
+ description: str = ""
90
+
91
+ def to_dict(self) -> dict[str, Any]:
92
+ """Convert to dictionary."""
93
+ return {
94
+ "type_hint": self.type_hint,
95
+ "description": self.description,
96
+ }
97
+
98
+
99
+ @dataclass
100
+ class RaiseInfo:
101
+ """Information about raised exceptions.
102
+
103
+ Attributes:
104
+ exception_type: Exception class name
105
+ description: When/why exception is raised
106
+ """
107
+
108
+ exception_type: str
109
+ description: str = ""
110
+
111
+ def to_dict(self) -> dict[str, Any]:
112
+ """Convert to dictionary."""
113
+ return {
114
+ "exception_type": self.exception_type,
115
+ "description": self.description,
116
+ }
117
+
118
+
119
+ @dataclass
120
+ class ExampleInfo:
121
+ """Code example from docstring.
122
+
123
+ Attributes:
124
+ code: Example code
125
+ description: Example description
126
+ language: Code language (default: python)
127
+ """
128
+
129
+ code: str
130
+ description: str = ""
131
+ language: str = "python"
132
+
133
+ def to_dict(self) -> dict[str, Any]:
134
+ """Convert to dictionary."""
135
+ return {
136
+ "code": self.code,
137
+ "description": self.description,
138
+ "language": self.language,
139
+ }
140
+
141
+
142
+ @dataclass
143
+ class AttributeInfo:
144
+ """Information about class/module attributes.
145
+
146
+ Attributes:
147
+ name: Attribute name
148
+ type_hint: Type annotation
149
+ description: Attribute description
150
+ """
151
+
152
+ name: str
153
+ type_hint: str = ""
154
+ description: str = ""
155
+
156
+ def to_dict(self) -> dict[str, Any]:
157
+ """Convert to dictionary."""
158
+ return {
159
+ "name": self.name,
160
+ "type_hint": self.type_hint,
161
+ "description": self.description,
162
+ }
163
+
164
+
165
+ @dataclass
166
+ class ParsedDocstring:
167
+ """Parsed docstring with structured information.
168
+
169
+ Contains all extracted information from a docstring in a
170
+ normalized format regardless of the original docstring style.
171
+
172
+ Attributes:
173
+ raw: Original raw docstring
174
+ style: Detected docstring style
175
+ short_description: First line summary
176
+ long_description: Extended description
177
+ params: List of parameter information
178
+ returns: Return value information
179
+ raises: List of exception information
180
+ examples: List of code examples
181
+ attributes: List of attribute information
182
+ notes: Additional notes
183
+ warnings: Warning notes
184
+ see_also: Cross-references
185
+ deprecated: Deprecation notice
186
+ version: Version information
187
+ todo: Todo items
188
+ """
189
+
190
+ raw: str = ""
191
+ style: DocstringStyle = DocstringStyle.AUTO
192
+ short_description: str = ""
193
+ long_description: str = ""
194
+ params: list[ParamInfo] = field(default_factory=list)
195
+ returns: ReturnInfo | None = None
196
+ yields: ReturnInfo | None = None
197
+ raises: list[RaiseInfo] = field(default_factory=list)
198
+ examples: list[ExampleInfo] = field(default_factory=list)
199
+ attributes: list[AttributeInfo] = field(default_factory=list)
200
+ notes: list[str] = field(default_factory=list)
201
+ warnings: list[str] = field(default_factory=list)
202
+ see_also: list[str] = field(default_factory=list)
203
+ deprecated: str | None = None
204
+ version: str | None = None
205
+ todo: list[str] = field(default_factory=list)
206
+
207
+ @property
208
+ def description(self) -> str:
209
+ """Get full description (short + long)."""
210
+ if self.long_description:
211
+ return f"{self.short_description}\n\n{self.long_description}"
212
+ return self.short_description
213
+
214
+ def to_dict(self) -> dict[str, Any]:
215
+ """Convert to dictionary for serialization."""
216
+ return {
217
+ "style": self.style.name,
218
+ "short_description": self.short_description,
219
+ "long_description": self.long_description,
220
+ "params": [p.to_dict() for p in self.params],
221
+ "returns": self.returns.to_dict() if self.returns else None,
222
+ "yields": self.yields.to_dict() if self.yields else None,
223
+ "raises": [r.to_dict() for r in self.raises],
224
+ "examples": [e.to_dict() for e in self.examples],
225
+ "attributes": [a.to_dict() for a in self.attributes],
226
+ "notes": self.notes,
227
+ "warnings": self.warnings,
228
+ "see_also": self.see_also,
229
+ "deprecated": self.deprecated,
230
+ "version": self.version,
231
+ "todo": self.todo,
232
+ }
233
+
234
+ def to_markdown(self) -> str:
235
+ """Render as Markdown."""
236
+ lines = []
237
+
238
+ if self.short_description:
239
+ lines.append(self.short_description)
240
+ lines.append("")
241
+
242
+ if self.long_description:
243
+ lines.append(self.long_description)
244
+ lines.append("")
245
+
246
+ if self.deprecated:
247
+ lines.append(f"> **Deprecated:** {self.deprecated}")
248
+ lines.append("")
249
+
250
+ if self.params:
251
+ lines.append("**Parameters:**")
252
+ lines.append("")
253
+ for p in self.params:
254
+ type_str = f" ({p.type_hint})" if p.type_hint else ""
255
+ default_str = f" = {p.default}" if p.default else ""
256
+ lines.append(f"- `{p.name}`{type_str}{default_str}: {p.description}")
257
+ lines.append("")
258
+
259
+ if self.returns:
260
+ lines.append("**Returns:**")
261
+ lines.append("")
262
+ type_str = f" ({self.returns.type_hint})" if self.returns.type_hint else ""
263
+ lines.append(f"- {type_str}: {self.returns.description}")
264
+ lines.append("")
265
+
266
+ if self.yields:
267
+ lines.append("**Yields:**")
268
+ lines.append("")
269
+ type_str = f" ({self.yields.type_hint})" if self.yields.type_hint else ""
270
+ lines.append(f"- {type_str}: {self.yields.description}")
271
+ lines.append("")
272
+
273
+ if self.raises:
274
+ lines.append("**Raises:**")
275
+ lines.append("")
276
+ for r in self.raises:
277
+ lines.append(f"- `{r.exception_type}`: {r.description}")
278
+ lines.append("")
279
+
280
+ if self.examples:
281
+ lines.append("**Examples:**")
282
+ lines.append("")
283
+ for ex in self.examples:
284
+ if ex.description:
285
+ lines.append(ex.description)
286
+ lines.append(f"```{ex.language}")
287
+ lines.append(ex.code)
288
+ lines.append("```")
289
+ lines.append("")
290
+
291
+ if self.notes:
292
+ lines.append("**Notes:**")
293
+ lines.append("")
294
+ for note in self.notes:
295
+ lines.append(f"- {note}")
296
+ lines.append("")
297
+
298
+ if self.warnings:
299
+ lines.append("**Warnings:**")
300
+ lines.append("")
301
+ for warning in self.warnings:
302
+ lines.append(f"> ⚠️ {warning}")
303
+ lines.append("")
304
+
305
+ if self.see_also:
306
+ lines.append("**See Also:**")
307
+ lines.append("")
308
+ for ref in self.see_also:
309
+ lines.append(f"- {ref}")
310
+ lines.append("")
311
+
312
+ return "\n".join(lines)
313
+
314
+
315
+ class DocstringParser:
316
+ """Parser for Python docstrings.
317
+
318
+ Supports automatic detection and parsing of Google, NumPy, Sphinx,
319
+ and Epytext docstring styles.
320
+
321
+ Example:
322
+ parser = DocstringParser()
323
+ parsed = parser.parse(my_function.__doc__)
324
+ print(parsed.short_description)
325
+ print(parsed.params)
326
+
327
+ Attributes:
328
+ style: Preferred docstring style (AUTO for detection)
329
+ trim: Whether to strip leading/trailing whitespace
330
+ """
331
+
332
+ # Section headers for Google style
333
+ GOOGLE_SECTIONS = {
334
+ "args": "params",
335
+ "arguments": "params",
336
+ "parameters": "params",
337
+ "params": "params",
338
+ "returns": "returns",
339
+ "return": "returns",
340
+ "yields": "yields",
341
+ "yield": "yields",
342
+ "raises": "raises",
343
+ "raise": "raises",
344
+ "exceptions": "raises",
345
+ "example": "examples",
346
+ "examples": "examples",
347
+ "attributes": "attributes",
348
+ "note": "notes",
349
+ "notes": "notes",
350
+ "warning": "warnings",
351
+ "warnings": "warnings",
352
+ "see also": "see_also",
353
+ "deprecated": "deprecated",
354
+ "todo": "todo",
355
+ }
356
+
357
+ # Section headers for NumPy style
358
+ NUMPY_SECTIONS = {
359
+ "parameters": "params",
360
+ "params": "params",
361
+ "returns": "returns",
362
+ "yields": "yields",
363
+ "raises": "raises",
364
+ "examples": "examples",
365
+ "attributes": "attributes",
366
+ "notes": "notes",
367
+ "warnings": "warnings",
368
+ "see also": "see_also",
369
+ "references": "see_also",
370
+ "deprecated": "deprecated",
371
+ }
372
+
373
+ def __init__(
374
+ self,
375
+ style: DocstringStyle = DocstringStyle.AUTO,
376
+ trim: bool = True,
377
+ ):
378
+ """Initialize parser.
379
+
380
+ Args:
381
+ style: Docstring style to use (AUTO for detection)
382
+ trim: Whether to strip whitespace from parsed content
383
+ """
384
+ self.style = style
385
+ self.trim = trim
386
+
387
+ def parse(self, docstring: str | None) -> ParsedDocstring:
388
+ """Parse a docstring.
389
+
390
+ Args:
391
+ docstring: Raw docstring to parse
392
+
393
+ Returns:
394
+ ParsedDocstring with extracted information
395
+ """
396
+ if not docstring:
397
+ return ParsedDocstring()
398
+
399
+ # Clean up the docstring
400
+ docstring = self._clean_docstring(docstring)
401
+
402
+ # Detect style if AUTO
403
+ style = self.style
404
+ if style == DocstringStyle.AUTO:
405
+ style = self._detect_style(docstring)
406
+
407
+ # Parse based on style
408
+ if style == DocstringStyle.GOOGLE:
409
+ return self._parse_google(docstring, style)
410
+ elif style == DocstringStyle.NUMPY:
411
+ return self._parse_numpy(docstring, style)
412
+ elif style == DocstringStyle.SPHINX:
413
+ return self._parse_sphinx(docstring, style)
414
+ elif style == DocstringStyle.EPYTEXT:
415
+ return self._parse_epytext(docstring, style)
416
+ else:
417
+ # Default to Google style parsing
418
+ return self._parse_google(docstring, style)
419
+
420
+ def _clean_docstring(self, docstring: str) -> str:
421
+ """Clean and normalize docstring."""
422
+ if not docstring:
423
+ return ""
424
+
425
+ # Handle indentation
426
+ lines = docstring.expandtabs().splitlines()
427
+
428
+ # Find minimum indentation (excluding first line)
429
+ min_indent = float("inf")
430
+ for line in lines[1:]:
431
+ stripped = line.lstrip()
432
+ if stripped:
433
+ min_indent = min(min_indent, len(line) - len(stripped))
434
+
435
+ # Remove common indentation
436
+ if min_indent < float("inf"):
437
+ cleaned = [lines[0].strip()]
438
+ for line in lines[1:]:
439
+ if line.strip():
440
+ cleaned.append(line[int(min_indent):])
441
+ else:
442
+ cleaned.append("")
443
+ docstring = "\n".join(cleaned)
444
+
445
+ if self.trim:
446
+ docstring = docstring.strip()
447
+
448
+ return docstring
449
+
450
+ def _detect_style(self, docstring: str) -> DocstringStyle:
451
+ """Detect docstring style.
452
+
453
+ Args:
454
+ docstring: Cleaned docstring
455
+
456
+ Returns:
457
+ Detected DocstringStyle
458
+ """
459
+ # Check for Sphinx style (:param, :returns:, etc.)
460
+ if re.search(r":\w+:", docstring):
461
+ return DocstringStyle.SPHINX
462
+
463
+ # Check for Epytext style (@param, @return, etc.)
464
+ if re.search(r"@\w+", docstring):
465
+ return DocstringStyle.EPYTEXT
466
+
467
+ # Check for NumPy style (underlined sections)
468
+ if re.search(r"\n\s*-{3,}\s*\n", docstring):
469
+ return DocstringStyle.NUMPY
470
+
471
+ # Check for Google style (Section:)
472
+ if re.search(r"\n\s*(Args|Returns|Raises|Example|Attributes):\s*\n", docstring):
473
+ return DocstringStyle.GOOGLE
474
+
475
+ # Default to Google
476
+ return DocstringStyle.GOOGLE
477
+
478
+ def _parse_google(self, docstring: str, style: DocstringStyle) -> ParsedDocstring:
479
+ """Parse Google-style docstring."""
480
+ result = ParsedDocstring(raw=docstring, style=style)
481
+
482
+ # Split into sections
483
+ sections = self._split_google_sections(docstring)
484
+
485
+ # Parse description (content before first section)
486
+ if "" in sections:
487
+ desc = sections[""].strip()
488
+ lines = desc.split("\n", 1)
489
+ result.short_description = lines[0].strip()
490
+ if len(lines) > 1:
491
+ result.long_description = lines[1].strip()
492
+
493
+ # Parse each section
494
+ for section_name, content in sections.items():
495
+ if section_name == "":
496
+ continue
497
+
498
+ section_type = self.GOOGLE_SECTIONS.get(section_name.lower())
499
+
500
+ if section_type == "params":
501
+ result.params = self._parse_google_params(content)
502
+ elif section_type == "returns":
503
+ result.returns = self._parse_google_return(content)
504
+ elif section_type == "yields":
505
+ result.yields = self._parse_google_return(content)
506
+ elif section_type == "raises":
507
+ result.raises = self._parse_google_raises(content)
508
+ elif section_type == "examples":
509
+ result.examples = self._parse_examples(content)
510
+ elif section_type == "attributes":
511
+ result.attributes = self._parse_google_attributes(content)
512
+ elif section_type == "notes":
513
+ result.notes = self._parse_notes(content)
514
+ elif section_type == "warnings":
515
+ result.warnings = self._parse_notes(content)
516
+ elif section_type == "see_also":
517
+ result.see_also = self._parse_see_also(content)
518
+ elif section_type == "deprecated":
519
+ result.deprecated = content.strip()
520
+ elif section_type == "todo":
521
+ result.todo = self._parse_notes(content)
522
+
523
+ return result
524
+
525
+ def _split_google_sections(self, docstring: str) -> dict[str, str]:
526
+ """Split docstring into Google-style sections."""
527
+ sections: dict[str, str] = {}
528
+
529
+ # Pattern for section headers
530
+ pattern = r"^(\s*)([A-Z][a-z]+(?:\s+[A-Z][a-z]+)?):\s*$"
531
+
532
+ current_section = ""
533
+ current_content: list[str] = []
534
+ base_indent = 0
535
+
536
+ for line in docstring.split("\n"):
537
+ match = re.match(pattern, line, re.MULTILINE)
538
+
539
+ if match:
540
+ # Save previous section
541
+ if current_section or current_content:
542
+ sections[current_section] = "\n".join(current_content)
543
+
544
+ current_section = match.group(2)
545
+ current_content = []
546
+ base_indent = len(match.group(1))
547
+ else:
548
+ current_content.append(line)
549
+
550
+ # Save last section
551
+ if current_section or current_content:
552
+ sections[current_section] = "\n".join(current_content)
553
+
554
+ return sections
555
+
556
+ def _parse_google_params(self, content: str) -> list[ParamInfo]:
557
+ """Parse Google-style parameter documentation."""
558
+ params = []
559
+
560
+ # Pattern: name (type): description or name: description
561
+ pattern = r"^\s*(\*{0,2}\w+)\s*(?:\(([^)]+)\))?\s*:\s*(.*)$"
562
+
563
+ current_param: ParamInfo | None = None
564
+ current_desc: list[str] = []
565
+
566
+ for line in content.split("\n"):
567
+ match = re.match(pattern, line)
568
+
569
+ if match:
570
+ # Save previous param
571
+ if current_param:
572
+ current_param.description = " ".join(current_desc).strip()
573
+ params.append(current_param)
574
+
575
+ name = match.group(1)
576
+ type_hint = match.group(2) or ""
577
+ desc = match.group(3).strip()
578
+
579
+ # Check for optional
580
+ optional = "optional" in type_hint.lower()
581
+ if optional:
582
+ type_hint = re.sub(r",?\s*optional", "", type_hint, flags=re.I)
583
+
584
+ # Check for default
585
+ default = None
586
+ default_match = re.search(r"default[s]?\s*[=:]\s*(.+)", desc, re.I)
587
+ if default_match:
588
+ default = default_match.group(1).strip()
589
+
590
+ current_param = ParamInfo(
591
+ name=name.lstrip("*"),
592
+ type_hint=type_hint.strip(),
593
+ optional=optional,
594
+ default=default,
595
+ )
596
+ current_desc = [desc] if desc else []
597
+ else:
598
+ stripped = line.strip()
599
+ if stripped and current_param:
600
+ current_desc.append(stripped)
601
+
602
+ # Save last param
603
+ if current_param:
604
+ current_param.description = " ".join(current_desc).strip()
605
+ params.append(current_param)
606
+
607
+ return params
608
+
609
+ def _parse_google_return(self, content: str) -> ReturnInfo:
610
+ """Parse Google-style return documentation."""
611
+ content = content.strip()
612
+
613
+ # Pattern: type: description or just description
614
+ match = re.match(r"^([^:]+):\s*(.*)$", content, re.DOTALL)
615
+
616
+ if match:
617
+ type_hint = match.group(1).strip()
618
+ description = match.group(2).strip()
619
+
620
+ # Check if it's really a type or just the description
621
+ if " " in type_hint and not any(c in type_hint for c in "[]|,()"):
622
+ # Likely not a type
623
+ return ReturnInfo(description=content)
624
+
625
+ return ReturnInfo(type_hint=type_hint, description=description)
626
+
627
+ return ReturnInfo(description=content)
628
+
629
+ def _parse_google_raises(self, content: str) -> list[RaiseInfo]:
630
+ """Parse Google-style raises documentation."""
631
+ raises = []
632
+
633
+ pattern = r"^\s*(\w+(?:\.\w+)*)\s*:\s*(.*)$"
634
+
635
+ current_raise: RaiseInfo | None = None
636
+ current_desc: list[str] = []
637
+
638
+ for line in content.split("\n"):
639
+ match = re.match(pattern, line)
640
+
641
+ if match:
642
+ if current_raise:
643
+ current_raise.description = " ".join(current_desc).strip()
644
+ raises.append(current_raise)
645
+
646
+ current_raise = RaiseInfo(
647
+ exception_type=match.group(1),
648
+ )
649
+ current_desc = [match.group(2)] if match.group(2) else []
650
+ else:
651
+ stripped = line.strip()
652
+ if stripped and current_raise:
653
+ current_desc.append(stripped)
654
+
655
+ if current_raise:
656
+ current_raise.description = " ".join(current_desc).strip()
657
+ raises.append(current_raise)
658
+
659
+ return raises
660
+
661
+ def _parse_google_attributes(self, content: str) -> list[AttributeInfo]:
662
+ """Parse Google-style attribute documentation."""
663
+ attributes = []
664
+
665
+ pattern = r"^\s*(\w+)\s*(?:\(([^)]+)\))?\s*:\s*(.*)$"
666
+
667
+ for line in content.split("\n"):
668
+ match = re.match(pattern, line)
669
+ if match:
670
+ attributes.append(AttributeInfo(
671
+ name=match.group(1),
672
+ type_hint=match.group(2) or "",
673
+ description=match.group(3).strip(),
674
+ ))
675
+
676
+ return attributes
677
+
678
+ def _parse_numpy(self, docstring: str, style: DocstringStyle) -> ParsedDocstring:
679
+ """Parse NumPy-style docstring."""
680
+ result = ParsedDocstring(raw=docstring, style=style)
681
+
682
+ # Split into sections based on underlines
683
+ sections = self._split_numpy_sections(docstring)
684
+
685
+ # Parse description
686
+ if "" in sections:
687
+ desc = sections[""].strip()
688
+ lines = desc.split("\n", 1)
689
+ result.short_description = lines[0].strip()
690
+ if len(lines) > 1:
691
+ result.long_description = lines[1].strip()
692
+
693
+ # Parse each section
694
+ for section_name, content in sections.items():
695
+ if section_name == "":
696
+ continue
697
+
698
+ section_type = self.NUMPY_SECTIONS.get(section_name.lower())
699
+
700
+ if section_type == "params":
701
+ result.params = self._parse_numpy_params(content)
702
+ elif section_type == "returns":
703
+ result.returns = self._parse_numpy_return(content)
704
+ elif section_type == "yields":
705
+ result.yields = self._parse_numpy_return(content)
706
+ elif section_type == "raises":
707
+ result.raises = self._parse_numpy_raises(content)
708
+ elif section_type == "examples":
709
+ result.examples = self._parse_examples(content)
710
+ elif section_type == "attributes":
711
+ result.attributes = self._parse_numpy_attributes(content)
712
+ elif section_type == "notes":
713
+ result.notes = [content.strip()]
714
+ elif section_type == "warnings":
715
+ result.warnings = [content.strip()]
716
+ elif section_type == "see_also":
717
+ result.see_also = self._parse_see_also(content)
718
+ elif section_type == "deprecated":
719
+ result.deprecated = content.strip()
720
+
721
+ return result
722
+
723
+ def _split_numpy_sections(self, docstring: str) -> dict[str, str]:
724
+ """Split docstring into NumPy-style sections."""
725
+ sections: dict[str, str] = {}
726
+
727
+ # Find section headers (word followed by dashes on next line)
728
+ lines = docstring.split("\n")
729
+ current_section = ""
730
+ current_content: list[str] = []
731
+
732
+ i = 0
733
+ while i < len(lines):
734
+ line = lines[i]
735
+ stripped = line.strip()
736
+
737
+ # Check if next line is dashes
738
+ if i + 1 < len(lines) and re.match(r"^-+$", lines[i + 1].strip()):
739
+ # Save previous section
740
+ if current_section or current_content:
741
+ sections[current_section] = "\n".join(current_content)
742
+
743
+ current_section = stripped
744
+ current_content = []
745
+ i += 2 # Skip the dash line
746
+ continue
747
+
748
+ current_content.append(line)
749
+ i += 1
750
+
751
+ # Save last section
752
+ if current_section or current_content:
753
+ sections[current_section] = "\n".join(current_content)
754
+
755
+ return sections
756
+
757
+ def _parse_numpy_params(self, content: str) -> list[ParamInfo]:
758
+ """Parse NumPy-style parameter documentation."""
759
+ params = []
760
+
761
+ # Pattern: name : type
762
+ pattern = r"^(\w+)\s*:\s*(.*)$"
763
+
764
+ lines = content.split("\n")
765
+ i = 0
766
+
767
+ while i < len(lines):
768
+ line = lines[i]
769
+ match = re.match(pattern, line)
770
+
771
+ if match:
772
+ name = match.group(1)
773
+ type_hint = match.group(2).strip()
774
+
775
+ # Collect description from following indented lines
776
+ desc_lines = []
777
+ i += 1
778
+ while i < len(lines) and (not lines[i].strip() or lines[i].startswith(" ")):
779
+ if lines[i].strip():
780
+ desc_lines.append(lines[i].strip())
781
+ i += 1
782
+
783
+ params.append(ParamInfo(
784
+ name=name,
785
+ type_hint=type_hint,
786
+ description=" ".join(desc_lines),
787
+ ))
788
+ else:
789
+ i += 1
790
+
791
+ return params
792
+
793
+ def _parse_numpy_return(self, content: str) -> ReturnInfo:
794
+ """Parse NumPy-style return documentation."""
795
+ lines = content.strip().split("\n")
796
+ if not lines:
797
+ return ReturnInfo()
798
+
799
+ # First line might be type
800
+ first = lines[0].strip()
801
+ if ":" in first:
802
+ type_hint, desc = first.split(":", 1)
803
+ return ReturnInfo(type_hint=type_hint.strip(), description=desc.strip())
804
+
805
+ # Check if it's just a type on the first line
806
+ if len(lines) > 1:
807
+ return ReturnInfo(
808
+ type_hint=first,
809
+ description=" ".join(line.strip() for line in lines[1:]),
810
+ )
811
+
812
+ return ReturnInfo(description=first)
813
+
814
+ def _parse_numpy_raises(self, content: str) -> list[RaiseInfo]:
815
+ """Parse NumPy-style raises documentation."""
816
+ return self._parse_numpy_params_generic(content, RaiseInfo, "exception_type")
817
+
818
+ def _parse_numpy_attributes(self, content: str) -> list[AttributeInfo]:
819
+ """Parse NumPy-style attribute documentation."""
820
+ return self._parse_numpy_params_generic(content, AttributeInfo, "name")
821
+
822
+ def _parse_numpy_params_generic(
823
+ self,
824
+ content: str,
825
+ cls: type,
826
+ name_field: str,
827
+ ) -> list:
828
+ """Generic parser for NumPy-style param-like sections."""
829
+ items = []
830
+ pattern = r"^(\w+(?:\.\w+)*)\s*:\s*(.*)$"
831
+
832
+ lines = content.split("\n")
833
+ i = 0
834
+
835
+ while i < len(lines):
836
+ line = lines[i]
837
+ match = re.match(pattern, line)
838
+
839
+ if match:
840
+ name = match.group(1)
841
+ extra = match.group(2).strip()
842
+
843
+ desc_lines = []
844
+ i += 1
845
+ while i < len(lines) and (not lines[i].strip() or lines[i].startswith(" ")):
846
+ if lines[i].strip():
847
+ desc_lines.append(lines[i].strip())
848
+ i += 1
849
+
850
+ kwargs = {
851
+ name_field: name,
852
+ "description": " ".join(desc_lines) if desc_lines else extra,
853
+ }
854
+ if hasattr(cls, "type_hint"):
855
+ kwargs["type_hint"] = extra
856
+
857
+ items.append(cls(**kwargs))
858
+ else:
859
+ i += 1
860
+
861
+ return items
862
+
863
+ def _parse_sphinx(self, docstring: str, style: DocstringStyle) -> ParsedDocstring:
864
+ """Parse Sphinx/reST-style docstring."""
865
+ result = ParsedDocstring(raw=docstring, style=style)
866
+
867
+ # Extract description (everything before first :field:)
868
+ desc_match = re.match(r"^(.*?)(?=\n\s*:|\Z)", docstring, re.DOTALL)
869
+ if desc_match:
870
+ desc = desc_match.group(1).strip()
871
+ lines = desc.split("\n", 1)
872
+ result.short_description = lines[0].strip()
873
+ if len(lines) > 1:
874
+ result.long_description = lines[1].strip()
875
+
876
+ # Parse :param name: description
877
+ for match in re.finditer(
878
+ r":param\s+(?:(\w+)\s+)?(\w+):\s*(.+?)(?=\n\s*:|$)",
879
+ docstring,
880
+ re.DOTALL,
881
+ ):
882
+ result.params.append(ParamInfo(
883
+ name=match.group(2),
884
+ type_hint=match.group(1) or "",
885
+ description=match.group(3).strip(),
886
+ ))
887
+
888
+ # Parse :type name: type
889
+ for match in re.finditer(r":type\s+(\w+):\s*(.+?)(?=\n\s*:|$)", docstring, re.DOTALL):
890
+ name = match.group(1)
891
+ type_hint = match.group(2).strip()
892
+ for param in result.params:
893
+ if param.name == name:
894
+ param.type_hint = type_hint
895
+ break
896
+
897
+ # Parse :returns: or :return:
898
+ return_match = re.search(r":returns?:\s*(.+?)(?=\n\s*:|$)", docstring, re.DOTALL)
899
+ if return_match:
900
+ result.returns = ReturnInfo(description=return_match.group(1).strip())
901
+
902
+ # Parse :rtype:
903
+ rtype_match = re.search(r":rtype:\s*(.+?)(?=\n\s*:|$)", docstring, re.DOTALL)
904
+ if rtype_match and result.returns:
905
+ result.returns.type_hint = rtype_match.group(1).strip()
906
+
907
+ # Parse :raises:
908
+ for match in re.finditer(
909
+ r":raises?\s+(\w+(?:\.\w+)*):\s*(.+?)(?=\n\s*:|$)",
910
+ docstring,
911
+ re.DOTALL,
912
+ ):
913
+ result.raises.append(RaiseInfo(
914
+ exception_type=match.group(1),
915
+ description=match.group(2).strip(),
916
+ ))
917
+
918
+ # Parse examples
919
+ example_match = re.search(r":example:(.+?)(?=\n\s*:|$)", docstring, re.DOTALL)
920
+ if example_match:
921
+ result.examples = self._parse_examples(example_match.group(1))
922
+
923
+ return result
924
+
925
+ def _parse_epytext(self, docstring: str, style: DocstringStyle) -> ParsedDocstring:
926
+ """Parse Epytext-style docstring."""
927
+ result = ParsedDocstring(raw=docstring, style=style)
928
+
929
+ # Extract description (everything before first @field)
930
+ desc_match = re.match(r"^(.*?)(?=\n\s*@|\Z)", docstring, re.DOTALL)
931
+ if desc_match:
932
+ desc = desc_match.group(1).strip()
933
+ lines = desc.split("\n", 1)
934
+ result.short_description = lines[0].strip()
935
+ if len(lines) > 1:
936
+ result.long_description = lines[1].strip()
937
+
938
+ # Parse @param name: description
939
+ for match in re.finditer(
940
+ r"@param\s+(\w+):\s*(.+?)(?=\n\s*@|$)",
941
+ docstring,
942
+ re.DOTALL,
943
+ ):
944
+ result.params.append(ParamInfo(
945
+ name=match.group(1),
946
+ description=match.group(2).strip(),
947
+ ))
948
+
949
+ # Parse @type name: type
950
+ for match in re.finditer(r"@type\s+(\w+):\s*(.+?)(?=\n\s*@|$)", docstring, re.DOTALL):
951
+ name = match.group(1)
952
+ type_hint = match.group(2).strip()
953
+ for param in result.params:
954
+ if param.name == name:
955
+ param.type_hint = type_hint
956
+ break
957
+
958
+ # Parse @return: description
959
+ return_match = re.search(r"@returns?:\s*(.+?)(?=\n\s*@|$)", docstring, re.DOTALL)
960
+ if return_match:
961
+ result.returns = ReturnInfo(description=return_match.group(1).strip())
962
+
963
+ # Parse @rtype: type
964
+ rtype_match = re.search(r"@rtype:\s*(.+?)(?=\n\s*@|$)", docstring, re.DOTALL)
965
+ if rtype_match and result.returns:
966
+ result.returns.type_hint = rtype_match.group(1).strip()
967
+
968
+ # Parse @raise exception: description
969
+ for match in re.finditer(
970
+ r"@raises?\s+(\w+(?:\.\w+)*):\s*(.+?)(?=\n\s*@|$)",
971
+ docstring,
972
+ re.DOTALL,
973
+ ):
974
+ result.raises.append(RaiseInfo(
975
+ exception_type=match.group(1),
976
+ description=match.group(2).strip(),
977
+ ))
978
+
979
+ return result
980
+
981
+ def _parse_examples(self, content: str) -> list[ExampleInfo]:
982
+ """Parse example code blocks."""
983
+ examples = []
984
+
985
+ # Look for doctest-style examples
986
+ doctest_pattern = r">>>\s*(.+?)(?=\n>>>|\n\n|\Z)"
987
+ matches = list(re.finditer(doctest_pattern, content, re.DOTALL))
988
+
989
+ if matches:
990
+ code_lines = []
991
+ for match in matches:
992
+ code_lines.append(match.group(0))
993
+ examples.append(ExampleInfo(
994
+ code="\n".join(code_lines).strip(),
995
+ language="python",
996
+ ))
997
+ elif content.strip():
998
+ # Treat entire content as example
999
+ examples.append(ExampleInfo(
1000
+ code=content.strip(),
1001
+ language="python",
1002
+ ))
1003
+
1004
+ return examples
1005
+
1006
+ def _parse_notes(self, content: str) -> list[str]:
1007
+ """Parse notes section."""
1008
+ notes = []
1009
+ current_note: list[str] = []
1010
+
1011
+ for line in content.split("\n"):
1012
+ stripped = line.strip()
1013
+ if stripped.startswith("-") or stripped.startswith("*"):
1014
+ if current_note:
1015
+ notes.append(" ".join(current_note))
1016
+ current_note = [stripped.lstrip("-* ")]
1017
+ elif stripped:
1018
+ current_note.append(stripped)
1019
+
1020
+ if current_note:
1021
+ notes.append(" ".join(current_note))
1022
+
1023
+ return notes if notes else [content.strip()]
1024
+
1025
+ def _parse_see_also(self, content: str) -> list[str]:
1026
+ """Parse see also references."""
1027
+ refs = []
1028
+
1029
+ for line in content.split("\n"):
1030
+ stripped = line.strip()
1031
+ if stripped:
1032
+ # Remove list markers
1033
+ stripped = stripped.lstrip("-* ")
1034
+ if stripped:
1035
+ refs.append(stripped)
1036
+
1037
+ return refs