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.
- duckstring-0.6.0/CHANGELOG.md +197 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/MANIFEST.in +2 -0
- {duckstring-0.4.0/src/duckstring.egg-info → duckstring-0.6.0}/PKG-INFO +94 -52
- duckstring-0.6.0/README.md +111 -0
- duckstring-0.6.0/pyproject.toml +106 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/__init__.py +0 -2
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/base.py +22 -4
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/email.py +9 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/event.py +14 -3
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/webhook.py +4 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/app.py +72 -5
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/auth.py +54 -11
- duckstring-0.6.0/src/duckstring/catchment/cloud.py +156 -0
- duckstring-0.6.0/src/duckstring/catchment/cloud_backends.py +156 -0
- duckstring-0.6.0/src/duckstring/catchment/cloud_deploy.py +77 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/data_lease.py +5 -6
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/db.py +20 -1
- duckstring-0.6.0/src/duckstring/catchment/dialback.py +41 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/driver.py +1599 -79
- duckstring-0.6.0/src/duckstring/catchment/ec2_launcher.py +390 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/egress_worker.py +33 -9
- duckstring-0.6.0/src/duckstring/catchment/fargate_launcher.py +304 -0
- duckstring-0.6.0/src/duckstring/catchment/flight_sql.py +98 -0
- duckstring-0.6.0/src/duckstring/catchment/launcher.py +342 -0
- duckstring-0.6.0/src/duckstring/catchment/pg_wire.py +317 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/poller.py +2 -1
- duckstring-0.6.0/src/duckstring/catchment/pool_launcher.py +391 -0
- duckstring-0.6.0/src/duckstring/catchment/registry.py +85 -0
- duckstring-0.6.0/src/duckstring/catchment/relay.py +217 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/__init__.py +4 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/alerts.py +3 -1
- duckstring-0.6.0/src/duckstring/catchment/routes/catchment.py +497 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/data.py +203 -42
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/deploy.py +151 -50
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/draw.py +24 -3
- duckstring-0.6.0/src/duckstring/catchment/routes/duck.py +68 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/duct.py +22 -2
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/metrics.py +36 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/orchestrate.py +85 -3
- duckstring-0.6.0/src/duckstring/catchment/routes/pool.py +38 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/secrets.py +17 -1
- duckstring-0.6.0/src/duckstring/catchment/routes/serving.py +104 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/017_alert_renotify.sql +7 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/018_duck.sql +11 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/019_lineage.sql +18 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/020_column_lineage.sql +15 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/021_cloud.sql +44 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/022_retire_duck_size.sql +25 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/023_pool_provider.sql +7 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/024_serving.sql +29 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/025_dbt.sql +3 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/026_deploy_config.sql +7 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/027_persist.sql +14 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/028_persist_timing.sql +3 -0
- duckstring-0.6.0/src/duckstring/catchment/schema/029_source_pins.sql +5 -0
- duckstring-0.6.0/src/duckstring/catchment/serving.py +208 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/404.html +1 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next.__PAGE__.txt +2 -2
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next._full.txt +3 -3
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next._head.txt +1 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next._index.txt +2 -2
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/__next._tree.txt +2 -2
- duckstring-0.6.0/src/duckstring/catchment/static/_next/static/chunks/09y-jd_5pt5my.js +2 -0
- duckstring-0.6.0/src/duckstring/catchment/static/_next/static/chunks/0_8xp0_1~w7l7.css +1 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._full.txt +2 -2
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._head.txt +1 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._index.txt +2 -2
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._not-found.txt +1 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found/__next._tree.txt +2 -2
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found.html +1 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_not-found.txt +2 -2
- duckstring-0.6.0/src/duckstring/catchment/static/index.html +1 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/index.txt +3 -3
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/__init__.py +22 -3
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/_http.py +8 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/alert.py +23 -17
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/bulk.py +22 -18
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/catchment.py +108 -45
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/control.py +65 -25
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/data.py +17 -10
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/deploy.py +85 -34
- duckstring-0.6.0/src/duckstring/cli/duck.py +194 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/duct.py +16 -15
- duckstring-0.6.0/src/duckstring/cli/lineage.py +111 -0
- duckstring-0.6.0/src/duckstring/cli/pond.py +334 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/puddle.py +12 -4
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/secret.py +5 -5
- duckstring-0.6.0/src/duckstring/cli/serve.py +97 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/spout.py +19 -17
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/status.py +2 -2
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/trigger.py +24 -15
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/window.py +12 -10
- duckstring-0.6.0/src/duckstring/core.py +1123 -0
- duckstring-0.6.0/src/duckstring/dataplane.py +1284 -0
- duckstring-0.6.0/src/duckstring/dbt_mode.py +149 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/catalog/src/pond.py +4 -1
- duckstring-0.6.0/src/duckstring/demo/gh_actors/README.md +10 -0
- duckstring-0.6.0/src/duckstring/demo/gh_actors/pond.toml +6 -0
- duckstring-0.6.0/src/duckstring/demo/gh_actors/src/pond.py +25 -0
- duckstring-0.6.0/src/duckstring/demo/gh_events/README.md +21 -0
- duckstring-0.6.0/src/duckstring/demo/gh_events/pond.toml +4 -0
- duckstring-0.6.0/src/duckstring/demo/gh_events/src/pond.py +109 -0
- duckstring-0.6.0/src/duckstring/demo/gh_pushes/README.md +10 -0
- duckstring-0.6.0/src/duckstring/demo/gh_pushes/pond.toml +7 -0
- duckstring-0.6.0/src/duckstring/demo/gh_pushes/src/pond.py +23 -0
- duckstring-0.6.0/src/duckstring/demo/gh_repo_activity/README.md +14 -0
- duckstring-0.6.0/src/duckstring/demo/gh_repo_activity/pond.toml +7 -0
- duckstring-0.6.0/src/duckstring/demo/gh_repo_activity/src/pond.py +24 -0
- duckstring-0.6.0/src/duckstring/demo/gh_stars/README.md +10 -0
- duckstring-0.6.0/src/duckstring/demo/gh_stars/pond.toml +6 -0
- duckstring-0.6.0/src/duckstring/demo/gh_stars/src/pond.py +19 -0
- duckstring-0.6.0/src/duckstring/demo/gh_trending/README.md +10 -0
- duckstring-0.6.0/src/duckstring/demo/gh_trending/pond.toml +7 -0
- duckstring-0.6.0/src/duckstring/demo/gh_trending/src/pond.py +23 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/src/pond.py +1 -1
- duckstring-0.6.0/src/duckstring/demo/shop_analytics/.gitignore +8 -0
- duckstring-0.6.0/src/duckstring/demo/shop_analytics/README.md +24 -0
- duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/dbt_project.yml +12 -0
- duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/models/orders_clean.sql +8 -0
- duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/models/revenue_by_product.sql +8 -0
- duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/models/sources.yml +9 -0
- duckstring-0.6.0/src/duckstring/demo/shop_analytics/dbt/models/top_products.sql +8 -0
- duckstring-0.6.0/src/duckstring/demo/shop_analytics/pond.toml +11 -0
- duckstring-0.6.0/src/duckstring/demo/shop_orders/.gitignore +3 -0
- duckstring-0.6.0/src/duckstring/demo/shop_orders/README.md +9 -0
- duckstring-0.6.0/src/duckstring/demo/shop_orders/pond.toml +4 -0
- duckstring-0.6.0/src/duckstring/demo/shop_orders/src/pond.py +32 -0
- duckstring-0.6.0/src/duckstring/demo/sql_reports/README.md +11 -0
- duckstring-0.6.0/src/duckstring/demo/sql_reports/pond.toml +11 -0
- duckstring-0.6.0/src/duckstring/demo/sql_reports/sql/monthly_summary.sql +11 -0
- duckstring-0.6.0/src/duckstring/demo/sql_sales/README.md +11 -0
- duckstring-0.6.0/src/duckstring/demo/sql_sales/data/tier_bands.csv +4 -0
- duckstring-0.6.0/src/duckstring/demo/sql_sales/pond.toml +22 -0
- duckstring-0.6.0/src/duckstring/demo/sql_sales/sql/daily_sales.sql +9 -0
- duckstring-0.6.0/src/duckstring/demo/sql_sales/sql/price_tiers.sql +9 -0
- duckstring-0.6.0/src/duckstring/demo/sql_sales/sql/sale_line.sql +12 -0
- duckstring-0.6.0/src/duckstring/demo/sql_sales/src/puddles.py +47 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_category_revenue/README.md +13 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_category_revenue/pond.toml +7 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_category_revenue/src/pond.py +26 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_items/README.md +11 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_items/pond.toml +4 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_items/src/pond.py +80 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_priced/README.md +11 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_priced/pond.toml +8 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_priced/src/pond.py +26 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_sales/README.md +22 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_sales/pond.toml +4 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_sales/src/pond.py +76 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_store_revenue/README.md +10 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_store_revenue/pond.toml +7 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_store_revenue/src/pond.py +25 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_stores/README.md +10 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_stores/pond.toml +4 -0
- duckstring-0.6.0/src/duckstring/demo/tpcds_stores/src/pond.py +48 -0
- duckstring-0.6.0/src/duckstring/discover.py +170 -0
- duckstring-0.6.0/src/duckstring/duck/__main__.py +420 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/duck/client.py +15 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/duck/core.py +19 -3
- duckstring-0.6.0/src/duckstring/duck/dbt_executor.py +174 -0
- duckstring-0.6.0/src/duckstring/duck/executor.py +393 -0
- duckstring-0.6.0/src/duckstring/duck/pool_agent.py +171 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/base.py +6 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/credentials.py +13 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/destination.py +21 -3
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/object_store.py +116 -14
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/postgres.py +1 -1
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/__init__.py +2 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/catchment.py +33 -4
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/core.py +14 -0
- duckstring-0.6.0/src/duckstring/environments.py +245 -0
- duckstring-0.6.0/src/duckstring/flock/__init__.py +349 -0
- duckstring-0.6.0/src/duckstring/flock/engines/__init__.py +0 -0
- duckstring-0.6.0/src/duckstring/flock/engines/athena.py +298 -0
- duckstring-0.6.0/src/duckstring/flock/equivalence.py +75 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/local/hydrate.py +11 -5
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/local/runner.py +18 -41
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/objects.py +5 -2
- duckstring-0.6.0/src/duckstring/pondignore.py +79 -0
- duckstring-0.6.0/src/duckstring/schema_contract.py +148 -0
- duckstring-0.6.0/src/duckstring/sql_ripples.py +420 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/storage.py +134 -70
- duckstring-0.6.0/src/duckstring/trickle/acc.py +122 -0
- duckstring-0.6.0/src/duckstring/trickle/agg.py +184 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle/builder.py +182 -97
- duckstring-0.6.0/src/duckstring/trickle/capture.py +363 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle/io.py +221 -50
- duckstring-0.6.0/src/duckstring/trickle/lineage.py +305 -0
- {duckstring-0.4.0 → duckstring-0.6.0/src/duckstring.egg-info}/PKG-INFO +94 -52
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/SOURCES.txt +107 -9
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/requires.txt +18 -3
- duckstring-0.4.0/README.md +0 -82
- duckstring-0.4.0/pyproject.toml +0 -78
- duckstring-0.4.0/src/duckstring/catchment/launcher.py +0 -103
- duckstring-0.4.0/src/duckstring/catchment/registry.py +0 -40
- duckstring-0.4.0/src/duckstring/catchment/routes/catchment.py +0 -161
- duckstring-0.4.0/src/duckstring/catchment/routes/duck.py +0 -32
- duckstring-0.4.0/src/duckstring/catchment/static/_next/static/chunks/0p30fzde51na5.css +0 -1
- duckstring-0.4.0/src/duckstring/catchment/static/_next/static/chunks/0qdnkaffkg.2-.js +0 -2
- duckstring-0.4.0/src/duckstring/catchment/static/index.html +0 -1
- duckstring-0.4.0/src/duckstring/cli/pond.py +0 -234
- duckstring-0.4.0/src/duckstring/core.py +0 -678
- duckstring-0.4.0/src/duckstring/dataplane.py +0 -563
- duckstring-0.4.0/src/duckstring/duck/__main__.py +0 -193
- duckstring-0.4.0/src/duckstring/duck/executor.py +0 -208
- duckstring-0.4.0/src/duckstring/iceberg_catalog.py +0 -227
- duckstring-0.4.0/src/duckstring/iceberg_plane.py +0 -286
- duckstring-0.4.0/src/duckstring/schema_contract.py +0 -67
- duckstring-0.4.0/src/duckstring/trickle/acc.py +0 -139
- duckstring-0.4.0/src/duckstring/trickle/agg.py +0 -209
- duckstring-0.4.0/src/duckstring/utils.py +0 -3
- {duckstring-0.4.0 → duckstring-0.6.0}/LICENSE +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/setup.cfg +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/__main__.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/acc.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/agg.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/alerts/__init__.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/__init__.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/alert_worker.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/asgi.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/dag.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/routes/view.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/001_init.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/002_ducts.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/003_pull_m.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/004_identity.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/005_pond_version_schema.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/006_refresh.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/007_catchment_key.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/008_spout.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/009_spout_state.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/010_spout_wake.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/011_spout_window.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/012_spout_node.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/013_changed_f.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/014_alert.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/schema/016_alert_scope_major.sql +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/secrets.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/state_sync.py +0 -0
- {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
- {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
- {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
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/03~yq9q893hmn.js +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/07lhk_q6pmm3r.js +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/0dbhjjzl8qfwv.js +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/0jvmviuftg5e2.css +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/0kczw6usu-y-f.js +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/11kjahy2ntf0n.js +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/_next/static/chunks/turbopack-03~mbvk_uplk_.js +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/favicon.svg +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/logo-mark.svg +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/catchment/static/logo.svg +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/cli/config.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/catalog/.gitignore +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/catalog/README.md +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/catalog/pond.toml +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/orders/.gitignore +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/orders/README.md +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/orders/pond.toml +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/orders/src/pond.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/.gitignore +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/README.md +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/pond.toml +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/priced/src/puddles.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/products/.gitignore +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/products/README.md +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/products/pond.toml +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/products/src/pond.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/reports/.gitignore +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/reports/README.md +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/reports/pond.toml +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/reports/src/pond.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/.gitignore +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/README.md +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/pond.toml +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/src/pond.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/revenue/src/puddles.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/.gitignore +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/README.md +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/pond.toml +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/src/pond.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/sales/src/puddles.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/transactions/.gitignore +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/transactions/README.md +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/transactions/pond.toml +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/demo/transactions/src/pond.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/duck/__init__.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/egress/__init__.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/pond.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/engine/worker.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/keys.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/local/__init__.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/local/project.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle/__init__.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle/context.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle_builder.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring/trickle_io.py +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/dependency_links.txt +0 -0
- {duckstring-0.4.0 → duckstring-0.6.0}/src/duckstring.egg-info/entry_points.txt +0 -0
- {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,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: duckstring
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
250
|
+
**Get your ducks in a row.**
|
|
239
251
|
|
|
240
|
-
|
|
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
|
-
|
|
254
|
+
[Documentation](https://docs.duckstring.com) · [Quickstart](https://docs.duckstring.com/quickstart) · [Playground](https://playground.duckstring.com)
|
|
243
255
|
|
|
244
|
-
##
|
|
256
|
+
## Versioned transformations
|
|
245
257
|
|
|
246
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
260
|
+
```toml
|
|
261
|
+
[pond]
|
|
262
|
+
name = "sales"
|
|
263
|
+
version = "1.2.0"
|
|
252
264
|
|
|
253
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
278
|
-
|
|
279
|
-
|
|
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
|
|
332
|
+
The [Quickstart](https://docs.duckstring.com/quickstart) goes through the same steps with explanations.
|
|
288
333
|
|
|
289
|
-
##
|
|
334
|
+
## Also included
|
|
290
335
|
|
|
291
|
-
|
|
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
|
-
|
|
294
|
-
|---|---|---|
|
|
295
|
-
| **Push** | Pulse | Tide |
|
|
296
|
-
| **Pull** | Tap | Wave |
|
|
344
|
+
## Documentation
|
|
297
345
|
|
|
298
|
-
|
|
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
|
-
##
|
|
348
|
+
## Status
|
|
301
349
|
|
|
302
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|