duckstring 0.4.0__tar.gz → 0.6.0__tar.gz

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 (301) hide show
  1. duckstring-0.6.0/CHANGELOG.md +197 -0
  2. {duckstring-0.4.0 → duckstring-0.6.0}/MANIFEST.in +2 -0
  3. {duckstring-0.4.0/src/duckstring.egg-info → duckstring-0.6.0}/PKG-INFO +94 -52
  4. duckstring-0.6.0/README.md +111 -0
  5. duckstring-0.6.0/pyproject.toml +106 -0
  6. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/__init__.py +0 -2
  7. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/base.py +22 -4
  8. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/email.py +9 -1
  9. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/event.py +14 -3
  10. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/webhook.py +4 -1
  11. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/app.py +72 -5
  12. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/auth.py +54 -11
  13. duckstring-0.6.0/src/duckstring/catchment/cloud.py +156 -0
  14. duckstring-0.6.0/src/duckstring/catchment/cloud_backends.py +156 -0
  15. duckstring-0.6.0/src/duckstring/catchment/cloud_deploy.py +77 -0
  16. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/data_lease.py +5 -6
  17. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/db.py +20 -1
  18. duckstring-0.6.0/src/duckstring/catchment/dialback.py +41 -0
  19. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/driver.py +1599 -79
  20. duckstring-0.6.0/src/duckstring/catchment/ec2_launcher.py +390 -0
  21. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/egress_worker.py +33 -9
  22. duckstring-0.6.0/src/duckstring/catchment/fargate_launcher.py +304 -0
  23. duckstring-0.6.0/src/duckstring/catchment/flight_sql.py +98 -0
  24. duckstring-0.6.0/src/duckstring/catchment/launcher.py +342 -0
  25. duckstring-0.6.0/src/duckstring/catchment/pg_wire.py +317 -0
  26. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/poller.py +2 -1
  27. duckstring-0.6.0/src/duckstring/catchment/pool_launcher.py +391 -0
  28. duckstring-0.6.0/src/duckstring/catchment/registry.py +85 -0
  29. duckstring-0.6.0/src/duckstring/catchment/relay.py +217 -0
  30. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/__init__.py +4 -0
  31. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/alerts.py +3 -1
  32. duckstring-0.6.0/src/duckstring/catchment/routes/catchment.py +497 -0
  33. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/data.py +203 -42
  34. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/deploy.py +151 -50
  35. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/draw.py +24 -3
  36. duckstring-0.6.0/src/duckstring/catchment/routes/duck.py +68 -0
  37. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/duct.py +22 -2
  38. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/metrics.py +36 -0
  39. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/orchestrate.py +85 -3
  40. duckstring-0.6.0/src/duckstring/catchment/routes/pool.py +38 -0
  41. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/secrets.py +17 -1
  42. duckstring-0.6.0/src/duckstring/catchment/routes/serving.py +104 -0
  43. duckstring-0.6.0/src/duckstring/catchment/schema/017_alert_renotify.sql +7 -0
  44. duckstring-0.6.0/src/duckstring/catchment/schema/018_duck.sql +11 -0
  45. duckstring-0.6.0/src/duckstring/catchment/schema/019_lineage.sql +18 -0
  46. duckstring-0.6.0/src/duckstring/catchment/schema/020_column_lineage.sql +15 -0
  47. duckstring-0.6.0/src/duckstring/catchment/schema/021_cloud.sql +44 -0
  48. duckstring-0.6.0/src/duckstring/catchment/schema/022_retire_duck_size.sql +25 -0
  49. duckstring-0.6.0/src/duckstring/catchment/schema/023_pool_provider.sql +7 -0
  50. duckstring-0.6.0/src/duckstring/catchment/schema/024_serving.sql +29 -0
  51. duckstring-0.6.0/src/duckstring/catchment/schema/025_dbt.sql +3 -0
  52. duckstring-0.6.0/src/duckstring/catchment/schema/026_deploy_config.sql +7 -0
  53. duckstring-0.6.0/src/duckstring/catchment/schema/027_persist.sql +14 -0
  54. duckstring-0.6.0/src/duckstring/catchment/schema/028_persist_timing.sql +3 -0
  55. duckstring-0.6.0/src/duckstring/catchment/schema/029_source_pins.sql +5 -0
  56. duckstring-0.6.0/src/duckstring/catchment/serving.py +208 -0
  57. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/404.html +1 -1
  58. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next.__PAGE__.txt +2 -2
  59. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next._full.txt +3 -3
  60. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next._head.txt +1 -1
  61. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next._index.txt +2 -2
  62. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next._tree.txt +2 -2
  63. duckstring-0.6.0/src/duckstring/catchment/static/_next/static/chunks/09y-jd_5pt5my.js +2 -0
  64. duckstring-0.6.0/src/duckstring/catchment/static/_next/static/chunks/0_8xp0_1~w7l7.css +1 -0
  65. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._full.txt +2 -2
  66. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._head.txt +1 -1
  67. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._index.txt +2 -2
  68. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  69. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._not-found.txt +1 -1
  70. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._tree.txt +2 -2
  71. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found.html +1 -1
  72. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found.txt +2 -2
  73. duckstring-0.6.0/src/duckstring/catchment/static/index.html +1 -0
  74. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/index.txt +3 -3
  75. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/__init__.py +22 -3
  76. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/_http.py +8 -0
  77. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/alert.py +23 -17
  78. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/bulk.py +22 -18
  79. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/catchment.py +108 -45
  80. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/control.py +65 -25
  81. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/data.py +17 -10
  82. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/deploy.py +85 -34
  83. duckstring-0.6.0/src/duckstring/cli/duck.py +194 -0
  84. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/duct.py +16 -15
  85. duckstring-0.6.0/src/duckstring/cli/lineage.py +111 -0
  86. duckstring-0.6.0/src/duckstring/cli/pond.py +334 -0
  87. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/puddle.py +12 -4
  88. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/secret.py +5 -5
  89. duckstring-0.6.0/src/duckstring/cli/serve.py +97 -0
  90. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/spout.py +19 -17
  91. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/status.py +2 -2
  92. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/trigger.py +24 -15
  93. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/window.py +12 -10
  94. duckstring-0.6.0/src/duckstring/core.py +1123 -0
  95. duckstring-0.6.0/src/duckstring/dataplane.py +1284 -0
  96. duckstring-0.6.0/src/duckstring/dbt_mode.py +149 -0
  97. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/catalog/src/pond.py +4 -1
  98. duckstring-0.6.0/src/duckstring/demo/gh_actors/README.md +10 -0
  99. duckstring-0.6.0/src/duckstring/demo/gh_actors/pond.toml +6 -0
  100. duckstring-0.6.0/src/duckstring/demo/gh_actors/src/pond.py +25 -0
  101. duckstring-0.6.0/src/duckstring/demo/gh_events/README.md +21 -0
  102. duckstring-0.6.0/src/duckstring/demo/gh_events/pond.toml +4 -0
  103. duckstring-0.6.0/src/duckstring/demo/gh_events/src/pond.py +109 -0
  104. duckstring-0.6.0/src/duckstring/demo/gh_pushes/README.md +10 -0
  105. duckstring-0.6.0/src/duckstring/demo/gh_pushes/pond.toml +7 -0
  106. duckstring-0.6.0/src/duckstring/demo/gh_pushes/src/pond.py +23 -0
  107. duckstring-0.6.0/src/duckstring/demo/gh_repo_activity/README.md +14 -0
  108. duckstring-0.6.0/src/duckstring/demo/gh_repo_activity/pond.toml +7 -0
  109. duckstring-0.6.0/src/duckstring/demo/gh_repo_activity/src/pond.py +24 -0
  110. duckstring-0.6.0/src/duckstring/demo/gh_stars/README.md +10 -0
  111. duckstring-0.6.0/src/duckstring/demo/gh_stars/pond.toml +6 -0
  112. duckstring-0.6.0/src/duckstring/demo/gh_stars/src/pond.py +19 -0
  113. duckstring-0.6.0/src/duckstring/demo/gh_trending/README.md +10 -0
  114. duckstring-0.6.0/src/duckstring/demo/gh_trending/pond.toml +7 -0
  115. duckstring-0.6.0/src/duckstring/demo/gh_trending/src/pond.py +23 -0
  116. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/src/pond.py +1 -1
  117. duckstring-0.6.0/src/duckstring/demo/shop_analytics/.gitignore +8 -0
  118. duckstring-0.6.0/src/duckstring/demo/shop_analytics/README.md +24 -0
  119. duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/dbt_project.yml +12 -0
  120. duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/models/orders_clean.sql +8 -0
  121. duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/models/revenue_by_product.sql +8 -0
  122. duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/models/sources.yml +9 -0
  123. duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/models/top_products.sql +8 -0
  124. duckstring-0.6.0/src/duckstring/demo/shop_analytics/pond.toml +11 -0
  125. duckstring-0.6.0/src/duckstring/demo/shop_orders/.gitignore +3 -0
  126. duckstring-0.6.0/src/duckstring/demo/shop_orders/README.md +9 -0
  127. duckstring-0.6.0/src/duckstring/demo/shop_orders/pond.toml +4 -0
  128. duckstring-0.6.0/src/duckstring/demo/shop_orders/src/pond.py +32 -0
  129. duckstring-0.6.0/src/duckstring/demo/sql_reports/README.md +11 -0
  130. duckstring-0.6.0/src/duckstring/demo/sql_reports/pond.toml +11 -0
  131. duckstring-0.6.0/src/duckstring/demo/sql_reports/sql/monthly_summary.sql +11 -0
  132. duckstring-0.6.0/src/duckstring/demo/sql_sales/README.md +11 -0
  133. duckstring-0.6.0/src/duckstring/demo/sql_sales/data/tier_bands.csv +4 -0
  134. duckstring-0.6.0/src/duckstring/demo/sql_sales/pond.toml +22 -0
  135. duckstring-0.6.0/src/duckstring/demo/sql_sales/sql/daily_sales.sql +9 -0
  136. duckstring-0.6.0/src/duckstring/demo/sql_sales/sql/price_tiers.sql +9 -0
  137. duckstring-0.6.0/src/duckstring/demo/sql_sales/sql/sale_line.sql +12 -0
  138. duckstring-0.6.0/src/duckstring/demo/sql_sales/src/puddles.py +47 -0
  139. duckstring-0.6.0/src/duckstring/demo/tpcds_category_revenue/README.md +13 -0
  140. duckstring-0.6.0/src/duckstring/demo/tpcds_category_revenue/pond.toml +7 -0
  141. duckstring-0.6.0/src/duckstring/demo/tpcds_category_revenue/src/pond.py +26 -0
  142. duckstring-0.6.0/src/duckstring/demo/tpcds_items/README.md +11 -0
  143. duckstring-0.6.0/src/duckstring/demo/tpcds_items/pond.toml +4 -0
  144. duckstring-0.6.0/src/duckstring/demo/tpcds_items/src/pond.py +80 -0
  145. duckstring-0.6.0/src/duckstring/demo/tpcds_priced/README.md +11 -0
  146. duckstring-0.6.0/src/duckstring/demo/tpcds_priced/pond.toml +8 -0
  147. duckstring-0.6.0/src/duckstring/demo/tpcds_priced/src/pond.py +26 -0
  148. duckstring-0.6.0/src/duckstring/demo/tpcds_sales/README.md +22 -0
  149. duckstring-0.6.0/src/duckstring/demo/tpcds_sales/pond.toml +4 -0
  150. duckstring-0.6.0/src/duckstring/demo/tpcds_sales/src/pond.py +76 -0
  151. duckstring-0.6.0/src/duckstring/demo/tpcds_store_revenue/README.md +10 -0
  152. duckstring-0.6.0/src/duckstring/demo/tpcds_store_revenue/pond.toml +7 -0
  153. duckstring-0.6.0/src/duckstring/demo/tpcds_store_revenue/src/pond.py +25 -0
  154. duckstring-0.6.0/src/duckstring/demo/tpcds_stores/README.md +10 -0
  155. duckstring-0.6.0/src/duckstring/demo/tpcds_stores/pond.toml +4 -0
  156. duckstring-0.6.0/src/duckstring/demo/tpcds_stores/src/pond.py +48 -0
  157. duckstring-0.6.0/src/duckstring/discover.py +170 -0
  158. duckstring-0.6.0/src/duckstring/duck/__main__.py +420 -0
  159. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/duck/client.py +15 -0
  160. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/duck/core.py +19 -3
  161. duckstring-0.6.0/src/duckstring/duck/dbt_executor.py +174 -0
  162. duckstring-0.6.0/src/duckstring/duck/executor.py +393 -0
  163. duckstring-0.6.0/src/duckstring/duck/pool_agent.py +171 -0
  164. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/base.py +6 -1
  165. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/credentials.py +13 -0
  166. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/destination.py +21 -3
  167. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/object_store.py +116 -14
  168. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/postgres.py +1 -1
  169. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/__init__.py +2 -0
  170. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/catchment.py +33 -4
  171. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/core.py +14 -0
  172. duckstring-0.6.0/src/duckstring/environments.py +245 -0
  173. duckstring-0.6.0/src/duckstring/flock/__init__.py +349 -0
  174. duckstring-0.6.0/src/duckstring/flock/engines/__init__.py +0 -0
  175. duckstring-0.6.0/src/duckstring/flock/engines/athena.py +298 -0
  176. duckstring-0.6.0/src/duckstring/flock/equivalence.py +75 -0
  177. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/local/hydrate.py +11 -5
  178. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/local/runner.py +18 -41
  179. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/objects.py +5 -2
  180. duckstring-0.6.0/src/duckstring/pondignore.py +79 -0
  181. duckstring-0.6.0/src/duckstring/schema_contract.py +148 -0
  182. duckstring-0.6.0/src/duckstring/sql_ripples.py +420 -0
  183. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/storage.py +134 -70
  184. duckstring-0.6.0/src/duckstring/trickle/acc.py +122 -0
  185. duckstring-0.6.0/src/duckstring/trickle/agg.py +184 -0
  186. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle/builder.py +182 -97
  187. duckstring-0.6.0/src/duckstring/trickle/capture.py +363 -0
  188. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle/io.py +221 -50
  189. duckstring-0.6.0/src/duckstring/trickle/lineage.py +305 -0
  190. {duckstring-0.4.0 → duckstring-0.6.0/src/duckstring.egg-info}/PKG-INFO +94 -52
  191. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/SOURCES.txt +107 -9
  192. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/requires.txt +18 -3
  193. duckstring-0.4.0/README.md +0 -82
  194. duckstring-0.4.0/pyproject.toml +0 -78
  195. duckstring-0.4.0/src/duckstring/catchment/launcher.py +0 -103
  196. duckstring-0.4.0/src/duckstring/catchment/registry.py +0 -40
  197. duckstring-0.4.0/src/duckstring/catchment/routes/catchment.py +0 -161
  198. duckstring-0.4.0/src/duckstring/catchment/routes/duck.py +0 -32
  199. duckstring-0.4.0/src/duckstring/catchment/static/_next/static/chunks/0p30fzde51na5.css +0 -1
  200. duckstring-0.4.0/src/duckstring/catchment/static/_next/static/chunks/0qdnkaffkg.2-.js +0 -2
  201. duckstring-0.4.0/src/duckstring/catchment/static/index.html +0 -1
  202. duckstring-0.4.0/src/duckstring/cli/pond.py +0 -234
  203. duckstring-0.4.0/src/duckstring/core.py +0 -678
  204. duckstring-0.4.0/src/duckstring/dataplane.py +0 -563
  205. duckstring-0.4.0/src/duckstring/duck/__main__.py +0 -193
  206. duckstring-0.4.0/src/duckstring/duck/executor.py +0 -208
  207. duckstring-0.4.0/src/duckstring/iceberg_catalog.py +0 -227
  208. duckstring-0.4.0/src/duckstring/iceberg_plane.py +0 -286
  209. duckstring-0.4.0/src/duckstring/schema_contract.py +0 -67
  210. duckstring-0.4.0/src/duckstring/trickle/acc.py +0 -139
  211. duckstring-0.4.0/src/duckstring/trickle/agg.py +0 -209
  212. duckstring-0.4.0/src/duckstring/utils.py +0 -3
  213. {duckstring-0.4.0 → duckstring-0.6.0}/LICENSE +0 -0
  214. {duckstring-0.4.0 → duckstring-0.6.0}/setup.cfg +0 -0
  215. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/__main__.py +0 -0
  216. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/acc.py +0 -0
  217. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/agg.py +0 -0
  218. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/__init__.py +0 -0
  219. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/__init__.py +0 -0
  220. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/alert_worker.py +0 -0
  221. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/asgi.py +0 -0
  222. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/dag.py +0 -0
  223. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/view.py +0 -0
  224. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/001_init.sql +0 -0
  225. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/002_ducts.sql +0 -0
  226. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/003_pull_m.sql +0 -0
  227. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/004_identity.sql +0 -0
  228. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/005_pond_version_schema.sql +0 -0
  229. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/006_refresh.sql +0 -0
  230. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/007_catchment_key.sql +0 -0
  231. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/008_spout.sql +0 -0
  232. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/009_spout_state.sql +0 -0
  233. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/010_spout_wake.sql +0 -0
  234. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/011_spout_window.sql +0 -0
  235. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/012_spout_node.sql +0 -0
  236. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/013_changed_f.sql +0 -0
  237. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/014_alert.sql +0 -0
  238. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/016_alert_scope_major.sql +0 -0
  239. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/secrets.py +0 -0
  240. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/state_sync.py +0 -0
  241. {duckstring-0.4.0/src/duckstring/catchment/static/_next/static/K01gZbd5BTz315upsZeJJ → duckstring-0.6.0/src/duckstring/catchment/static/_next/static/IexTU6uXUggN4m637L3i4}/_buildManifest.js +0 -0
  242. {duckstring-0.4.0/src/duckstring/catchment/static/_next/static/K01gZbd5BTz315upsZeJJ → duckstring-0.6.0/src/duckstring/catchment/static/_next/static/IexTU6uXUggN4m637L3i4}/_clientMiddlewareManifest.js +0 -0
  243. {duckstring-0.4.0/src/duckstring/catchment/static/_next/static/K01gZbd5BTz315upsZeJJ → duckstring-0.6.0/src/duckstring/catchment/static/_next/static/IexTU6uXUggN4m637L3i4}/_ssgManifest.js +0 -0
  244. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/03~yq9q893hmn.js +0 -0
  245. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/07lhk_q6pmm3r.js +0 -0
  246. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/0dbhjjzl8qfwv.js +0 -0
  247. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/0jvmviuftg5e2.css +0 -0
  248. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/0kczw6usu-y-f.js +0 -0
  249. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/11kjahy2ntf0n.js +0 -0
  250. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/turbopack-03~mbvk_uplk_.js +0 -0
  251. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/favicon.svg +0 -0
  252. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/logo-mark.svg +0 -0
  253. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/logo.svg +0 -0
  254. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/config.py +0 -0
  255. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/catalog/.gitignore +0 -0
  256. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/catalog/README.md +0 -0
  257. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/catalog/pond.toml +0 -0
  258. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/orders/.gitignore +0 -0
  259. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/orders/README.md +0 -0
  260. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/orders/pond.toml +0 -0
  261. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/orders/src/pond.py +0 -0
  262. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/.gitignore +0 -0
  263. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/README.md +0 -0
  264. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/pond.toml +0 -0
  265. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/src/puddles.py +0 -0
  266. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/products/.gitignore +0 -0
  267. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/products/README.md +0 -0
  268. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/products/pond.toml +0 -0
  269. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/products/src/pond.py +0 -0
  270. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/reports/.gitignore +0 -0
  271. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/reports/README.md +0 -0
  272. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/reports/pond.toml +0 -0
  273. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/reports/src/pond.py +0 -0
  274. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/.gitignore +0 -0
  275. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/README.md +0 -0
  276. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/pond.toml +0 -0
  277. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/src/pond.py +0 -0
  278. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/src/puddles.py +0 -0
  279. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/.gitignore +0 -0
  280. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/README.md +0 -0
  281. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/pond.toml +0 -0
  282. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/src/pond.py +0 -0
  283. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/src/puddles.py +0 -0
  284. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/transactions/.gitignore +0 -0
  285. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/transactions/README.md +0 -0
  286. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/transactions/pond.toml +0 -0
  287. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/transactions/src/pond.py +0 -0
  288. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/duck/__init__.py +0 -0
  289. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/__init__.py +0 -0
  290. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/pond.py +0 -0
  291. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/worker.py +0 -0
  292. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/keys.py +0 -0
  293. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/local/__init__.py +0 -0
  294. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/local/project.py +0 -0
  295. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle/__init__.py +0 -0
  296. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle/context.py +0 -0
  297. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle_builder.py +0 -0
  298. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle_io.py +0 -0
  299. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/dependency_links.txt +0 -0
  300. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/entry_points.txt +0 -0
  301. {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/top_level.txt +0 -0
@@ -0,0 +1,197 @@
1
+ # Changelog
2
+
3
+ Notable changes per release. Versions before 0.5.0 are recorded in the git history and the `v*` tags.
4
+
5
+ ## 0.6.0 — 2026-10-05
6
+
7
+ SQL Ripples, clustering, Pond environments and a simpler data plane. The Iceberg plane is gone: every
8
+ Catchment now publishes versioned Parquet, which pins each run's Source reads and costs a third of the
9
+ export time. See "Upgrading" at the end.
10
+
11
+ ### Building Ponds
12
+
13
+ - **SQL Ripples**: a Ripple can be one `SELECT` in a file, declared in `pond.toml` under
14
+ `[ripples.NAME]` with its `parents`, the Source tables it `reads`, and how it writes (`overwrite`,
15
+ `merge` or `append`). Nothing is inferred from the SQL; each query is checked against its declaration
16
+ at deploy, so a forgotten parent is refused instead of silently reading the previous run's table. SQL
17
+ and Python Ripples mix freely (`@ripple(parents=...)` now takes names), and column lineage is
18
+ recorded for SQL Ripples too. `duckstring pond demo --sql` shows the default pipeline in SQL.
19
+ - **Static tables**: files shipped with a Pond's code, declared under `[static.NAME]`, are read-only
20
+ tables every Ripple can query.
21
+ - **Pond environments**: a Pond with a `pyproject.toml` and `uv.lock` runs in its own environment, built
22
+ at deploy on the Catchment and by cloud and Pool Ducks on their own machines. A missing or stale lock,
23
+ or code that won't import, now refuses the deploy with the reason (an import error used to register no
24
+ Ripples).
25
+ - **`.pondignore`**: chooses which files a deploy uploads; by default `puddles/`, `.env` files, hidden
26
+ directories and caches stay behind.
27
+ - Names containing dots can be referenced with backticks (``sales.`daily.v2` ``).
28
+ - A merge Trickle's name is a view over its current state, so plain SQL in later Ripples reads what
29
+ `read_table` returns.
30
+
31
+ ### Performance
32
+
33
+ - **Clustering**: `cluster_by` on `merge_table`, the builder's `.merge()`, `write_table` and SQL Ripples
34
+ orders a table for reads that filter on those columns. One column sorts; two or more are interleaved
35
+ with a rank-Morton order (each column's quantile ranks, bit-interleaved), so every listed column
36
+ prunes, skewed ones included. A merge table's compacted base is now ordered by its primary key by
37
+ default instead of by freshness, which no read could use.
38
+ - A merge table's warm tier is read where it's published instead of being copied into a fresh Duck's
39
+ database, and is no longer also re-published in full on every export.
40
+
41
+ ### Data plane
42
+
43
+ - **Versioned overwrite tables**: each run publishes a plain table as a new immutable version, and a
44
+ Sink run reads the version its Source had published when the run started, even if the Source publishes
45
+ again meanwhile. Superseded versions are kept only while a running Sink still reads them.
46
+ - **The Iceberg plane is removed**, with `pyiceberg`, `DUCKSTRING_DATA_PLANE` and
47
+ `DUCKSTRING_ICEBERG_KEEP_SNAPSHOTS`. It cost about three times the export time, more than twice the
48
+ memory and over three times the disk of the Parquet plane, for interop no external engine could use.
49
+ - A registry DuckDB can't open (written by a newer DuckDB, or corrupt) is set aside and rebuilt from
50
+ published state instead of stopping the Duck.
51
+
52
+ ### Cloud and egress
53
+
54
+ - Flock settings and engine credentials travel with each run, so Fargate and EC2 Ducks can use the
55
+ Flock; they received neither before.
56
+ - The **Duck image is published** to GHCR for amd64 and arm64, tagged with the version, so cloud Ducks
57
+ need no image of your own unless Ponds need system libraries.
58
+ - The S3 Spout now honours `?endpoint=` and `DUCKSTRING_S3_ENDPOINT`, so it works with MinIO, R2 and
59
+ Ceph; it used to reach only AWS. Tested against MinIO in CI.
60
+ - A failed Spout delivery is retried when its source next publishes, and a Spout or alert destination
61
+ can be a single `${env:...}` or `${secret:...}` reference.
62
+ - A Pond Draw records its upstream's real version, so a `[sources]` pin above `{major}.0.0` deploys.
63
+ - A Duck that lost its local publish restores it from the durable layer before rebuilding, so Ponds on
64
+ the same machine never read it incomplete; the durable copy no longer loses warm bands after such a
65
+ rebuild.
66
+
67
+ ### Correctness and operability
68
+
69
+ - **DuckDB is bounded** to the tested range, `>=1.5,<2`, and CI tests the floor and the DuckDB 2.0
70
+ pre-release. A change to or from DuckDB 2.0's `VARIANT` type counts as breaking in the version
71
+ contract.
72
+ - `catchment init` and `start` warn when an open Catchment binds beyond loopback, since a deploy runs code
73
+ on the machine.
74
+ - Querying a missing table says the Pond hasn't run yet, or lists the tables it has.
75
+ - A Pool agent stopped with SIGTERM stops its Ducks.
76
+ - A Fargate or EC2 Duck shut down for being idle is replaced on the Pond's next run. Before, the Catchment
77
+ kept the departed Duck's record, launched nothing, and failed the run as "Lost contact" three minutes
78
+ later.
79
+ - An EC2 Duck's instance terminates when the Duck exits cleanly, instead of running, and billing, after
80
+ the Duck has gone. A Duck that crashes leaves its instance up so its console output can be read.
81
+ - SQL Ripples that read a Source work on the DuckDB 2.0 pre-release.
82
+ - A Catchment without API keys could answer a request with 401 under load. The Catchment's database
83
+ connection is shared by its threads, and the key check read it without the lock the engine holds,
84
+ so it sometimes saw a key that wasn't there. Every user of the connection now takes that lock, and
85
+ the key check is cached.
86
+ - A Duck that has heard nothing from its Catchment for an hour while idle exits, so a Duck left behind by
87
+ a Catchment that was deleted or stopped hard doesn't run, and bill, indefinitely. Set
88
+ `DUCKSTRING_DUCK_ORPHAN_MINUTES` on the Catchment to change the limit, or `0` to turn it off. Pool
89
+ machines aren't affected.
90
+
91
+ ### Documentation
92
+
93
+ The documentation is rewritten: Quickstart, Concepts, task-led Guides and a full Reference (Python API,
94
+ `pond.toml`, every CLI command group, the HTTP API, formats and environment variables), with the README and
95
+ landing page repositioned around Duckstring as a data engineering platform for DuckDB.
96
+
97
+ ### Upgrading
98
+
99
+ - **Published layout**: plain tables are now written as `{table}__v/{version}.parquet`. Readers still read
100
+ a Source's old single `{table}.parquet` until it publishes again, so Ponds can be upgraded in any order.
101
+ - **Leftover Iceberg files**: a data directory published by the Iceberg plane keeps a `catalog.json` and a
102
+ `pond/` directory that nothing reads now. Delete them; on a Catchment using local-first publish with an
103
+ S3 data root, the Persist mirror would otherwise upload them.
104
+ - **`DUCKSTRING_DATA_PLANE`** is no longer read; remove it from the environment.
105
+ - **DuckDB 2.0** isn't supported yet: installing Duckstring keeps DuckDB below 2, including in Pond
106
+ environments that lock a newer one.
107
+ - The Catchment database migrates itself on start (one new column).
108
+
109
+ ## 0.5.0 — 2026-08-03
110
+
111
+ The cloud release: a Catchment can now run its Ducks on AWS, keep its data on S3, and serve that data
112
+ over standard wire protocols. Everything below is opt-in — a Catchment with no cloud configuration
113
+ behaves exactly as it did in 0.4.0, and a `pond.toml` asking for remote compute still runs anywhere.
114
+
115
+ ### Cloud compute
116
+
117
+ - **Duck launchers**: a Pond's Duck runs on the Catchment's own box (default), on **Fargate** (the
118
+ default remote backend — serverless containers, task-role IAM), or on **EC2** (the escape hatch, for
119
+ sizes and GPUs Fargate does not offer). Built-in **S/M/L/XL preset pools** resolve with zero setup.
120
+ - **Duck Pools**: named remote-compute pools (`duckstring duck pool add`), with per-Pond compute
121
+ declared in `pond.toml` and overridable operationally (`duckstring duck set`).
122
+ - **The Pool agent**: one shared machine per named Pool hosting many co-resident Ducks.
123
+ - **Auto-relay**: a Catchment on a laptop behind NAT can run cloud Ducks with no manual tunnel — the
124
+ first remote spawn provisions a small always-reachable box and holds a reverse tunnel to it.
125
+ - **Remote failures are observable**: boot output is teed to the console (EC2) / CloudWatch (Fargate)
126
+ and the tail is attached to the Pond's failure, so a Duck that dies before dialling back still says
127
+ why. Spawn-failure reasons (missing image, missing AMI, bad IAM) surface on the Pond rather than as a
128
+ generic crash.
129
+ - Per-provider **startup grace** so a cold machine is not judged by the steady-state silence window.
130
+
131
+ ### Data plane on S3
132
+
133
+ - A Catchment's data root can be an object store (`duckstring catchment settings --data-root s3://…`),
134
+ with an S3-compatible **endpoint override** for MinIO/Ceph/R2.
135
+ - **Switch, adopt, or migrate** an existing plane, with a background copy and live progress.
136
+ - **Local-first publish + async Persist**: a run publishes locally and mirrors durably in the
137
+ background, so compute never waits on the object store; `persisted_f` is the durable watermark and
138
+ freshness gating is Pool-aware.
139
+ - **S3-resident state**: a merge base is read as a view rather than hydrated, and retention pruning is
140
+ floor-anchored rather than inferred from absence.
141
+
142
+ ### The Flock — over-envelope compute
143
+
144
+ A comprehensive `pond.trickle(...)` recompute that exceeds the Duck's memory envelope can be dispatched
145
+ to a serverless engine (**Athena** ships first), while the Duck keeps merge/diff/publish.
146
+
147
+ **DuckDB is the authority.** Dispatch decides *where* work runs, never *what* is published, enforced
148
+ structurally: an engine-owned allow-list of expressions proven equivalent on both engines (division and
149
+ CAST are excluded — the two engines genuinely disagree), a `conform` step that casts the engine's result
150
+ to DuckDB's own schema and rejects a differing column set, and degradation to local compute on any
151
+ failure. Dispatch counters ride `/metrics`, since a silently degrading Flock is otherwise invisible.
152
+
153
+ ### Data serving
154
+
155
+ - A sandboxed, warm, read-only query surface over published data: catalog = catchment, schema = pond.
156
+ - **Postgres wire** and **Arrow Flight SQL** adapters, plus `/api/serve` and a CLI.
157
+ - A **Catalog UI** unifying the query surface, with role-governed access.
158
+
159
+ ### Lineage
160
+
161
+ - Table-level (observed per run), column-level (static per version, with a sqlglot upgrade for SQL
162
+ outputs), and row-level temporal provenance (`duckstring trace`).
163
+ - **OpenLineage** events emitted on run completion for catalog integration.
164
+
165
+ ### dbt-mode Ponds
166
+
167
+ Deploy a dbt project as a Pond with no `@ripple` code: each dbt model becomes a Ripple with its own
168
+ freshness, failure and retry tracking, and `ref()` becomes the intra-Pond graph. Opt in with the
169
+ `duckstring[dbt]` extra.
170
+
171
+ ### Correctness and operability
172
+
173
+ - **Version contract**: a lossless widening of a column type is accepted (a data-dependent branch could
174
+ legitimately wedge a live Pond permanently); `reset-contract` is the escape hatch for a genuine
175
+ narrowing; contract failures carry a dedicated sub-reason end-to-end.
176
+ - **Reads reject a stale local publish** the Catchment knows is behind, rather than serving it.
177
+ - Pond Runs the Pond has moved past are closed rather than stranded at `running`.
178
+ - A Pool machine that disappears is detected and relaunched, instead of wedging every Pond on it.
179
+ - The **serving sandbox** no longer spills into the shared system temp — DuckDB exempts its temp
180
+ directory from the external-access lock, which on Linux exposed everything under `/tmp` to a
181
+ read-level user.
182
+ - An idle Duck backs off instead of polling ten times a second.
183
+ - Opt-in **re-notify cadence** for alert channels while a failure or freshness episode persists.
184
+ - Incremental object-store egress (`mode=append`) mirrors the published collection per delivery.
185
+
186
+ ### Testing
187
+
188
+ - The object-store data plane runs against a **real S3 API** in CI (MinIO, which rejects unsigned
189
+ requests — the class of bug moto cannot catch).
190
+ - Real-data demo sets: **TPC-DS** and **GHArchive** (the latter offline against a committed fixture).
191
+ - Python matrix, the dbt extra, real-Postgres egress, and frontend tests in CI.
192
+
193
+ ### Documentation
194
+
195
+ - A full **AWS guide** (`guides/cloud.md`): the cloud-enable gate, the three IAM roles, security-group
196
+ asymmetry, the AMI Python constraint, the build-your-own-image policy, and the Flock's authority rules.
197
+ - New guides for lineage, dbt-mode, and querying data.
@@ -1,5 +1,7 @@
1
1
  # The sdist ships the installable package only — keep internal design notes and the test suite
2
2
  # out of the PyPI source tarball (they're in the git repo for contributors).
3
+ include CHANGELOG.md
4
+
3
5
  prune tests
4
6
  prune plans
5
7
  prune experiment
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: duckstring
3
- Version: 0.4.0
4
- Summary: Build data pipelines the way you build software: version each transform, declare its dependencies, and Duckstring resolves the execution DAG automatically.
3
+ Version: 0.6.0
4
+ Summary: An open source data engineering platform for DuckDB: versioned transformations, pull orchestration, incremental processing and a built-in catalog.
5
5
  Author-email: Duckstring <dev@duckstring.com>
6
6
  License: Apache License
7
7
  Version 2.0, January 2004
@@ -212,7 +212,7 @@ Project-URL: Playground, https://playground.duckstring.com
212
212
  Requires-Python: >=3.10
213
213
  Description-Content-Type: text/markdown
214
214
  License-File: LICENSE
215
- Requires-Dist: duckdb>=0.10
215
+ Requires-Dist: duckdb<2,>=1.5
216
216
  Requires-Dist: rich>=13.7
217
217
  Requires-Dist: typer>=0.12
218
218
  Requires-Dist: fastapi>=0.111
@@ -221,95 +221,137 @@ Requires-Dist: uvicorn[standard]>=0.29
221
221
  Requires-Dist: httpx>=0.25
222
222
  Requires-Dist: tomli>=2.0; python_version < "3.11"
223
223
  Requires-Dist: tomli-w>=1.0
224
- Requires-Dist: pyiceberg>=0.7
225
224
  Requires-Dist: pyarrow>=14
226
225
  Requires-Dist: pytz
227
- Provides-Extra: iceberg
226
+ Requires-Dist: pathspec>=0.10
227
+ Requires-Dist: uv>=0.5
228
+ Provides-Extra: dbt
229
+ Requires-Dist: dbt-duckdb>=1.9; extra == "dbt"
230
+ Provides-Extra: lineage
231
+ Requires-Dist: sqlglot>=23; extra == "lineage"
232
+ Provides-Extra: aws
233
+ Requires-Dist: s3fs>=2023.1; extra == "aws"
234
+ Requires-Dist: boto3>=1.34; extra == "aws"
228
235
  Provides-Extra: dev
229
236
  Requires-Dist: pytest>=8.0; extra == "dev"
237
+ Requires-Dist: moto[server]>=5.0; extra == "dev"
230
238
  Requires-Dist: pytest-timeout>=0.5; extra == "dev"
231
239
  Requires-Dist: ruff>=0.3; extra == "dev"
232
240
  Requires-Dist: mypy>=1.8; extra == "dev"
241
+ Requires-Dist: dbt-duckdb>=1.9; extra == "dev"
242
+ Requires-Dist: sqlglot>=23; extra == "dev"
243
+ Requires-Dist: s3fs>=2023.1; extra == "dev"
244
+ Requires-Dist: boto3>=1.34; extra == "dev"
245
+ Requires-Dist: pg8000>=1.31; extra == "dev"
233
246
  Dynamic: license-file
234
247
 
235
248
  # Duckstring
236
- *There is no DAG.*
237
249
 
238
- Duckstring treats data transformations as software packages. Upstream dependencies are declared per Pond (unit operation), defining the DAG without the need for its direct management.
250
+ **Get your ducks in a row.**
239
251
 
240
- Ponds are upgraded and deployed to Duckstring's pull-based Catchment (orchestrator) atomically - like upgrading a package - with the earlier version continuing to execute until there are no consumers dependent on it. Upstream defines constraints on what it can consume, downstream defines when it's needed, and the Catchment optimally executes the sequence of Ponds supplying it with the best currency and frequency as possible.
252
+ Duckstring is an open source data engineering platform built on DuckDB. Data into the terabytes fits comfortably on a single machine, which is where DuckDB is at its best, and Duckstring provides what a pipeline needs around it: packaging and versioning, orchestration, incremental processing, a catalog and delivery. It runs the same on a laptop, on a single server, or with work spread across cloud machines.
241
253
 
242
- You should not need to manage the DAG. You should not need global governance. You should know yourself and your suppliers and trust that you'll get what you need when you need it.
254
+ [Documentation](https://docs.duckstring.com) · [Quickstart](https://docs.duckstring.com/quickstart) · [Playground](https://playground.duckstring.com)
243
255
 
244
- ## Core Concepts
256
+ ## Versioned transformations
245
257
 
246
- The main elements:
258
+ Each set of transformations is a *Pond*: a Python project whose `pond.toml` names it, gives it a semantic version and lists the Ponds it reads from.
247
259
 
248
- - **Catchment**: Control environment (FastAPI + UI + CLI)
249
- - **Pond**: Versioned container with declared upstream dependencies
250
- - **Ripple**: Unit operation within a Pond (e.g. a single transformation producing a table)
251
- - **Trickle**: DBSP-based incremental engine operating within a Ripple
260
+ ```toml
261
+ [pond]
262
+ name = "sales"
263
+ version = "1.2.0"
252
264
 
253
- Ponds are typed or referred to in context:
265
+ [sources]
266
+ transactions = "1.0.0"
267
+ products = "1.1.0"
268
+ ```
269
+
270
+ The pipeline follows from those declarations, so it never has to be drawn by hand. A breaking change ships as a new major version, which runs alongside the old one while downstream Ponds move across at their own pace.
271
+
272
+ ## Pull orchestration
254
273
 
255
- - **Source**: A parent Pond
256
- - **Sink**: A child Pond
257
- - **Inlet**: A Pond with external dependencies and no Sources
258
- - **Outlet**: A Pond with no Sinks (e.g. outputs final data products)
274
+ Schedules are set on the Ponds whose output is actually used, at the end of the pipeline. Duckstring works back from there, running each upstream Pond only as often as something downstream needs it, so a path nobody consumes never runs. A daily job is one command:
275
+
276
+ ```bash
277
+ duckstring trigger tide reports 1d
278
+ ```
259
279
 
260
- To see the orchestration model in action without installing anything, try the [Duckstring Playground](https://playground.duckstring.com).
280
+ ## Incremental processing
281
+
282
+ A *Trickle* stores a table's changes as a Z-set: rows weighted +1 when added and -1 when removed. Joins and aggregations written with the Trickle builder recompute only the keys that changed since the last run, and handle updates and deletes correctly across any shape of join:
283
+
284
+ ```python
285
+ from duckstring import ripple
286
+
287
+
288
+ @ripple
289
+ def priced_line(pond):
290
+ (
291
+ pond.trickle("orders.order_line")
292
+ .join(pond.trickle("catalog.product"), on="product_id")
293
+ .select(
294
+ "s0.order_id, s0.product_id, s0.quantity, s1.unit_price, "
295
+ "CAST(round(s0.quantity * s1.unit_price, 2) AS DECIMAL(14,2)) AS revenue"
296
+ )
297
+ .merge("priced_line", pk="order_id")
298
+ )
299
+ ```
300
+
301
+ ## Code and data together
302
+
303
+ The runtime, called the *Catchment*, is also the catalog. Each major version of a Pond is a schema holding its tables, with lineage, run history and the version's schema contract recorded alongside. Query it from the CLI, from Python, or from any Postgres client:
304
+
305
+ ```sql
306
+ SELECT * FROM reports_v1.monthly_summary;
307
+ ```
308
+
309
+ ## Python and the command line
310
+
311
+ Ripples, the steps inside a Pond, are Python functions with a DuckDB connection, so SQL, DuckDB's relation API, pandas and Arrow all work. Everything a Catchment does can be driven from the `duckstring` CLI or its HTTP API, and a web UI shows pipelines as they run.
261
312
 
262
313
  ## Quickstart
263
314
 
315
+ Duckstring needs Python 3.10 or newer.
316
+
264
317
  ```bash
265
318
  pip install duckstring
266
-
267
- # Start a local Catchment (the runtime + web UI) — leave it running
268
319
  duckstring catchment init --name dev
269
320
  ```
270
321
 
271
- Then, in another terminal:
322
+ This starts a local Catchment, with its web UI at http://127.0.0.1:7474. Leave it running, and in a second terminal:
272
323
 
273
324
  ```bash
274
- # Create the demo pipeline (transactions, products → sales → reports) and deploy it
275
325
  mkdir demo && cd demo
276
- duckstring pond demo
277
- duckstring pond deploy --all -y
278
-
279
- # Run it end to end, once
280
- duckstring trigger pulse reports
281
-
282
- # Look around
283
- duckstring status # live state of every Pond
284
- duckstring query reports monthly_summary # peek at an output table
326
+ duckstring pond demo # transactions, products → sales → reports
327
+ duckstring pond deploy --all --yes
328
+ duckstring trigger pulse reports # run the pipeline once, from the end
329
+ duckstring query reports monthly_summary
285
330
  ```
286
331
 
287
- The Catchment also serves a live web UI at `http://127.0.0.1:7474` — the Pond graph, freshness, run history, and the full trigger/control surface.
332
+ The [Quickstart](https://docs.duckstring.com/quickstart) goes through the same steps with explanations.
288
333
 
289
- ## Execution
334
+ ## Also included
290
335
 
291
- Ponds execute on demand signals sent to an Outlet, in two flavours — **push** runs the lineage forward to a target freshness; **pull** propagates demand upstream so every Pond re-runs as its Sources update, naturally throttled to the bottleneck. Each comes as a one-shot or a standing trigger:
336
+ - [Local testing](https://docs.duckstring.com/guides/testing_with_puddles) against small samples of each Source, called Puddles
337
+ - [dbt projects](https://docs.duckstring.com/guides/dbt_projects) deployed as Ponds, one step per model
338
+ - [Delivery](https://docs.duckstring.com/guides/delivering_with_spouts) to Postgres (incremental and exactly once), S3, Google Cloud Storage and local files
339
+ - [Retries, alerts and Prometheus metrics](https://docs.duckstring.com/guides/monitoring_and_failures)
340
+ - [Per-Pond Python environments](https://docs.duckstring.com/guides/writing_ripples#python-dependencies) from `pyproject.toml` and `uv.lock`
341
+ - [Cloud compute](https://docs.duckstring.com/guides/cloud_compute_on_aws) on AWS Fargate or EC2, chosen per Pond, with data on S3
342
+ - [Connected Catchments](https://docs.duckstring.com/guides/connecting_catchments), where a Pond reads from a Pond on another Catchment
292
343
 
293
- | | Once | Continuously |
294
- |---|---|---|
295
- | **Push** | Pulse | Tide |
296
- | **Pull** | Tap | Wave |
344
+ ## Documentation
297
345
 
298
- A Tide keeps an Outlet no staler than a bound (`duckstring trigger tide reports 1d`); a Wave keeps it as fresh as the pipeline can supply. See [Triggers](https://docs.duckstring.com/guides/triggers) for the full semantics.
346
+ [docs.duckstring.com](https://docs.duckstring.com) has the concepts, task guides, and a reference for the CLI, Python API, `pond.toml` and HTTP API. To try pull orchestration without installing anything, use the [Playground](https://playground.duckstring.com).
299
347
 
300
- ## Going further
348
+ ## Status
301
349
 
302
- Full documentation lives at **[docs.duckstring.com](https://docs.duckstring.com)**:
350
+ Duckstring is pre-1.0, so interfaces may still change between minor versions. Changes are listed in the [changelog](CHANGELOG.md).
303
351
 
304
- - [Quickstart](https://docs.duckstring.com/getting-started/quickstart) — the path above, with explanations
305
- - [Theory](https://docs.duckstring.com/theory) — the freshness-based orchestration model in depth
306
- - [Versioning](https://docs.duckstring.com/concepts/versioning) — SemVer on Ponds, concurrent major versions, atomic upgrades
307
- - [Local testing](https://docs.duckstring.com/guides/local-testing) — Puddles: test a Pond before deploying it
308
- - [Fault tolerance](https://docs.duckstring.com/guides/fault-tolerance) — retry budgets, failure states, recovery
309
- - [Running a Catchment](https://docs.duckstring.com/guides/running-a-catchment) — hosting, authentication, platform deployment
310
- - [CLI](https://docs.duckstring.com/reference/cli) / [HTTP API](https://docs.duckstring.com/reference/http-api) — full references
352
+ ## Hosted Catchments
311
353
 
312
- There are future plans for a hosted Catchment service at [duckstring.com](https://duckstring.com). If you're interested, please [get in touch](mailto:dev@duckstring.com).
354
+ A hosted Catchment service is planned at [duckstring.com](https://duckstring.com). If you're interested, get in touch at [dev@duckstring.com](mailto:dev@duckstring.com).
313
355
 
314
356
  ## License
315
357
 
@@ -0,0 +1,111 @@
1
+ # Duckstring
2
+
3
+ **Get your ducks in a row.**
4
+
5
+ Duckstring is an open source data engineering platform built on DuckDB. Data into the terabytes fits comfortably on a single machine, which is where DuckDB is at its best, and Duckstring provides what a pipeline needs around it: packaging and versioning, orchestration, incremental processing, a catalog and delivery. It runs the same on a laptop, on a single server, or with work spread across cloud machines.
6
+
7
+ [Documentation](https://docs.duckstring.com) · [Quickstart](https://docs.duckstring.com/quickstart) · [Playground](https://playground.duckstring.com)
8
+
9
+ ## Versioned transformations
10
+
11
+ Each set of transformations is a *Pond*: a Python project whose `pond.toml` names it, gives it a semantic version and lists the Ponds it reads from.
12
+
13
+ ```toml
14
+ [pond]
15
+ name = "sales"
16
+ version = "1.2.0"
17
+
18
+ [sources]
19
+ transactions = "1.0.0"
20
+ products = "1.1.0"
21
+ ```
22
+
23
+ The pipeline follows from those declarations, so it never has to be drawn by hand. A breaking change ships as a new major version, which runs alongside the old one while downstream Ponds move across at their own pace.
24
+
25
+ ## Pull orchestration
26
+
27
+ Schedules are set on the Ponds whose output is actually used, at the end of the pipeline. Duckstring works back from there, running each upstream Pond only as often as something downstream needs it, so a path nobody consumes never runs. A daily job is one command:
28
+
29
+ ```bash
30
+ duckstring trigger tide reports 1d
31
+ ```
32
+
33
+ ## Incremental processing
34
+
35
+ A *Trickle* stores a table's changes as a Z-set: rows weighted +1 when added and -1 when removed. Joins and aggregations written with the Trickle builder recompute only the keys that changed since the last run, and handle updates and deletes correctly across any shape of join:
36
+
37
+ ```python
38
+ from duckstring import ripple
39
+
40
+
41
+ @ripple
42
+ def priced_line(pond):
43
+ (
44
+ pond.trickle("orders.order_line")
45
+ .join(pond.trickle("catalog.product"), on="product_id")
46
+ .select(
47
+ "s0.order_id, s0.product_id, s0.quantity, s1.unit_price, "
48
+ "CAST(round(s0.quantity * s1.unit_price, 2) AS DECIMAL(14,2)) AS revenue"
49
+ )
50
+ .merge("priced_line", pk="order_id")
51
+ )
52
+ ```
53
+
54
+ ## Code and data together
55
+
56
+ The runtime, called the *Catchment*, is also the catalog. Each major version of a Pond is a schema holding its tables, with lineage, run history and the version's schema contract recorded alongside. Query it from the CLI, from Python, or from any Postgres client:
57
+
58
+ ```sql
59
+ SELECT * FROM reports_v1.monthly_summary;
60
+ ```
61
+
62
+ ## Python and the command line
63
+
64
+ Ripples, the steps inside a Pond, are Python functions with a DuckDB connection, so SQL, DuckDB's relation API, pandas and Arrow all work. Everything a Catchment does can be driven from the `duckstring` CLI or its HTTP API, and a web UI shows pipelines as they run.
65
+
66
+ ## Quickstart
67
+
68
+ Duckstring needs Python 3.10 or newer.
69
+
70
+ ```bash
71
+ pip install duckstring
72
+ duckstring catchment init --name dev
73
+ ```
74
+
75
+ This starts a local Catchment, with its web UI at http://127.0.0.1:7474. Leave it running, and in a second terminal:
76
+
77
+ ```bash
78
+ mkdir demo && cd demo
79
+ duckstring pond demo # transactions, products → sales → reports
80
+ duckstring pond deploy --all --yes
81
+ duckstring trigger pulse reports # run the pipeline once, from the end
82
+ duckstring query reports monthly_summary
83
+ ```
84
+
85
+ The [Quickstart](https://docs.duckstring.com/quickstart) goes through the same steps with explanations.
86
+
87
+ ## Also included
88
+
89
+ - [Local testing](https://docs.duckstring.com/guides/testing_with_puddles) against small samples of each Source, called Puddles
90
+ - [dbt projects](https://docs.duckstring.com/guides/dbt_projects) deployed as Ponds, one step per model
91
+ - [Delivery](https://docs.duckstring.com/guides/delivering_with_spouts) to Postgres (incremental and exactly once), S3, Google Cloud Storage and local files
92
+ - [Retries, alerts and Prometheus metrics](https://docs.duckstring.com/guides/monitoring_and_failures)
93
+ - [Per-Pond Python environments](https://docs.duckstring.com/guides/writing_ripples#python-dependencies) from `pyproject.toml` and `uv.lock`
94
+ - [Cloud compute](https://docs.duckstring.com/guides/cloud_compute_on_aws) on AWS Fargate or EC2, chosen per Pond, with data on S3
95
+ - [Connected Catchments](https://docs.duckstring.com/guides/connecting_catchments), where a Pond reads from a Pond on another Catchment
96
+
97
+ ## Documentation
98
+
99
+ [docs.duckstring.com](https://docs.duckstring.com) has the concepts, task guides, and a reference for the CLI, Python API, `pond.toml` and HTTP API. To try pull orchestration without installing anything, use the [Playground](https://playground.duckstring.com).
100
+
101
+ ## Status
102
+
103
+ Duckstring is pre-1.0, so interfaces may still change between minor versions. Changes are listed in the [changelog](CHANGELOG.md).
104
+
105
+ ## Hosted Catchments
106
+
107
+ A hosted Catchment service is planned at [duckstring.com](https://duckstring.com). If you're interested, get in touch at [dev@duckstring.com](mailto:dev@duckstring.com).
108
+
109
+ ## License
110
+
111
+ [Apache 2.0](LICENSE)
@@ -0,0 +1,106 @@
1
+ [project]
2
+ name = "duckstring"
3
+ version = "0.6.0"
4
+ description = "An open source data engineering platform for DuckDB: versioned transformations, pull orchestration, incremental processing and a built-in catalog."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = { file = "LICENSE" }
8
+ authors = [
9
+ { name = "Duckstring", email = "dev@duckstring.com" }
10
+ ]
11
+
12
+ dependencies = [
13
+ # The tested range: CI runs the suite on the floor and the latest release, and against the pre-release
14
+ # in an allowed-to-fail job. Raise the ceiling once the suite passes on the next major's release
15
+ # (plans/duckdb-2-ready.md). A Pond environment gets this Duckstring, so these bounds hold there too.
16
+ "duckdb>=1.5,<2",
17
+ "rich>=13.7",
18
+ "typer>=0.12",
19
+ "fastapi>=0.111",
20
+ "python-multipart>=0.0.9",
21
+ "uvicorn[standard]>=0.29",
22
+ "httpx>=0.25",
23
+ "tomli>=2.0; python_version < '3.11'",
24
+ "tomli-w>=1.0",
25
+ # Arrow interchange: the Flight SQL server and Arrow fetches from DuckDB.
26
+ "pyarrow>=14",
27
+ # DuckDB requires pytz to convert TIMESTAMPTZ columns to Python datetimes on fetch
28
+ # (our _duckstring_f freshness column is TIMESTAMPTZ, read back in dataplane/trickle export).
29
+ # It is otherwise only present transitively (pandas), so a clean install lacks it.
30
+ "pytz",
31
+ # .pondignore matching with exact .gitignore semantics (negation, anchoring, **). Pure Python.
32
+ "pathspec>=0.10",
33
+ # Builds each Pond's own environment from its pyproject.toml + uv.lock (plans/pond-environments.md).
34
+ # A self-contained binary wheel; duckstring.environments finds it with uv.find_uv_bin().
35
+ "uv>=0.5",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ # dbt-mode Ponds (plans/dbt.md): deploy a dbt project as a Pond, each model a Ripple. Heavyweight and
40
+ # opt-in — only pulled when a team actually runs dbt-mode. dbt-duckdb pulls dbt-core transitively.
41
+ dbt = [
42
+ "dbt-duckdb>=1.9",
43
+ ]
44
+ # Column lineage through .sql() escape hatches (plans/lineage.md Phase 3): a real SQL parser, opt-in --
45
+ # without it those outputs are honestly "opaque", never guessed. Pure python, light.
46
+ lineage = [
47
+ "sqlglot>=23",
48
+ ]
49
+ # AWS / cloud (plans/cloud-config.md): the object-store data plane + egress (s3fs — fsspec's small-file
50
+ # work; DuckDB does the bulk Parquet I/O over httpfs on its own), and the remote-compute launchers
51
+ # (EC2/Fargate/relay) + the Athena Flock engine (boto3). Opt-in — a local/offline Catchment needs none.
52
+ aws = [
53
+ "s3fs>=2023.1",
54
+ "boto3>=1.34",
55
+ ]
56
+ dev = [
57
+ "pytest>=8.0",
58
+ # A local S3 API, so the object-store data plane is a normal test rather than a live-AWS session.
59
+ "moto[server]>=5.0",
60
+ "pytest-timeout>=0.5",
61
+ "ruff>=0.3",
62
+ "mypy>=1.8",
63
+ "dbt-duckdb>=1.9",
64
+ "sqlglot>=23",
65
+ "s3fs>=2023.1",
66
+ "boto3>=1.34",
67
+ "pg8000>=1.31",
68
+ ]
69
+
70
+ [tool.setuptools]
71
+ package-dir = { "" = "src" }
72
+
73
+ [tool.setuptools.package-data]
74
+ "duckstring.catchment" = ["static/**/*", "schema/*.sql"]
75
+ "duckstring" = ["demo/**/*", "demo/**/.gitignore"]
76
+
77
+ [tool.setuptools.packages.find]
78
+ where = ["src"]
79
+
80
+ [project.urls]
81
+ Homepage = "https://duckstring.com"
82
+ Documentation = "https://docs.duckstring.com"
83
+ Repository = "https://github.com/duckstring-dev/duckstring"
84
+ Playground = "https://playground.duckstring.com"
85
+
86
+ [project.scripts]
87
+ duckstring = "duckstring.cli:main"
88
+ ds = "duckstring.cli:main"
89
+
90
+ [tool.pytest.ini_options]
91
+ testpaths = ["tests"]
92
+ # Some suites share fixtures by importing `tests.<module>`; put the repo root on the path so a bare
93
+ # `pytest` (what CI runs) resolves them the same as `python -m pytest`.
94
+ pythonpath = ["."]
95
+ # Tight enough to catch an accidental real sleep (sim tests finish in ms), loose enough to absorb
96
+ # cold-runner I/O on CI (SQLite migrate / uvicorn startup can exceed 1s on shared runners).
97
+ timeout = 5
98
+
99
+ [tool.ruff]
100
+ line-length = 128
101
+ target-version = "py310"
102
+ extend-exclude = ["experiment"]
103
+
104
+ [tool.ruff.lint]
105
+ select = ["E", "F", "I", "B"]
106
+ ignore = ["B008"] # typer.Option/Argument in defaults is intentional
@@ -5,7 +5,6 @@ from .core import (
5
5
  Catchment,
6
6
  Pond,
7
7
  Puddle,
8
- Ripple,
9
8
  puddle,
10
9
  ripple,
11
10
  )
@@ -19,7 +18,6 @@ __all__ = [
19
18
  "Catchment",
20
19
  "Pond",
21
20
  "Puddle",
22
- "Ripple",
23
21
  "puddle",
24
22
  "ripple",
25
23
  "__version__",