interlaced 2.8.6__tar.gz → 2.8.7__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.
- {interlaced-2.8.6/src/interlaced.egg-info → interlaced-2.8.7}/PKG-INFO +1 -1
- {interlaced-2.8.6 → interlaced-2.8.7}/pyproject.toml +1 -1
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cli/operate.py +3 -2
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/app.py +4 -3
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/lifespan.py +53 -8
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/routes/models.py +7 -1
- {interlaced-2.8.6 → interlaced-2.8.7/src/interlaced.egg-info}/PKG-INFO +1 -1
- {interlaced-2.8.6 → interlaced-2.8.7}/LICENSE +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/MANIFEST.in +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/README.md +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/setup.cfg +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cdc/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cdc/decode.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cdc/publish.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cdc/slot.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/checks/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/checks/builtin.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/checks/runner.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/checks/spec.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cli/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cli/build.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cli/inspect.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cli/main.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/cli/support.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/config/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/config/config.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/connections.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/contracts.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/dsl/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/dsl/decorators.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/dsl/discovery.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/dsl/dynamic.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/dsl/sql_config.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/adbc.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/base.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/bigquery.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/duckdb.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/postgres.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/quack.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/redshift.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/registry.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/snowflake.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/engines/spark.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/exceptions.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/graph/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/graph/column_lineage.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/graph/dag.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/graph/project.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/graph/selectors.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/inputs.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/inspect.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/ir/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/ir/canonicalize.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/ir/dialect.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/ir/fingerprint.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/ir/layout.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/ir/macros.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/ir/relation.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/ir/vars.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/mcp_server.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/physical/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/physical/changes.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/physical/ddl.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/physical/drift.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/physical/reconcile.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/physical/spec.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/annotate.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/apply.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/backfill.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/comment.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/delivery.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/differ.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/files.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/fit.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/orchestrate.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/payload.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/plan.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/resolve.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/result.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/run.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/schedule.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/table_diff.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/plan/transfer.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/project.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/py.typed +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/query.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/runtime/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/runtime/handles.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/runtime/python_model.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/scaffold.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/scheduler/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/scheduler/completion.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/scheduler/daemon.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/scheduler/engine.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/scheduler/triggers.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/scheduler/worker.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/auth.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/present.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/routes/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/routes/admin.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/routes/catalog.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/routes/environments.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/routes/plan.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/routes/runs.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/routes/streams.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/types.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/app.css +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/favicon.svg +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/index.html +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/api.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/app.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/dag.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/timeline.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/ui.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/checks.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/environments.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/lineage.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/models.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/overview.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/plan.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/query.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/runs.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/streams.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/service/ui/js/views/system.js +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/sinks.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/sources/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/sources/auth.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/sources/rest.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/advisory.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/beat.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/cdc.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/checks.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/codec.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/conn.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/events.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/interval.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/janitor.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/keys.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/locks.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/pg.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/queue.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/schema.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/snapshot.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/snapshots.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/store.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/state/triggers.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/append.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/base.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/full_merge.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/hash_merge.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/incremental.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/merge.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/replace.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/replace_in_place.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/scd.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/strategies/view.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/streaming/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/streaming/log.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/streaming/materializer.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/streaming/schema.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/README.md +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/docker-compose.yml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/init/seed.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/interlace.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/models/orders.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/models/orders_by_status.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/models/orders_stream.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/push.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/template.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/README.md +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/generate.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/interlace.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/models/events.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/models/events_by_minute.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/models/events_by_type.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/models/top_users.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/models/user_spend.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/template.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/github/README.md +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/github/interlace.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/github/models/github_issues.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/github/models/issues_by_state.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/github/template.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/postgres/README.md +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/postgres/docker-compose.yml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/postgres/init/seed.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/postgres/interlace.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/postgres/models/orders.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/postgres/models/orders_by_status.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/postgres/template.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/README.md +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/interlace.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/models/enriched_events.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/models/event_summary.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/models/raw_events.sql +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/template.yaml +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/testing/__init__.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/testing/golden.py +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlaced.egg-info/SOURCES.txt +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlaced.egg-info/dependency_links.txt +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlaced.egg-info/entry_points.txt +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlaced.egg-info/requires.txt +0 -0
- {interlaced-2.8.6 → interlaced-2.8.7}/src/interlaced.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: interlaced
|
|
3
|
-
Version: 2.8.
|
|
3
|
+
Version: 2.8.7
|
|
4
4
|
Summary: Python and SQL models in one DAG. Transformation, orchestration and durable streaming in one process, on DuckDB and Postgres.
|
|
5
5
|
Author-email: Mark <mark@interlace.sh>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "interlaced"
|
|
7
|
-
version = "2.8.
|
|
7
|
+
version = "2.8.7"
|
|
8
8
|
description = "Python and SQL models in one DAG. Transformation, orchestration and durable streaming in one process, on DuckDB and Postgres."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -288,7 +288,7 @@ def serve(
|
|
|
288
288
|
apply_on_start: bool = typer.Option(
|
|
289
289
|
True,
|
|
290
290
|
"--apply/--no-apply",
|
|
291
|
-
help="Apply the
|
|
291
|
+
help="Apply once at startup, while the API is already listening. --no-apply serves the warehouse as it is.",
|
|
292
292
|
),
|
|
293
293
|
allow_open: bool = typer.Option(
|
|
294
294
|
False,
|
|
@@ -299,7 +299,8 @@ def serve(
|
|
|
299
299
|
"""Run the interlace daemon: HTTP API + scheduler in one process (requires the `service` extra).
|
|
300
300
|
|
|
301
301
|
Use --no-scheduler for an API-only process (run `interlace scheduler` separately).
|
|
302
|
-
The project is applied once before the
|
|
302
|
+
The API listens immediately. The project is applied once at startup, before the
|
|
303
|
+
scheduler loop; pass --no-apply to skip that. Writes return 503 until the apply finishes.
|
|
303
304
|
A `cdc:` block in the project config is read here: each Postgres slot appends into its stream.
|
|
304
305
|
"""
|
|
305
306
|
try:
|
|
@@ -19,7 +19,7 @@ from litestar.static_files import create_static_files_router
|
|
|
19
19
|
from interlace import __version__
|
|
20
20
|
from interlace.exceptions import BreakingPlanError, LockError
|
|
21
21
|
from interlace.service.auth import auth_guard
|
|
22
|
-
from interlace.service.lifespan import project_lifespan
|
|
22
|
+
from interlace.service.lifespan import project_lifespan, startup_guard
|
|
23
23
|
from interlace.service.routes.admin import (
|
|
24
24
|
delete_apikey,
|
|
25
25
|
get_apikeys,
|
|
@@ -90,7 +90,8 @@ def create_app(
|
|
|
90
90
|
``scheduler=True`` makes this the combined daemon: the HTTP API plus a
|
|
91
91
|
background scheduler loop (tick triggers, drain the run queue) in one
|
|
92
92
|
process — the default for ``interlace serve``. ``apply_on_start`` applies the
|
|
93
|
-
project once
|
|
93
|
+
project once while the API is already accepting connections; writes get
|
|
94
|
+
503 until that apply finishes. The ``serve`` command turns it on.
|
|
94
95
|
``quack`` (a
|
|
95
96
|
``quack:<host>:<port>`` URI) additionally serves the warehouse over the
|
|
96
97
|
quack protocol so other processes — CLI runs, ad-hoc DuckDB clients —
|
|
@@ -220,7 +221,7 @@ def create_app(
|
|
|
220
221
|
stream_events,
|
|
221
222
|
],
|
|
222
223
|
lifespan=[lifespan],
|
|
223
|
-
guards=[auth_guard],
|
|
224
|
+
guards=[auth_guard, startup_guard],
|
|
224
225
|
openapi_config=OpenAPIConfig(
|
|
225
226
|
title="interlace",
|
|
226
227
|
version=__version__,
|
|
@@ -17,7 +17,9 @@ from pathlib import Path
|
|
|
17
17
|
from typing import cast
|
|
18
18
|
|
|
19
19
|
from litestar import Litestar
|
|
20
|
-
from litestar.
|
|
20
|
+
from litestar.connection import ASGIConnection
|
|
21
|
+
from litestar.exceptions import ImproperlyConfiguredException, ServiceUnavailableException
|
|
22
|
+
from litestar.handlers.base import BaseRouteHandler
|
|
21
23
|
|
|
22
24
|
from interlace.graph.column_lineage import column_lineage
|
|
23
25
|
from interlace.project import Project
|
|
@@ -38,6 +40,21 @@ from interlace.streaming.materializer import (
|
|
|
38
40
|
)
|
|
39
41
|
|
|
40
42
|
|
|
43
|
+
async def startup_guard(connection: ASGIConnection, _route_handler: BaseRouteHandler) -> None:
|
|
44
|
+
"""Refuse writes until the startup apply has finished.
|
|
45
|
+
|
|
46
|
+
The socket is open while that apply runs, so a client gets this response
|
|
47
|
+
instead of a refused connection. Reads stay available.
|
|
48
|
+
"""
|
|
49
|
+
status = getattr(connection.app.state, "startup_status", "ok")
|
|
50
|
+
if status == "ok" or connection.scope.get("method", "GET") in ("GET", "HEAD", "OPTIONS"):
|
|
51
|
+
return
|
|
52
|
+
if status == "starting":
|
|
53
|
+
raise ServiceUnavailableException(detail="startup apply is still running")
|
|
54
|
+
detail = getattr(connection.app.state, "startup_error", "") or "startup apply failed"
|
|
55
|
+
raise ServiceUnavailableException(detail=detail)
|
|
56
|
+
|
|
57
|
+
|
|
41
58
|
def project_lifespan( # noqa: C901
|
|
42
59
|
root: Path | str,
|
|
43
60
|
environment: str,
|
|
@@ -140,6 +157,10 @@ def project_lifespan( # noqa: C901
|
|
|
140
157
|
|
|
141
158
|
if streams:
|
|
142
159
|
app.state.flush_wanted.set() # catch up anything durable but unflushed at last shutdown
|
|
160
|
+
# "starting" until the first apply finishes. The socket opens at the yield
|
|
161
|
+
# below, so clients reach /health instead of a refused connection.
|
|
162
|
+
app.state.startup_status = "starting" if apply_on_start else "ok"
|
|
163
|
+
app.state.startup_error = ""
|
|
143
164
|
|
|
144
165
|
async def shutdown_watch() -> None:
|
|
145
166
|
"""End every open SSE stream the moment uvicorn begins shutting down.
|
|
@@ -159,16 +180,36 @@ def project_lifespan( # noqa: C901
|
|
|
159
180
|
with contextlib.suppress(asyncio.QueueFull):
|
|
160
181
|
subscriber.put_nowait(None)
|
|
161
182
|
|
|
162
|
-
|
|
163
|
-
|
|
183
|
+
flusher_task: asyncio.Task[None] | None = None
|
|
184
|
+
loop_task: asyncio.Task[None] | None = None
|
|
185
|
+
cdc_task: asyncio.Task[None] | None = None
|
|
186
|
+
|
|
187
|
+
async def open_service() -> None:
|
|
188
|
+
"""Apply first, then the loops. The listen socket is already open."""
|
|
189
|
+
nonlocal flusher_task, loop_task, cdc_task
|
|
190
|
+
try:
|
|
191
|
+
if apply_on_start:
|
|
192
|
+
logger.info("startup apply running; the API is accepting connections")
|
|
193
|
+
await startup_apply(app.state)
|
|
194
|
+
app.state.startup_status = "ok"
|
|
195
|
+
except Exception as exc:
|
|
196
|
+
app.state.startup_status = "error"
|
|
197
|
+
app.state.startup_error = str(exc)
|
|
198
|
+
logger.exception("startup apply failed")
|
|
199
|
+
server = getattr(app.state, "uvicorn_server", None)
|
|
200
|
+
if server is not None:
|
|
201
|
+
server.should_exit = True
|
|
202
|
+
return
|
|
203
|
+
if streams:
|
|
204
|
+
flusher_task = asyncio.create_task(flusher_loop(app.state, flush_interval=stream_flush_interval))
|
|
205
|
+
if scheduler:
|
|
206
|
+
loop_task = asyncio.create_task(scheduler_loop(app.state, interval=scheduler_interval))
|
|
207
|
+
if project.config.cdc:
|
|
208
|
+
cdc_task = asyncio.create_task(cdc_loop(app.state))
|
|
164
209
|
|
|
165
210
|
tail_task = asyncio.create_task(event_tail())
|
|
166
|
-
flusher_task = (
|
|
167
|
-
asyncio.create_task(flusher_loop(app.state, flush_interval=stream_flush_interval)) if streams else None
|
|
168
|
-
)
|
|
169
|
-
loop_task = asyncio.create_task(scheduler_loop(app.state, interval=scheduler_interval)) if scheduler else None
|
|
170
|
-
cdc_task = asyncio.create_task(cdc_loop(app.state)) if project.config.cdc else None
|
|
171
211
|
watch_task = asyncio.create_task(shutdown_watch())
|
|
212
|
+
service_task = asyncio.create_task(open_service())
|
|
172
213
|
try:
|
|
173
214
|
yield
|
|
174
215
|
finally:
|
|
@@ -183,6 +224,10 @@ def project_lifespan( # noqa: C901
|
|
|
183
224
|
for subscriber in list(app.state.sse_subscribers):
|
|
184
225
|
with contextlib.suppress(asyncio.QueueFull):
|
|
185
226
|
subscriber.put_nowait(None)
|
|
227
|
+
# Stop the opener first so it cannot start a loop after this snapshot.
|
|
228
|
+
service_task.cancel()
|
|
229
|
+
with contextlib.suppress(asyncio.CancelledError, Exception):
|
|
230
|
+
await service_task
|
|
186
231
|
for task in (cdc_task, loop_task, flusher_task, tail_task, watch_task):
|
|
187
232
|
if task is not None:
|
|
188
233
|
task.cancel()
|
|
@@ -33,7 +33,13 @@ from interlace.service.types import (
|
|
|
33
33
|
|
|
34
34
|
@get("/health")
|
|
35
35
|
async def health(state: State) -> dict[str, str]:
|
|
36
|
-
|
|
36
|
+
status = getattr(state, "startup_status", "ok")
|
|
37
|
+
body = {"status": status, "version": __version__, "environment": state.environment}
|
|
38
|
+
if status == "starting":
|
|
39
|
+
body["detail"] = "startup apply is still running"
|
|
40
|
+
elif status == "error":
|
|
41
|
+
body["detail"] = getattr(state, "startup_error", "") or "startup apply failed"
|
|
42
|
+
return body
|
|
37
43
|
|
|
38
44
|
|
|
39
45
|
@get("/", include_in_schema=False)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: interlaced
|
|
3
|
-
Version: 2.8.
|
|
3
|
+
Version: 2.8.7
|
|
4
4
|
Summary: Python and SQL models in one DAG. Transformation, orchestration and durable streaming in one process, on DuckDB and Postgres.
|
|
5
5
|
Author-email: Mark <mark@interlace.sh>
|
|
6
6
|
License-Expression: MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/cdc/models/orders_by_status.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/models/events_by_minute.sql
RENAMED
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/events/models/events_by_type.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/github/models/github_issues.py
RENAMED
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/github/models/issues_by_state.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/postgres/models/orders_by_status.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/models/enriched_events.py
RENAMED
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/models/event_summary.sql
RENAMED
|
File without changes
|
{interlaced-2.8.6 → interlaced-2.8.7}/src/interlace/templates/quickstart/models/raw_events.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|