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,971 @@
1
+ """API information extraction from Python source code.
2
+
3
+ This module provides tools to extract API information from Python
4
+ modules, classes, and functions for documentation generation.
5
+
6
+ Key Features:
7
+ - AST-based extraction for accurate parsing
8
+ - Import analysis and dependency tracking
9
+ - Type annotation extraction
10
+ - Decorator recognition
11
+ - Source code context extraction
12
+
13
+ Example:
14
+ from truthound.docs.extractor import APIExtractor
15
+
16
+ extractor = APIExtractor()
17
+ module_info = extractor.extract_module("truthound.api")
18
+
19
+ for cls in module_info.classes:
20
+ print(f"Class: {cls.name}")
21
+ for method in cls.methods:
22
+ print(f" Method: {method.name}")
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import ast
28
+ import importlib
29
+ import importlib.util
30
+ import inspect
31
+ import os
32
+ import sys
33
+ from dataclasses import dataclass, field
34
+ from enum import Enum
35
+ from pathlib import Path
36
+ from typing import Any
37
+
38
+ from truthound.docs.parser import DocstringParser, ParsedDocstring
39
+
40
+
41
+ class Visibility(str, Enum):
42
+ """Visibility level of API elements."""
43
+
44
+ PUBLIC = "public"
45
+ PROTECTED = "protected" # Single underscore
46
+ PRIVATE = "private" # Double underscore
47
+
48
+
49
+ @dataclass
50
+ class SourceLocation:
51
+ """Source code location information.
52
+
53
+ Attributes:
54
+ file_path: Path to source file
55
+ line_start: Starting line number
56
+ line_end: Ending line number
57
+ column: Column offset
58
+ """
59
+
60
+ file_path: str = ""
61
+ line_start: int = 0
62
+ line_end: int = 0
63
+ column: int = 0
64
+
65
+ def to_dict(self) -> dict[str, Any]:
66
+ """Convert to dictionary."""
67
+ return {
68
+ "file_path": self.file_path,
69
+ "line_start": self.line_start,
70
+ "line_end": self.line_end,
71
+ "column": self.column,
72
+ }
73
+
74
+
75
+ @dataclass
76
+ class DecoratorInfo:
77
+ """Information about a decorator.
78
+
79
+ Attributes:
80
+ name: Decorator name
81
+ args: Positional arguments
82
+ kwargs: Keyword arguments
83
+ source: Original decorator source
84
+ """
85
+
86
+ name: str
87
+ args: list[str] = field(default_factory=list)
88
+ kwargs: dict[str, str] = field(default_factory=dict)
89
+ source: str = ""
90
+
91
+ def to_dict(self) -> dict[str, Any]:
92
+ """Convert to dictionary."""
93
+ return {
94
+ "name": self.name,
95
+ "args": self.args,
96
+ "kwargs": self.kwargs,
97
+ "source": self.source,
98
+ }
99
+
100
+
101
+ @dataclass
102
+ class ParameterInfo:
103
+ """Function parameter information.
104
+
105
+ Attributes:
106
+ name: Parameter name
107
+ type_annotation: Type annotation string
108
+ default: Default value string
109
+ kind: Parameter kind (positional, keyword, etc.)
110
+ """
111
+
112
+ name: str
113
+ type_annotation: str = ""
114
+ default: str | None = None
115
+ kind: str = "positional_or_keyword"
116
+
117
+ def to_dict(self) -> dict[str, Any]:
118
+ """Convert to dictionary."""
119
+ return {
120
+ "name": self.name,
121
+ "type_annotation": self.type_annotation,
122
+ "default": self.default,
123
+ "kind": self.kind,
124
+ }
125
+
126
+
127
+ @dataclass
128
+ class FunctionInfo:
129
+ """Information about a function or method.
130
+
131
+ Attributes:
132
+ name: Function name
133
+ qualified_name: Fully qualified name
134
+ signature: Function signature string
135
+ parameters: List of parameters
136
+ return_type: Return type annotation
137
+ docstring: Parsed docstring
138
+ decorators: List of decorators
139
+ is_async: Whether function is async
140
+ is_generator: Whether function is a generator
141
+ is_classmethod: Whether it's a classmethod
142
+ is_staticmethod: Whether it's a staticmethod
143
+ is_property: Whether it's a property
144
+ visibility: Visibility level
145
+ location: Source location
146
+ """
147
+
148
+ name: str
149
+ qualified_name: str = ""
150
+ signature: str = ""
151
+ parameters: list[ParameterInfo] = field(default_factory=list)
152
+ return_type: str = ""
153
+ docstring: ParsedDocstring = field(default_factory=ParsedDocstring)
154
+ decorators: list[DecoratorInfo] = field(default_factory=list)
155
+ is_async: bool = False
156
+ is_generator: bool = False
157
+ is_classmethod: bool = False
158
+ is_staticmethod: bool = False
159
+ is_property: bool = False
160
+ visibility: Visibility = Visibility.PUBLIC
161
+ location: SourceLocation = field(default_factory=SourceLocation)
162
+
163
+ def to_dict(self) -> dict[str, Any]:
164
+ """Convert to dictionary."""
165
+ return {
166
+ "name": self.name,
167
+ "qualified_name": self.qualified_name,
168
+ "signature": self.signature,
169
+ "parameters": [p.to_dict() for p in self.parameters],
170
+ "return_type": self.return_type,
171
+ "docstring": self.docstring.to_dict(),
172
+ "decorators": [d.to_dict() for d in self.decorators],
173
+ "is_async": self.is_async,
174
+ "is_generator": self.is_generator,
175
+ "is_classmethod": self.is_classmethod,
176
+ "is_staticmethod": self.is_staticmethod,
177
+ "is_property": self.is_property,
178
+ "visibility": self.visibility.value,
179
+ "location": self.location.to_dict(),
180
+ }
181
+
182
+
183
+ @dataclass
184
+ class ClassInfo:
185
+ """Information about a class.
186
+
187
+ Attributes:
188
+ name: Class name
189
+ qualified_name: Fully qualified name
190
+ bases: Base class names
191
+ docstring: Parsed docstring
192
+ methods: List of methods
193
+ class_methods: List of class methods
194
+ static_methods: List of static methods
195
+ properties: List of properties
196
+ attributes: List of class attributes
197
+ decorators: List of decorators
198
+ is_abstract: Whether class is abstract
199
+ is_dataclass: Whether class is a dataclass
200
+ is_enum: Whether class is an Enum
201
+ visibility: Visibility level
202
+ location: Source location
203
+ """
204
+
205
+ name: str
206
+ qualified_name: str = ""
207
+ bases: list[str] = field(default_factory=list)
208
+ docstring: ParsedDocstring = field(default_factory=ParsedDocstring)
209
+ methods: list[FunctionInfo] = field(default_factory=list)
210
+ class_methods: list[FunctionInfo] = field(default_factory=list)
211
+ static_methods: list[FunctionInfo] = field(default_factory=list)
212
+ properties: list[FunctionInfo] = field(default_factory=list)
213
+ attributes: list[dict[str, Any]] = field(default_factory=list)
214
+ decorators: list[DecoratorInfo] = field(default_factory=list)
215
+ is_abstract: bool = False
216
+ is_dataclass: bool = False
217
+ is_enum: bool = False
218
+ visibility: Visibility = Visibility.PUBLIC
219
+ location: SourceLocation = field(default_factory=SourceLocation)
220
+
221
+ def to_dict(self) -> dict[str, Any]:
222
+ """Convert to dictionary."""
223
+ return {
224
+ "name": self.name,
225
+ "qualified_name": self.qualified_name,
226
+ "bases": self.bases,
227
+ "docstring": self.docstring.to_dict(),
228
+ "methods": [m.to_dict() for m in self.methods],
229
+ "class_methods": [m.to_dict() for m in self.class_methods],
230
+ "static_methods": [m.to_dict() for m in self.static_methods],
231
+ "properties": [p.to_dict() for p in self.properties],
232
+ "attributes": self.attributes,
233
+ "decorators": [d.to_dict() for d in self.decorators],
234
+ "is_abstract": self.is_abstract,
235
+ "is_dataclass": self.is_dataclass,
236
+ "is_enum": self.is_enum,
237
+ "visibility": self.visibility.value,
238
+ "location": self.location.to_dict(),
239
+ }
240
+
241
+
242
+ @dataclass
243
+ class ModuleInfo:
244
+ """Information about a Python module.
245
+
246
+ Attributes:
247
+ name: Module name
248
+ qualified_name: Fully qualified module name
249
+ file_path: Path to module file
250
+ docstring: Parsed module docstring
251
+ classes: List of classes
252
+ functions: List of functions
253
+ constants: List of module-level constants
254
+ imports: List of imports
255
+ all_exports: Contents of __all__
256
+ submodules: List of submodule names
257
+ """
258
+
259
+ name: str
260
+ qualified_name: str = ""
261
+ file_path: str = ""
262
+ docstring: ParsedDocstring = field(default_factory=ParsedDocstring)
263
+ classes: list[ClassInfo] = field(default_factory=list)
264
+ functions: list[FunctionInfo] = field(default_factory=list)
265
+ constants: list[dict[str, Any]] = field(default_factory=list)
266
+ imports: list[dict[str, str]] = field(default_factory=list)
267
+ all_exports: list[str] = field(default_factory=list)
268
+ submodules: list[str] = field(default_factory=list)
269
+
270
+ def to_dict(self) -> dict[str, Any]:
271
+ """Convert to dictionary."""
272
+ return {
273
+ "name": self.name,
274
+ "qualified_name": self.qualified_name,
275
+ "file_path": self.file_path,
276
+ "docstring": self.docstring.to_dict(),
277
+ "classes": [c.to_dict() for c in self.classes],
278
+ "functions": [f.to_dict() for f in self.functions],
279
+ "constants": self.constants,
280
+ "imports": self.imports,
281
+ "all_exports": self.all_exports,
282
+ "submodules": self.submodules,
283
+ }
284
+
285
+
286
+ @dataclass
287
+ class PackageInfo:
288
+ """Information about a Python package.
289
+
290
+ Attributes:
291
+ name: Package name
292
+ root_path: Root directory path
293
+ modules: List of module information
294
+ version: Package version
295
+ description: Package description
296
+ """
297
+
298
+ name: str
299
+ root_path: str = ""
300
+ modules: list[ModuleInfo] = field(default_factory=list)
301
+ version: str = ""
302
+ description: str = ""
303
+
304
+ def to_dict(self) -> dict[str, Any]:
305
+ """Convert to dictionary."""
306
+ return {
307
+ "name": self.name,
308
+ "root_path": self.root_path,
309
+ "modules": [m.to_dict() for m in self.modules],
310
+ "version": self.version,
311
+ "description": self.description,
312
+ }
313
+
314
+
315
+ class APIExtractor:
316
+ """Extracts API information from Python source code.
317
+
318
+ Uses AST parsing combined with runtime introspection to extract
319
+ comprehensive API information for documentation.
320
+
321
+ Example:
322
+ extractor = APIExtractor()
323
+
324
+ # Extract single module
325
+ module_info = extractor.extract_module("truthound.api")
326
+
327
+ # Extract entire package
328
+ package_info = extractor.extract_package("src/truthound")
329
+
330
+ Attributes:
331
+ include_private: Whether to include private members
332
+ include_magic: Whether to include dunder methods
333
+ docstring_parser: Parser for docstrings
334
+ """
335
+
336
+ def __init__(
337
+ self,
338
+ include_private: bool = False,
339
+ include_magic: bool = False,
340
+ docstring_parser: DocstringParser | None = None,
341
+ ):
342
+ """Initialize extractor.
343
+
344
+ Args:
345
+ include_private: Include private (underscore) members
346
+ include_magic: Include magic (__dunder__) methods
347
+ docstring_parser: Custom docstring parser
348
+ """
349
+ self.include_private = include_private
350
+ self.include_magic = include_magic
351
+ self.docstring_parser = docstring_parser or DocstringParser()
352
+
353
+ def extract_module(
354
+ self,
355
+ module_name: str | None = None,
356
+ file_path: str | Path | None = None,
357
+ ) -> ModuleInfo:
358
+ """Extract API information from a module.
359
+
360
+ Args:
361
+ module_name: Fully qualified module name
362
+ file_path: Path to module file
363
+
364
+ Returns:
365
+ ModuleInfo with extracted information
366
+
367
+ Raises:
368
+ ValueError: If neither module_name nor file_path provided
369
+ ImportError: If module cannot be loaded
370
+ """
371
+ if not module_name and not file_path:
372
+ raise ValueError("Either module_name or file_path must be provided")
373
+
374
+ # Load module
375
+ if file_path:
376
+ file_path = Path(file_path)
377
+ module = self._load_module_from_file(file_path)
378
+ module_name = module.__name__
379
+ else:
380
+ module = importlib.import_module(module_name)
381
+ file_path = Path(inspect.getfile(module)) if hasattr(module, "__file__") else None
382
+
383
+ # Get source code
384
+ try:
385
+ source = inspect.getsource(module)
386
+ tree = ast.parse(source)
387
+ except (OSError, TypeError):
388
+ # Can't get source, use introspection only
389
+ return self._extract_module_introspection(module, module_name, file_path)
390
+
391
+ # Extract using AST
392
+ return self._extract_module_ast(
393
+ module,
394
+ tree,
395
+ module_name,
396
+ str(file_path) if file_path else "",
397
+ )
398
+
399
+ def extract_package(
400
+ self,
401
+ package_path: str | Path,
402
+ package_name: str | None = None,
403
+ ) -> PackageInfo:
404
+ """Extract API information from an entire package.
405
+
406
+ Args:
407
+ package_path: Path to package directory
408
+ package_name: Package name (inferred from path if not given)
409
+
410
+ Returns:
411
+ PackageInfo with all module information
412
+ """
413
+ package_path = Path(package_path)
414
+
415
+ if not package_path.is_dir():
416
+ raise ValueError(f"Package path must be a directory: {package_path}")
417
+
418
+ if package_name is None:
419
+ package_name = package_path.name
420
+
421
+ # Add package to sys.path temporarily
422
+ parent_path = str(package_path.parent)
423
+ if parent_path not in sys.path:
424
+ sys.path.insert(0, parent_path)
425
+
426
+ package_info = PackageInfo(
427
+ name=package_name,
428
+ root_path=str(package_path),
429
+ )
430
+
431
+ # Find all Python files
432
+ for py_file in package_path.rglob("*.py"):
433
+ # Skip test files
434
+ if "test" in py_file.parts:
435
+ continue
436
+
437
+ # Calculate module name
438
+ rel_path = py_file.relative_to(package_path)
439
+ parts = list(rel_path.parts)
440
+ parts[-1] = parts[-1].replace(".py", "")
441
+
442
+ if parts[-1] == "__init__":
443
+ parts = parts[:-1]
444
+
445
+ if parts:
446
+ module_name = f"{package_name}." + ".".join(parts)
447
+ else:
448
+ module_name = package_name
449
+
450
+ try:
451
+ module_info = self.extract_module(file_path=py_file)
452
+ module_info.qualified_name = module_name
453
+ package_info.modules.append(module_info)
454
+ except Exception as e:
455
+ # Log and continue
456
+ print(f"Warning: Failed to extract {py_file}: {e}")
457
+
458
+ # Extract package info from __init__.py
459
+ init_file = package_path / "__init__.py"
460
+ if init_file.exists():
461
+ try:
462
+ init_module = self.extract_module(file_path=init_file)
463
+ package_info.description = init_module.docstring.short_description
464
+ if hasattr(init_module, "all_exports"):
465
+ package_info.version = self._extract_version(init_file)
466
+ except Exception:
467
+ pass
468
+
469
+ return package_info
470
+
471
+ def _load_module_from_file(self, file_path: Path) -> Any:
472
+ """Load a module from file path."""
473
+ module_name = file_path.stem
474
+
475
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
476
+ if spec is None or spec.loader is None:
477
+ raise ImportError(f"Cannot load module from {file_path}")
478
+
479
+ module = importlib.util.module_from_spec(spec)
480
+ sys.modules[module_name] = module
481
+
482
+ try:
483
+ spec.loader.exec_module(module)
484
+ except Exception:
485
+ # Module might have import errors, still extract what we can
486
+ pass
487
+
488
+ return module
489
+
490
+ def _extract_module_ast(
491
+ self,
492
+ module: Any,
493
+ tree: ast.Module,
494
+ module_name: str,
495
+ file_path: str,
496
+ ) -> ModuleInfo:
497
+ """Extract module info using AST."""
498
+ info = ModuleInfo(
499
+ name=module_name.split(".")[-1],
500
+ qualified_name=module_name,
501
+ file_path=file_path,
502
+ )
503
+
504
+ # Extract module docstring
505
+ if ast.get_docstring(tree):
506
+ info.docstring = self.docstring_parser.parse(ast.get_docstring(tree))
507
+
508
+ # Extract __all__
509
+ for node in ast.iter_child_nodes(tree):
510
+ if isinstance(node, ast.Assign):
511
+ for target in node.targets:
512
+ if isinstance(target, ast.Name) and target.id == "__all__":
513
+ if isinstance(node.value, ast.List):
514
+ info.all_exports = [
515
+ elt.value
516
+ for elt in node.value.elts
517
+ if isinstance(elt, ast.Constant)
518
+ ]
519
+
520
+ # Extract imports
521
+ for node in ast.iter_child_nodes(tree):
522
+ if isinstance(node, ast.Import):
523
+ for alias in node.names:
524
+ info.imports.append({
525
+ "module": alias.name,
526
+ "alias": alias.asname or "",
527
+ })
528
+ elif isinstance(node, ast.ImportFrom):
529
+ module_from = node.module or ""
530
+ for alias in node.names:
531
+ info.imports.append({
532
+ "module": f"{module_from}.{alias.name}" if module_from else alias.name,
533
+ "from": module_from,
534
+ "name": alias.name,
535
+ "alias": alias.asname or "",
536
+ })
537
+
538
+ # Extract classes and functions
539
+ for node in ast.iter_child_nodes(tree):
540
+ if isinstance(node, ast.ClassDef):
541
+ if self._should_include(node.name):
542
+ class_info = self._extract_class_ast(node, module_name, file_path)
543
+ info.classes.append(class_info)
544
+
545
+ elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
546
+ if self._should_include(node.name):
547
+ func_info = self._extract_function_ast(node, module_name, file_path)
548
+ info.functions.append(func_info)
549
+
550
+ elif isinstance(node, ast.Assign):
551
+ # Module-level constants
552
+ for target in node.targets:
553
+ if isinstance(target, ast.Name) and target.id.isupper():
554
+ info.constants.append({
555
+ "name": target.id,
556
+ "value": ast.unparse(node.value) if hasattr(ast, "unparse") else "",
557
+ "line": node.lineno,
558
+ })
559
+
560
+ return info
561
+
562
+ def _extract_class_ast(
563
+ self,
564
+ node: ast.ClassDef,
565
+ parent_name: str,
566
+ file_path: str,
567
+ ) -> ClassInfo:
568
+ """Extract class info from AST node."""
569
+ qualified_name = f"{parent_name}.{node.name}"
570
+
571
+ info = ClassInfo(
572
+ name=node.name,
573
+ qualified_name=qualified_name,
574
+ visibility=self._get_visibility(node.name),
575
+ location=SourceLocation(
576
+ file_path=file_path,
577
+ line_start=node.lineno,
578
+ line_end=node.end_lineno or node.lineno,
579
+ column=node.col_offset,
580
+ ),
581
+ )
582
+
583
+ # Extract bases
584
+ info.bases = [self._unparse_node(base) for base in node.bases]
585
+
586
+ # Extract decorators
587
+ for decorator in node.decorator_list:
588
+ info.decorators.append(self._extract_decorator(decorator))
589
+
590
+ # Check for special decorators
591
+ dec_name = self._get_decorator_name(decorator)
592
+ if dec_name == "dataclass":
593
+ info.is_dataclass = True
594
+ elif dec_name in ("abstractmethod", "ABC"):
595
+ info.is_abstract = True
596
+
597
+ # Check if enum
598
+ for base in info.bases:
599
+ if "Enum" in base:
600
+ info.is_enum = True
601
+
602
+ # Extract docstring
603
+ if ast.get_docstring(node):
604
+ info.docstring = self.docstring_parser.parse(ast.get_docstring(node))
605
+
606
+ # Extract methods and attributes
607
+ for child in ast.iter_child_nodes(node):
608
+ if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
609
+ if not self._should_include(child.name):
610
+ continue
611
+
612
+ method_info = self._extract_function_ast(child, qualified_name, file_path)
613
+
614
+ # Classify method
615
+ if method_info.is_classmethod:
616
+ info.class_methods.append(method_info)
617
+ elif method_info.is_staticmethod:
618
+ info.static_methods.append(method_info)
619
+ elif method_info.is_property:
620
+ info.properties.append(method_info)
621
+ else:
622
+ info.methods.append(method_info)
623
+
624
+ elif isinstance(child, ast.AnnAssign) and isinstance(child.target, ast.Name):
625
+ # Class attribute with annotation
626
+ info.attributes.append({
627
+ "name": child.target.id,
628
+ "type": self._unparse_node(child.annotation) if child.annotation else "",
629
+ "value": self._unparse_node(child.value) if child.value else None,
630
+ })
631
+
632
+ return info
633
+
634
+ def _extract_function_ast(
635
+ self,
636
+ node: ast.FunctionDef | ast.AsyncFunctionDef,
637
+ parent_name: str,
638
+ file_path: str,
639
+ ) -> FunctionInfo:
640
+ """Extract function info from AST node."""
641
+ qualified_name = f"{parent_name}.{node.name}"
642
+
643
+ info = FunctionInfo(
644
+ name=node.name,
645
+ qualified_name=qualified_name,
646
+ is_async=isinstance(node, ast.AsyncFunctionDef),
647
+ visibility=self._get_visibility(node.name),
648
+ location=SourceLocation(
649
+ file_path=file_path,
650
+ line_start=node.lineno,
651
+ line_end=node.end_lineno or node.lineno,
652
+ column=node.col_offset,
653
+ ),
654
+ )
655
+
656
+ # Extract decorators
657
+ for decorator in node.decorator_list:
658
+ info.decorators.append(self._extract_decorator(decorator))
659
+
660
+ dec_name = self._get_decorator_name(decorator)
661
+ if dec_name == "classmethod":
662
+ info.is_classmethod = True
663
+ elif dec_name == "staticmethod":
664
+ info.is_staticmethod = True
665
+ elif dec_name == "property":
666
+ info.is_property = True
667
+
668
+ # Extract parameters
669
+ args = node.args
670
+
671
+ # Defaults are aligned to the end
672
+ defaults = [None] * (len(args.args) - len(args.defaults)) + list(args.defaults)
673
+
674
+ for i, arg in enumerate(args.args):
675
+ param = ParameterInfo(
676
+ name=arg.arg,
677
+ type_annotation=self._unparse_node(arg.annotation) if arg.annotation else "",
678
+ default=self._unparse_node(defaults[i]) if defaults[i] else None,
679
+ kind="positional_or_keyword",
680
+ )
681
+ info.parameters.append(param)
682
+
683
+ # *args
684
+ if args.vararg:
685
+ info.parameters.append(ParameterInfo(
686
+ name=f"*{args.vararg.arg}",
687
+ type_annotation=self._unparse_node(args.vararg.annotation) if args.vararg.annotation else "",
688
+ kind="var_positional",
689
+ ))
690
+
691
+ # Keyword-only args
692
+ kw_defaults = list(args.kw_defaults)
693
+ for i, arg in enumerate(args.kwonlyargs):
694
+ info.parameters.append(ParameterInfo(
695
+ name=arg.arg,
696
+ type_annotation=self._unparse_node(arg.annotation) if arg.annotation else "",
697
+ default=self._unparse_node(kw_defaults[i]) if kw_defaults[i] else None,
698
+ kind="keyword_only",
699
+ ))
700
+
701
+ # **kwargs
702
+ if args.kwarg:
703
+ info.parameters.append(ParameterInfo(
704
+ name=f"**{args.kwarg.arg}",
705
+ type_annotation=self._unparse_node(args.kwarg.annotation) if args.kwarg.annotation else "",
706
+ kind="var_keyword",
707
+ ))
708
+
709
+ # Return type
710
+ if node.returns:
711
+ info.return_type = self._unparse_node(node.returns)
712
+
713
+ # Build signature
714
+ info.signature = self._build_signature(info)
715
+
716
+ # Check for generator
717
+ for child in ast.walk(node):
718
+ if isinstance(child, (ast.Yield, ast.YieldFrom)):
719
+ info.is_generator = True
720
+ break
721
+
722
+ # Extract docstring
723
+ if ast.get_docstring(node):
724
+ info.docstring = self.docstring_parser.parse(ast.get_docstring(node))
725
+
726
+ return info
727
+
728
+ def _extract_decorator(self, node: ast.expr) -> DecoratorInfo:
729
+ """Extract decorator information."""
730
+ if isinstance(node, ast.Name):
731
+ return DecoratorInfo(name=node.id, source=node.id)
732
+
733
+ elif isinstance(node, ast.Attribute):
734
+ source = self._unparse_node(node)
735
+ return DecoratorInfo(name=node.attr, source=source)
736
+
737
+ elif isinstance(node, ast.Call):
738
+ name = self._get_decorator_name(node)
739
+ args = [self._unparse_node(arg) for arg in node.args]
740
+ kwargs = {
741
+ kw.arg: self._unparse_node(kw.value)
742
+ for kw in node.keywords
743
+ if kw.arg
744
+ }
745
+ source = self._unparse_node(node)
746
+ return DecoratorInfo(name=name, args=args, kwargs=kwargs, source=source)
747
+
748
+ return DecoratorInfo(name="unknown", source=self._unparse_node(node))
749
+
750
+ def _get_decorator_name(self, node: ast.expr) -> str:
751
+ """Get decorator name from AST node."""
752
+ if isinstance(node, ast.Name):
753
+ return node.id
754
+ elif isinstance(node, ast.Attribute):
755
+ return node.attr
756
+ elif isinstance(node, ast.Call):
757
+ return self._get_decorator_name(node.func)
758
+ return ""
759
+
760
+ def _build_signature(self, func_info: FunctionInfo) -> str:
761
+ """Build function signature string."""
762
+ parts = []
763
+
764
+ for param in func_info.parameters:
765
+ part = param.name
766
+ if param.type_annotation:
767
+ part += f": {param.type_annotation}"
768
+ if param.default is not None:
769
+ part += f" = {param.default}"
770
+ parts.append(part)
771
+
772
+ sig = f"({', '.join(parts)})"
773
+
774
+ if func_info.return_type:
775
+ sig += f" -> {func_info.return_type}"
776
+
777
+ return sig
778
+
779
+ def _unparse_node(self, node: ast.expr | None) -> str:
780
+ """Convert AST node back to source code."""
781
+ if node is None:
782
+ return ""
783
+
784
+ if hasattr(ast, "unparse"):
785
+ return ast.unparse(node)
786
+
787
+ # Fallback for older Python
788
+ if isinstance(node, ast.Name):
789
+ return node.id
790
+ elif isinstance(node, ast.Constant):
791
+ return repr(node.value)
792
+ elif isinstance(node, ast.Attribute):
793
+ return f"{self._unparse_node(node.value)}.{node.attr}"
794
+ elif isinstance(node, ast.Subscript):
795
+ return f"{self._unparse_node(node.value)}[{self._unparse_node(node.slice)}]"
796
+ elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
797
+ return f"{self._unparse_node(node.left)} | {self._unparse_node(node.right)}"
798
+ elif isinstance(node, ast.List):
799
+ items = ", ".join(self._unparse_node(elt) for elt in node.elts)
800
+ return f"[{items}]"
801
+ elif isinstance(node, ast.Tuple):
802
+ items = ", ".join(self._unparse_node(elt) for elt in node.elts)
803
+ return f"({items})"
804
+ elif isinstance(node, ast.Dict):
805
+ pairs = [
806
+ f"{self._unparse_node(k)}: {self._unparse_node(v)}"
807
+ for k, v in zip(node.keys, node.values)
808
+ if k is not None
809
+ ]
810
+ return "{" + ", ".join(pairs) + "}"
811
+
812
+ return str(node)
813
+
814
+ def _get_visibility(self, name: str) -> Visibility:
815
+ """Determine visibility from name."""
816
+ if name.startswith("__") and not name.endswith("__"):
817
+ return Visibility.PRIVATE
818
+ elif name.startswith("_"):
819
+ return Visibility.PROTECTED
820
+ return Visibility.PUBLIC
821
+
822
+ def _should_include(self, name: str) -> bool:
823
+ """Check if member should be included based on settings."""
824
+ if name.startswith("__"):
825
+ if name.endswith("__"):
826
+ return self.include_magic
827
+ return self.include_private
828
+ elif name.startswith("_"):
829
+ return self.include_private
830
+ return True
831
+
832
+ def _extract_module_introspection(
833
+ self,
834
+ module: Any,
835
+ module_name: str,
836
+ file_path: Path | None,
837
+ ) -> ModuleInfo:
838
+ """Extract module info using runtime introspection."""
839
+ info = ModuleInfo(
840
+ name=module_name.split(".")[-1],
841
+ qualified_name=module_name,
842
+ file_path=str(file_path) if file_path else "",
843
+ )
844
+
845
+ # Get docstring
846
+ if module.__doc__:
847
+ info.docstring = self.docstring_parser.parse(module.__doc__)
848
+
849
+ # Get __all__
850
+ if hasattr(module, "__all__"):
851
+ info.all_exports = list(module.__all__)
852
+
853
+ # Iterate members
854
+ for name, obj in inspect.getmembers(module):
855
+ if not self._should_include(name):
856
+ continue
857
+
858
+ if inspect.isclass(obj) and obj.__module__ == module_name:
859
+ info.classes.append(self._extract_class_introspection(obj, module_name))
860
+
861
+ elif inspect.isfunction(obj) and obj.__module__ == module_name:
862
+ info.functions.append(self._extract_function_introspection(obj, module_name))
863
+
864
+ return info
865
+
866
+ def _extract_class_introspection(self, cls: type, parent_name: str) -> ClassInfo:
867
+ """Extract class info using introspection."""
868
+ info = ClassInfo(
869
+ name=cls.__name__,
870
+ qualified_name=f"{parent_name}.{cls.__name__}",
871
+ bases=[base.__name__ for base in cls.__bases__ if base is not object],
872
+ visibility=self._get_visibility(cls.__name__),
873
+ )
874
+
875
+ if cls.__doc__:
876
+ info.docstring = self.docstring_parser.parse(cls.__doc__)
877
+
878
+ for name, obj in inspect.getmembers(cls):
879
+ if not self._should_include(name):
880
+ continue
881
+
882
+ if isinstance(obj, property):
883
+ info.properties.append(FunctionInfo(
884
+ name=name,
885
+ is_property=True,
886
+ docstring=self.docstring_parser.parse(obj.fget.__doc__) if obj.fget else ParsedDocstring(),
887
+ ))
888
+
889
+ elif inspect.ismethod(obj) or inspect.isfunction(obj):
890
+ method_info = self._extract_function_introspection(obj, info.qualified_name)
891
+ info.methods.append(method_info)
892
+
893
+ return info
894
+
895
+ def _extract_function_introspection(self, func: Any, parent_name: str) -> FunctionInfo:
896
+ """Extract function info using introspection."""
897
+ info = FunctionInfo(
898
+ name=func.__name__,
899
+ qualified_name=f"{parent_name}.{func.__name__}",
900
+ visibility=self._get_visibility(func.__name__),
901
+ )
902
+
903
+ # Get signature
904
+ try:
905
+ sig = inspect.signature(func)
906
+ info.signature = str(sig)
907
+
908
+ for param_name, param in sig.parameters.items():
909
+ param_info = ParameterInfo(
910
+ name=param_name,
911
+ kind=param.kind.name.lower(),
912
+ )
913
+
914
+ if param.annotation != inspect.Parameter.empty:
915
+ param_info.type_annotation = str(param.annotation)
916
+
917
+ if param.default != inspect.Parameter.empty:
918
+ param_info.default = repr(param.default)
919
+
920
+ info.parameters.append(param_info)
921
+
922
+ if sig.return_annotation != inspect.Signature.empty:
923
+ info.return_type = str(sig.return_annotation)
924
+
925
+ except (ValueError, TypeError):
926
+ pass
927
+
928
+ if func.__doc__:
929
+ info.docstring = self.docstring_parser.parse(func.__doc__)
930
+
931
+ info.is_async = inspect.iscoroutinefunction(func)
932
+ info.is_generator = inspect.isgeneratorfunction(func)
933
+
934
+ return info
935
+
936
+ def _extract_version(self, init_file: Path) -> str:
937
+ """Try to extract version from __init__.py."""
938
+ try:
939
+ content = init_file.read_text()
940
+ match = re.search(r'__version__\s*=\s*["\']([^"\']+)["\']', content)
941
+ if match:
942
+ return match.group(1)
943
+ except Exception:
944
+ pass
945
+ return ""
946
+
947
+
948
+ # Convenience function
949
+ def extract_api(
950
+ source: str | Path,
951
+ **kwargs: Any,
952
+ ) -> ModuleInfo | PackageInfo:
953
+ """Extract API information from a module or package.
954
+
955
+ Args:
956
+ source: Module name, file path, or package directory
957
+ **kwargs: Options passed to APIExtractor
958
+
959
+ Returns:
960
+ ModuleInfo for single modules, PackageInfo for packages
961
+ """
962
+ extractor = APIExtractor(**kwargs)
963
+ source_path = Path(source) if not isinstance(source, Path) else source
964
+
965
+ if source_path.is_dir():
966
+ return extractor.extract_package(source_path)
967
+ elif source_path.is_file():
968
+ return extractor.extract_module(file_path=source_path)
969
+ else:
970
+ # Assume it's a module name
971
+ return extractor.extract_module(module_name=str(source))