delta-engine 0.2.0__tar.gz → 0.4.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.
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/CLAUDE.md +38 -33
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/rules/documentation.md +1 -1
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/rules/testing.md +6 -7
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/skills/add-tests/SKILL.md +4 -3
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/workflows/ci.yaml +2 -1
- delta_engine-0.4.0/.github/workflows/docs.yaml +52 -0
- delta_engine-0.4.0/.github/workflows/live.yaml +92 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.gitignore +1 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/CHANGELOG.md +154 -0
- delta_engine-0.4.0/PKG-INFO +145 -0
- delta_engine-0.4.0/README.md +113 -0
- delta_engine-0.4.0/body.md +116 -0
- delta_engine-0.4.0/docs/conf.py +81 -0
- delta_engine-0.4.0/docs/explanation-architecture.md +758 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/explanation-safety-model.md +19 -12
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/explanation-sync-lifecycle.md +28 -25
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-add-action-type.md +59 -18
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-configure-table.md +288 -67
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-deploy-metadata-only.md +19 -17
- delta_engine-0.4.0/docs/how-to-gate-changes-in-ci.md +145 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-handle-sync-failures.md +7 -5
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-implement-adapter.md +40 -12
- delta_engine-0.4.0/docs/how-to-preview-changes.md +66 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/index.md +11 -6
- delta_engine-0.4.0/docs/installation.md +129 -0
- delta_engine-0.4.0/docs/reference-api.md +40 -0
- delta_engine-0.4.0/docs/reference-cli.md +153 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/reference-data-types.md +23 -5
- delta_engine-0.4.0/docs/reference-limitations.md +113 -0
- delta_engine-0.4.0/docs/reference-run-report.md +106 -0
- delta_engine-0.4.0/docs/reference-safe-change-rules.md +98 -0
- delta_engine-0.4.0/docs/todo/business-logic-delta-databricks-correctness-review.md +334 -0
- delta_engine-0.4.0/docs/todo/roadmap.md +319 -0
- delta_engine-0.4.0/docs/todo/todo.md +28 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/tutorial-getting-started.md +8 -9
- {delta_engine-0.2.0 → delta_engine-0.4.0}/notebooks/delta_engine_walkthrough.py +80 -58
- {delta_engine-0.2.0 → delta_engine-0.4.0}/pyproject.toml +92 -8
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/__init__.py +15 -1
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/__init__.py +15 -0
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/errors.py +55 -0
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/execution.py +87 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/log_config.py +6 -6
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/spark/__init__.py +9 -0
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/spark/executor.py +29 -0
- {delta_engine-0.2.0/src/delta_engine/adapters/databricks → delta_engine-0.4.0/src/delta_engine/adapters/databricks/spark}/factory.py +4 -4
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/spark/reader.py +244 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/sql/__init__.py +27 -8
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/sql/compile.py +66 -43
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/sql/parse.py +261 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/sql/queries.py +39 -0
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/sql/rows.py +229 -0
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/sql/types.py +85 -0
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/warehouse/executor.py +65 -0
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/warehouse/factory.py +26 -0
- delta_engine-0.4.0/src/delta_engine/adapters/databricks/warehouse/reader.py +178 -0
- delta_engine-0.4.0/src/delta_engine/api/delta_table.py +594 -0
- delta_engine-0.4.0/src/delta_engine/application/__init__.py +38 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/dependency_resolution.py +17 -7
- delta_engine-0.4.0/src/delta_engine/application/diff_entries.py +261 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/engine.py +71 -45
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/errors.py +10 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/failures.py +19 -9
- delta_engine-0.4.0/src/delta_engine/application/planning.py +55 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/ports.py +62 -24
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/properties.py +15 -4
- delta_engine-0.4.0/src/delta_engine/application/rendering.py +187 -0
- delta_engine-0.4.0/src/delta_engine/application/report.py +202 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/validation.py +194 -70
- delta_engine-0.4.0/src/delta_engine/cli/__init__.py +25 -0
- delta_engine-0.4.0/src/delta_engine/cli/__main__.py +6 -0
- delta_engine-0.4.0/src/delta_engine/cli/app.py +113 -0
- delta_engine-0.4.0/src/delta_engine/cli/connection.py +197 -0
- delta_engine-0.4.0/src/delta_engine/cli/declarations.py +134 -0
- delta_engine-0.4.0/src/delta_engine/cli/errors.py +10 -0
- delta_engine-0.4.0/src/delta_engine/cli/rendering.py +48 -0
- delta_engine-0.4.0/src/delta_engine/databricks.py +50 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/model/__init__.py +3 -4
- delta_engine-0.4.0/src/delta_engine/domain/model/column.py +83 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/model/constraints.py +20 -9
- delta_engine-0.4.0/src/delta_engine/domain/model/table.py +314 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/plan/__init__.py +11 -39
- delta_engine-0.4.0/src/delta_engine/domain/plan/actions.py +420 -0
- delta_engine-0.4.0/src/delta_engine/domain/plan/diff.py +471 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/schema.py +1 -1
- {delta_engine-0.2.0/tests/adapters/databricks → delta_engine-0.4.0/tests/adapters/databricks/spark}/test_build_engine.py +25 -2
- {delta_engine-0.2.0/tests/adapters/databricks → delta_engine-0.4.0/tests/adapters/databricks/spark}/test_executor.py +55 -139
- {delta_engine-0.2.0/tests/adapters/databricks → delta_engine-0.4.0/tests/adapters/databricks/spark}/test_reader.py +70 -43
- delta_engine-0.4.0/tests/adapters/databricks/spark/test_reader_mappers.py +93 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/adapters/databricks/sql/test_compile.py +127 -76
- delta_engine-0.4.0/tests/adapters/databricks/sql/test_parse.py +184 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/adapters/databricks/sql/test_queries.py +28 -0
- delta_engine-0.2.0/tests/adapters/databricks/test_reader_mappers.py → delta_engine-0.4.0/tests/adapters/databricks/sql/test_rows.py +58 -94
- delta_engine-0.4.0/tests/adapters/databricks/sql/test_types.py +51 -0
- delta_engine-0.4.0/tests/adapters/databricks/test_errors.py +56 -0
- delta_engine-0.4.0/tests/adapters/databricks/test_execution.py +115 -0
- delta_engine-0.4.0/tests/adapters/databricks/warehouse/test_build_engine.py +17 -0
- delta_engine-0.4.0/tests/adapters/databricks/warehouse/test_executor.py +125 -0
- delta_engine-0.4.0/tests/adapters/databricks/warehouse/test_reader.py +264 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/api/test_delta_table.py +338 -49
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/api/test_foreign_key.py +229 -97
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_dependency_resolution.py +94 -9
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_engine.py +218 -60
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_errors.py +18 -11
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_failures.py +30 -11
- delta_engine-0.4.0/tests/application/test_planning.py +346 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_ports.py +24 -27
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_properties.py +7 -2
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_public_api.py +37 -3
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_rendering.py +171 -65
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_report.py +183 -22
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_validation.py +332 -85
- delta_engine-0.4.0/tests/builders.py +26 -0
- delta_engine-0.4.0/tests/cli/__init__.py +0 -0
- delta_engine-0.4.0/tests/cli/conftest.py +133 -0
- delta_engine-0.4.0/tests/cli/test_app_plan.py +297 -0
- delta_engine-0.4.0/tests/cli/test_connection.py +290 -0
- delta_engine-0.4.0/tests/cli/test_declarations.py +226 -0
- delta_engine-0.4.0/tests/cli/test_entry.py +31 -0
- delta_engine-0.4.0/tests/domain/__init__.py +0 -0
- delta_engine-0.4.0/tests/domain/model/__init__.py +0 -0
- delta_engine-0.4.0/tests/domain/model/test_column.py +90 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_foreign_key.py +53 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_table.py +253 -54
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_table_aspect.py +9 -0
- delta_engine-0.4.0/tests/domain/plan/__init__.py +0 -0
- delta_engine-0.4.0/tests/domain/plan/test_actions.py +309 -0
- delta_engine-0.4.0/tests/domain/plan/test_diff.py +984 -0
- delta_engine-0.4.0/tests/e2e/__init__.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/e2e/conftest.py +2 -2
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/e2e/test_engine_e2e.py +261 -27
- delta_engine-0.4.0/tests/live/__init__.py +0 -0
- delta_engine-0.4.0/tests/live/conftest.py +167 -0
- delta_engine-0.4.0/tests/live/databricks_connection.py +73 -0
- delta_engine-0.4.0/tests/live/sql_warehouse_live_helpers.py +148 -0
- delta_engine-0.4.0/tests/live/test_sql_warehouse_live.py +303 -0
- delta_engine-0.4.0/tests/live/test_sql_warehouse_live_constraints.py +237 -0
- delta_engine-0.4.0/tests/live/test_sql_warehouse_live_platform_assumptions.py +223 -0
- delta_engine-0.4.0/tests/live/test_sql_warehouse_live_renames.py +327 -0
- delta_engine-0.4.0/tests/live/test_sql_warehouse_live_safety.py +495 -0
- delta_engine-0.4.0/tests/live/test_sql_warehouse_live_scopes.py +111 -0
- delta_engine-0.4.0/tests/live/test_sql_warehouse_live_types_and_layout.py +357 -0
- delta_engine-0.4.0/tests/test_packaging.py +62 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/test_public_api.py +32 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/test_public_imports.py +31 -6
- {delta_engine-0.2.0 → delta_engine-0.4.0}/uv.lock +575 -6
- delta_engine-0.2.0/PKG-INFO +0 -116
- delta_engine-0.2.0/README.md +0 -90
- delta_engine-0.2.0/body.md +0 -28
- delta_engine-0.2.0/docs/conf.py +0 -31
- delta_engine-0.2.0/docs/explanation-architecture.md +0 -630
- delta_engine-0.2.0/docs/how-to-preview-changes.md +0 -57
- delta_engine-0.2.0/docs/installation.md +0 -59
- delta_engine-0.2.0/docs/reference-api.md +0 -120
- delta_engine-0.2.0/docs/reference-limitations.md +0 -72
- delta_engine-0.2.0/docs/reference-safe-change-rules.md +0 -48
- delta_engine-0.2.0/docs/todo/todo.md +0 -25
- delta_engine-0.2.0/src/delta_engine/adapters/databricks/__init__.py +0 -8
- delta_engine-0.2.0/src/delta_engine/adapters/databricks/errors.py +0 -53
- delta_engine-0.2.0/src/delta_engine/adapters/databricks/executor.py +0 -120
- delta_engine-0.2.0/src/delta_engine/adapters/databricks/reader.py +0 -386
- delta_engine-0.2.0/src/delta_engine/adapters/databricks/sql/types.py +0 -185
- delta_engine-0.2.0/src/delta_engine/api/delta_table.py +0 -436
- delta_engine-0.2.0/src/delta_engine/application/__init__.py +0 -23
- delta_engine-0.2.0/src/delta_engine/application/desired_tables.py +0 -50
- delta_engine-0.2.0/src/delta_engine/application/rendering.py +0 -355
- delta_engine-0.2.0/src/delta_engine/application/report.py +0 -97
- delta_engine-0.2.0/src/delta_engine/databricks.py +0 -35
- delta_engine-0.2.0/src/delta_engine/domain/model/column.py +0 -46
- delta_engine-0.2.0/src/delta_engine/domain/model/table.py +0 -236
- delta_engine-0.2.0/src/delta_engine/domain/plan/actions.py +0 -337
- delta_engine-0.2.0/src/delta_engine/domain/plan/changes.py +0 -431
- delta_engine-0.2.0/src/delta_engine/domain/plan/diff.py +0 -370
- delta_engine-0.2.0/tests/adapters/databricks/sql/test_types.py +0 -163
- delta_engine-0.2.0/tests/adapters/databricks/test_errors.py +0 -38
- delta_engine-0.2.0/tests/application/test_desired_tables.py +0 -108
- delta_engine-0.2.0/tests/domain/model/test_column.py +0 -59
- delta_engine-0.2.0/tests/domain/plan/test_actions.py +0 -279
- delta_engine-0.2.0/tests/domain/plan/test_diff.py +0 -891
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/agents/ousterhout-design-reviewer.md +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/agents/test-reviewer.md +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/rules/coding-style.md +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/rules/domain-architecture.md +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/pull_request_template.md +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/workflows/pr-title.yaml +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/workflows/publish-testpypi.yaml +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/workflows/release.yaml +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/.pre-commit-config.yaml +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/LICENSE +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/notebooks/README.md +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/notebooks/catalog_inspector.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/__init__.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/sql/dialect.py +0 -0
- {delta_engine-0.2.0/src/delta_engine/domain → delta_engine-0.4.0/src/delta_engine/adapters/databricks/warehouse}/__init__.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/api/__init__.py +0 -0
- {delta_engine-0.2.0/tests → delta_engine-0.4.0/src/delta_engine/domain}/__init__.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/model/data_type.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/model/qualified_name.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/py.typed +0 -0
- {delta_engine-0.2.0/tests/adapters/databricks → delta_engine-0.4.0/tests}/__init__.py +0 -0
- {delta_engine-0.2.0/tests/adapters/databricks/sql → delta_engine-0.4.0/tests/adapters}/__init__.py +0 -0
- {delta_engine-0.2.0/tests/api → delta_engine-0.4.0/tests/adapters/databricks}/__init__.py +0 -0
- {delta_engine-0.2.0/tests/application → delta_engine-0.4.0/tests/adapters/databricks/spark}/__init__.py +0 -0
- {delta_engine-0.2.0/tests/domain → delta_engine-0.4.0/tests/adapters/databricks/sql}/__init__.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/adapters/databricks/sql/test_dialect.py +0 -0
- {delta_engine-0.2.0/tests/domain/model → delta_engine-0.4.0/tests/adapters/databricks/warehouse}/__init__.py +0 -0
- {delta_engine-0.2.0/tests/domain/plan → delta_engine-0.4.0/tests/api}/__init__.py +0 -0
- {delta_engine-0.2.0/tests/e2e → delta_engine-0.4.0/tests/application}/__init__.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/config.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/conftest.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_data_type.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_primary_key.py +0 -0
- {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_qualified_name.py +0 -0
|
@@ -10,16 +10,17 @@ This file contains project-level instructions that should be available in every
|
|
|
10
10
|
|
|
11
11
|
Main source layout:
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
- `src/delta_engine/domain`: backend-free domain model, table snapshots, diffs, actions, and deterministic plans.
|
|
14
|
+
- `src/delta_engine/application`: orchestration, ports, validation, dependency resolution, reporting, and errors.
|
|
15
|
+
- `src/delta_engine/adapters`: backend integration. The Databricks adapter has two backends: Spark and SQL warehouse.
|
|
16
|
+
- `src/delta_engine/api`: implementation of public declaration objects.
|
|
17
|
+
- `src/delta_engine/schema.py`: public schema declaration import surface.
|
|
18
|
+
- `src/delta_engine/databricks.py`: public Databricks helper import surface with lazy adapter imports.
|
|
19
|
+
- `src/delta_engine/cli`: read-only `plan` command over the warehouse backend; requires the `cli` extra.
|
|
20
|
+
- `tests`: unit, integration, adapter, and end-to-end tests.
|
|
21
|
+
- `docs`: Sphinx/MyST documentation.
|
|
22
|
+
|
|
23
|
+
The library should remain importable for schema declaration and planning without requiring PySpark unless the Spark backend is used; the SQL warehouse backend runs without PySpark entirely.
|
|
23
24
|
|
|
24
25
|
## Commands
|
|
25
26
|
|
|
@@ -35,7 +36,7 @@ Run tests:
|
|
|
35
36
|
uv run pytest
|
|
36
37
|
uv run pytest tests/domain/plan/test_diff.py
|
|
37
38
|
uv run pytest tests/domain/plan/test_diff.py::test_name
|
|
38
|
-
uv run pytest -m "not local_e2e"
|
|
39
|
+
uv run pytest -m "not local_e2e and not databricks_e2e"
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
The first test run starts a local Spark session through the `spark` fixture in `tests/conftest.py` and can take 30-60 seconds.
|
|
@@ -90,7 +91,7 @@ uv run lint-imports
|
|
|
90
91
|
uv run --group docs sphinx-build -b html docs docs/_build/html -W
|
|
91
92
|
```
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
This repo carries no tests that need Databricks credentials; the full suite runs locally. Live verification against a real workspace happens in a separate project outside this repo.
|
|
94
95
|
|
|
95
96
|
## Architecture boundaries
|
|
96
97
|
|
|
@@ -100,20 +101,23 @@ The import architecture is enforced by `import-linter` in `pyproject.toml`. Do n
|
|
|
100
101
|
|
|
101
102
|
Rules:
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
- `domain` must stay backend-free, immutable, and deterministic.
|
|
105
|
+
- `application` owns orchestration, ports, safety policy, dependency resolution, reports, and failure propagation.
|
|
106
|
+
- `adapters` own backend integration, SQL compilation, Spark/Databricks parsing, identifier quoting, and backend exception translation.
|
|
107
|
+
- `api` owns public declaration implementation and lowers declarations into domain snapshots.
|
|
108
|
+
- `schema.py` and `databricks.py` are user-facing facades; keep them thin.
|
|
109
|
+
- `cli.connection` may import the Databricks SDK and SQL connector only to
|
|
110
|
+
resolve unified authentication and own a connection; backend reads and
|
|
111
|
+
execution stay in adapters.
|
|
112
|
+
- PySpark, Delta, Py4J, Spark SQL details, and Databricks-specific assumptions must not leak into `domain` or `application`.
|
|
109
113
|
|
|
110
114
|
Expected dependency direction:
|
|
111
115
|
|
|
112
116
|
```text
|
|
113
|
-
databricks | schema | adapters | api -> application -> domain
|
|
117
|
+
cli -> databricks | schema | adapters | api -> application -> domain
|
|
114
118
|
```
|
|
115
119
|
|
|
116
|
-
|
|
120
|
+
Two more import-linter contracts forbid `delta` and `pyspark` imports: one covering `schema`, `api`, `application`, and `domain`; another covering `cli`, with a carve-out for the one legitimate edge (the lazy Spark facade import). A further contract confines `typer`/`click`/`rich` imports to `cli`.
|
|
117
121
|
|
|
118
122
|
## Sync lifecycle
|
|
119
123
|
|
|
@@ -134,8 +138,8 @@ A table that fails an early phase keeps that failure in its report and is skippe
|
|
|
134
138
|
|
|
135
139
|
Both application ports are total from the engine's perspective:
|
|
136
140
|
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
- `CatalogStateReader.fetch_state(...)` returns `TablePresent`, `TableAbsent`, or `ReadFailed`.
|
|
142
|
+
- `PlanExecutor.execute(...)` returns `ExecutionSummary`.
|
|
139
143
|
|
|
140
144
|
Adapters should catch backend exceptions and convert them into typed failures rather than raising backend-specific exceptions through the port.
|
|
141
145
|
|
|
@@ -153,8 +157,8 @@ Action ordering is defined by `ActionPhase` and then by action `subject`.
|
|
|
153
157
|
|
|
154
158
|
Properties and tags intentionally use different semantics:
|
|
155
159
|
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
- Properties use exact-declaration semantics. A declared `None` asserts absence and plans an unset when present.
|
|
161
|
+
- Tags use full-state semantics. An observed-only tag is drift and should be unset when tags are managed.
|
|
158
162
|
|
|
159
163
|
`managed_aspects` is part of the safety model. Drift outside the aspects managed by a declaration should fail validation rather than be silently reconciled.
|
|
160
164
|
|
|
@@ -178,16 +182,17 @@ Foreign-key declarations reference the target `DeltaTable` object, or `Self`, ra
|
|
|
178
182
|
| Report/output formatting | `src/delta_engine/application/report.py` and `src/delta_engine/application/rendering.py` |
|
|
179
183
|
| Databricks SQL generation | `src/delta_engine/adapters/databricks/sql` |
|
|
180
184
|
| Public Databricks helper | `src/delta_engine/databricks.py` |
|
|
185
|
+
| CLI commands, output, exit codes | `src/delta_engine/cli` |
|
|
181
186
|
| Documentation | `docs` |
|
|
182
187
|
|
|
183
188
|
Before changing architecture, action planning, validation, adapters, or public behaviour, read the relevant docs instead of guessing:
|
|
184
189
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
- `docs/explanation-architecture.md`
|
|
191
|
+
- `docs/how-to-implement-adapter.md`
|
|
192
|
+
- `docs/how-to-add-action-type.md`
|
|
193
|
+
- `docs/reference-safe-change-rules.md`
|
|
194
|
+
- `docs/how-to-handle-sync-failures.md`
|
|
195
|
+
- `docs/how-to-deploy-metadata-only.md`
|
|
191
196
|
|
|
192
197
|
## Documentation
|
|
193
198
|
|
|
@@ -209,6 +214,6 @@ When a change touches domain, application, adapter, and docs, keep the conceptua
|
|
|
209
214
|
|
|
210
215
|
Before finishing, state:
|
|
211
216
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
217
|
+
- what changed
|
|
218
|
+
- what checks were run
|
|
219
|
+
- what risks or follow-ups remain
|
|
@@ -79,7 +79,7 @@ Use public import paths in user-facing examples:
|
|
|
79
79
|
|
|
80
80
|
```python
|
|
81
81
|
from delta_engine.schema import DeltaTable
|
|
82
|
-
from delta_engine.databricks import
|
|
82
|
+
from delta_engine.databricks import build_spark_engine
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
Avoid examples that import from internal modules unless the doc is explicitly for contributors.
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
paths:
|
|
4
|
-
|
|
5
|
-
- "tests/**/*.py"
|
|
6
|
-
|
|
3
|
+
- "tests/**/*.py"
|
|
7
4
|
---
|
|
8
5
|
|
|
9
6
|
# Testing rules
|
|
@@ -54,7 +51,9 @@ For Databricks executor changes, assert execution summaries, successful action r
|
|
|
54
51
|
|
|
55
52
|
Do not require real Databricks for ordinary unit tests.
|
|
56
53
|
|
|
57
|
-
|
|
54
|
+
This repo carries no tests that need Databricks credentials; live verification against Unity Catalog happens in a separate project outside this repo.
|
|
55
|
+
|
|
56
|
+
If a credentialed Databricks test is ever added, it must be opt-in through the `databricks_e2e` marker and must not run in default CI.
|
|
58
57
|
|
|
59
58
|
Local Spark/Delta end-to-end tests should stay marked with `local_e2e`.
|
|
60
59
|
|
|
@@ -78,7 +77,7 @@ uv run pytest
|
|
|
78
77
|
To skip local Spark/Delta end-to-end tests:
|
|
79
78
|
|
|
80
79
|
```bash
|
|
81
|
-
uv run pytest -m "not local_e2e"
|
|
80
|
+
uv run pytest -m "not local_e2e and not databricks_e2e"
|
|
82
81
|
```
|
|
83
82
|
|
|
84
|
-
|
|
83
|
+
Every test in this repo runs locally without Databricks credentials.
|
|
@@ -302,7 +302,7 @@ def test_metadata_only_declaration_carries_properties_without_deploying_them():
|
|
|
302
302
|
|
|
303
303
|
### Outgoing Interaction Test
|
|
304
304
|
|
|
305
|
-
Use a fake only when the behaviour is the outgoing interaction — here, the executor handing compiled SQL to Spark. This project's own adapter tests use exactly this pattern (see `tests/adapters/databricks/test_executor.py`).
|
|
305
|
+
Use a fake only when the behaviour is the outgoing interaction — here, the executor handing compiled SQL to Spark. This project's own adapter tests use exactly this pattern (see `tests/adapters/databricks/spark/test_executor.py`).
|
|
306
306
|
|
|
307
307
|
```python
|
|
308
308
|
class _FakeSpark:
|
|
@@ -318,11 +318,12 @@ class _FakeSpark:
|
|
|
318
318
|
def test_executor_runs_the_compiled_statement_against_spark():
|
|
319
319
|
# given
|
|
320
320
|
spark = _FakeSpark()
|
|
321
|
-
executor =
|
|
321
|
+
executor = SparkExecutor(spark)
|
|
322
322
|
plan = ActionPlan((SetTableComment("new comment"),))
|
|
323
323
|
|
|
324
324
|
# when
|
|
325
|
-
executor.
|
|
325
|
+
statements = executor.compile(QualifiedName("catalog", "schema", "table"), plan)
|
|
326
|
+
executor.execute(statements)
|
|
326
327
|
|
|
327
328
|
# then
|
|
328
329
|
assert len(spark.executed) == 1
|
|
@@ -37,9 +37,10 @@ jobs:
|
|
|
37
37
|
schema="silver",
|
|
38
38
|
name="customers",
|
|
39
39
|
columns=[
|
|
40
|
-
Column("id", Integer(), nullable=False
|
|
40
|
+
Column("id", Integer(), nullable=False),
|
|
41
41
|
Column("name", String(), nullable=True),
|
|
42
42
|
],
|
|
43
|
+
primary_key=["id"],
|
|
43
44
|
)
|
|
44
45
|
|
|
45
46
|
desired = table.to_desired_table()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: pages
|
|
15
|
+
cancel-in-progress: false
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v5
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
cache-dependency-glob: uv.lock
|
|
28
|
+
|
|
29
|
+
- name: Set up Python
|
|
30
|
+
run: uv python install 3.12
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: uv sync --locked --group docs
|
|
34
|
+
|
|
35
|
+
- name: Build docs
|
|
36
|
+
run: uv run sphinx-build -W -b html docs docs/_build/html
|
|
37
|
+
|
|
38
|
+
- name: Upload Pages artifact
|
|
39
|
+
uses: actions/upload-pages-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
path: docs/_build/html
|
|
42
|
+
|
|
43
|
+
deploy:
|
|
44
|
+
needs: build
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
environment:
|
|
47
|
+
name: github-pages
|
|
48
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
49
|
+
steps:
|
|
50
|
+
- name: Deploy to GitHub Pages
|
|
51
|
+
id: deployment
|
|
52
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Credentialed live suite against a real Databricks SQL warehouse.
|
|
2
|
+
#
|
|
3
|
+
# Required repository variables:
|
|
4
|
+
# DATABRICKS_HOST workspace URL
|
|
5
|
+
# DATABRICKS_HTTP_PATH SQL warehouse endpoint path
|
|
6
|
+
# DELTA_ENGINE_E2E_CATALOG catalog the suite may create tables in
|
|
7
|
+
# DELTA_ENGINE_E2E_SCHEMA schema the suite may create tables in
|
|
8
|
+
# Required repository secret (either one):
|
|
9
|
+
# DATABRICKS_TOKEN personal access token, or
|
|
10
|
+
# DATABRICKS_CLIENT_ID service principal for OIDC workload identity
|
|
11
|
+
# federation (needs a Databricks federation
|
|
12
|
+
# policy for this repository)
|
|
13
|
+
name: Live Databricks Tests
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
schedule:
|
|
18
|
+
# Weekly drift check (Mondays 07:00 UTC): catches Databricks-side
|
|
19
|
+
# behaviour changes the hermetic suite cannot see. Non-blocking — it
|
|
20
|
+
# never gates a PR.
|
|
21
|
+
- cron: "0 7 * * 1"
|
|
22
|
+
|
|
23
|
+
# One live run at a time: the suite shares a schema and a warehouse.
|
|
24
|
+
concurrency:
|
|
25
|
+
group: ${{ github.workflow }}
|
|
26
|
+
cancel-in-progress: false
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
id-token: write
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
live:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
timeout-minutes: 45
|
|
36
|
+
env:
|
|
37
|
+
DATABRICKS_HOST: ${{ vars.DATABRICKS_HOST }}
|
|
38
|
+
DATABRICKS_HTTP_PATH: ${{ vars.DATABRICKS_HTTP_PATH }}
|
|
39
|
+
DELTA_ENGINE_E2E_CATALOG: ${{ vars.DELTA_ENGINE_E2E_CATALOG }}
|
|
40
|
+
DELTA_ENGINE_E2E_SCHEMA: ${{ vars.DELTA_ENGINE_E2E_SCHEMA }}
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v5
|
|
43
|
+
|
|
44
|
+
- name: Require live configuration
|
|
45
|
+
# Missing configuration must fail loudly here: without these the
|
|
46
|
+
# suite's fixtures skip every test and the run would pass green.
|
|
47
|
+
run: |
|
|
48
|
+
missing=0
|
|
49
|
+
for name in DATABRICKS_HOST DATABRICKS_HTTP_PATH DELTA_ENGINE_E2E_CATALOG DELTA_ENGINE_E2E_SCHEMA; do
|
|
50
|
+
if [ -z "${!name}" ]; then
|
|
51
|
+
echo "::error::repository variable ${name} is not set"
|
|
52
|
+
missing=1
|
|
53
|
+
fi
|
|
54
|
+
done
|
|
55
|
+
exit "${missing}"
|
|
56
|
+
|
|
57
|
+
- name: Select Databricks authentication
|
|
58
|
+
env:
|
|
59
|
+
TOKEN: ${{ secrets.DATABRICKS_TOKEN }}
|
|
60
|
+
CLIENT_ID: ${{ secrets.DATABRICKS_CLIENT_ID }}
|
|
61
|
+
run: |
|
|
62
|
+
if [ -n "${TOKEN}" ]; then
|
|
63
|
+
echo "DATABRICKS_TOKEN=${TOKEN}" >> "$GITHUB_ENV"
|
|
64
|
+
echo "authenticating with a personal access token"
|
|
65
|
+
elif [ -n "${CLIENT_ID}" ]; then
|
|
66
|
+
echo "DATABRICKS_CLIENT_ID=${CLIENT_ID}" >> "$GITHUB_ENV"
|
|
67
|
+
echo "DATABRICKS_AUTH_TYPE=github-oidc" >> "$GITHUB_ENV"
|
|
68
|
+
echo "authenticating with OIDC workload identity federation"
|
|
69
|
+
else
|
|
70
|
+
echo "::error::set the DATABRICKS_TOKEN or DATABRICKS_CLIENT_ID repository secret"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
- name: Install uv
|
|
75
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
76
|
+
with:
|
|
77
|
+
enable-cache: true
|
|
78
|
+
cache-dependency-glob: uv.lock
|
|
79
|
+
|
|
80
|
+
- name: Set up Python
|
|
81
|
+
run: uv python install 3.12
|
|
82
|
+
|
|
83
|
+
- name: Install dependencies
|
|
84
|
+
run: uv sync --locked
|
|
85
|
+
|
|
86
|
+
- name: Run live suite
|
|
87
|
+
# -n 8: the suite is I/O-bound on warehouse round-trips, not runner
|
|
88
|
+
# CPU, so running more workers than cores still speeds it up. Tests are
|
|
89
|
+
# isolated (uuid-named tables, per-test cleanup), so they parallelise
|
|
90
|
+
# safely; excess concurrency degrades to warehouse queueing, not
|
|
91
|
+
# failure. Tune to the warehouse's concurrency headroom.
|
|
92
|
+
run: uv run pytest tests/live -m databricks_e2e --no-cov -rA -n 8
|
|
@@ -1,3 +1,157 @@
|
|
|
1
|
+
## v0.4.0 (2026-07-14)
|
|
2
|
+
|
|
3
|
+
### BREAKING CHANGE
|
|
4
|
+
|
|
5
|
+
- public result/failure types lose statement_preview in
|
|
6
|
+
favour of statement; failure text renders 'SQL:' instead of 'SQL preview:'.
|
|
7
|
+
- install with delta-engine[spark] instead of
|
|
8
|
+
delta-engine[databricks]; import build_spark_engine instead of build_engine
|
|
9
|
+
from delta_engine.databricks.
|
|
10
|
+
- ExecutionFailure.statement_index replaces
|
|
11
|
+
action_index; the run report's execution.total now counts planned
|
|
12
|
+
statements.
|
|
13
|
+
- SyncReport.any_failures is now SyncReport.has_failures,
|
|
14
|
+
mirroring TableRunReport.has_failures.
|
|
15
|
+
|
|
16
|
+
### Feat
|
|
17
|
+
|
|
18
|
+
- render column renames in sync reports
|
|
19
|
+
- compile RenameColumn to ALTER TABLE ... RENAME COLUMN
|
|
20
|
+
- reject ambiguous declared renames; point guard at renamed_from
|
|
21
|
+
- relabel observed columns through declared renames in diff_table
|
|
22
|
+
- add ColumnRenamed change and RenameColumn action
|
|
23
|
+
- require column mapping at construction for rename hints
|
|
24
|
+
- enforce rename-hint coherence on DesiredTable
|
|
25
|
+
- add renamed_from declaration hint to Column
|
|
26
|
+
- adopt unified auth and explicit declaration selection in the CLI
|
|
27
|
+
- add DuplicateTableDefinitionError and extract prepare_desired_tables
|
|
28
|
+
- wire the delta-engine console script with graceful degradation
|
|
29
|
+
- add delta-engine apply command
|
|
30
|
+
- add delta-engine plan command
|
|
31
|
+
- add render_planned_sql for planned-statement previews
|
|
32
|
+
- add CLI warehouse connection resolution
|
|
33
|
+
- add CLI declaration loading for module[:attr] specs
|
|
34
|
+
- expose build_sql_engine for PySpark-free warehouse syncs
|
|
35
|
+
- add warehouse plan executor
|
|
36
|
+
- add warehouse catalog state reader
|
|
37
|
+
- add information_schema table-row and columns query builders
|
|
38
|
+
- add PySpark-free DDL type-string parser to the shared sql package
|
|
39
|
+
- export TableRunReport and concrete failure types
|
|
40
|
+
- add to_dict projection on sync and table run reports
|
|
41
|
+
- record compiled SQL statements on every table run report
|
|
42
|
+
- add PlanExecutor.compile returning the plan's SQL statements
|
|
43
|
+
- add has_changes and rename SyncReport.any_failures to has_failures
|
|
44
|
+
- adopt the full Delta type-widening matrix
|
|
45
|
+
- permit safe type widenings gated on delta.enableTypeWidening
|
|
46
|
+
- lower column type changes to AlterColumnType
|
|
47
|
+
- add AlterColumnType action with SQL compilation and diff rendering
|
|
48
|
+
- register delta.enableTypeWidening as a managed property
|
|
49
|
+
|
|
50
|
+
### Fix
|
|
51
|
+
|
|
52
|
+
- Update verification table header in conftest.py
|
|
53
|
+
- enforce Databricks 256-character tag key and value limits
|
|
54
|
+
- harden correctness edge cases
|
|
55
|
+
- compile empty column comments as COMMENT '' instead of UNSET COMMENT
|
|
56
|
+
- register the parent in the FK-rename live sync
|
|
57
|
+
- harden declarative column rename planning
|
|
58
|
+
- filter lazy connector warning
|
|
59
|
+
- reject path-like declaration references and honour module __getattr__
|
|
60
|
+
- suppress irrelevant PyArrow warning
|
|
61
|
+
- close a false-positive gap in the CLI PySpark-free contract
|
|
62
|
+
- compile empty column comments as COMMENT '' instead of UNSET COMMENT
|
|
63
|
+
- make WarehouseExecutor.execute total across cursor lifecycle
|
|
64
|
+
- treat pathological nesting depth as unmappable in the type parser
|
|
65
|
+
- lint
|
|
66
|
+
|
|
67
|
+
### Refactor
|
|
68
|
+
|
|
69
|
+
- plan explicit constraint drops around column renames
|
|
70
|
+
- rename domain Column to DesiredColumn
|
|
71
|
+
- drop compiler-facing alias properties from actions
|
|
72
|
+
- split table drift into actions and findings
|
|
73
|
+
- remove primary-key replacement correlation
|
|
74
|
+
- clarify planning domain boundaries
|
|
75
|
+
- collapse changes into canonical actions
|
|
76
|
+
- single-source the duplicate-table rule in engine preparation
|
|
77
|
+
- delegate CLI authentication to Databricks SDK
|
|
78
|
+
- narrow CLI to read-only OIDC plans
|
|
79
|
+
- fail fast on a missing HTTP path and pin cwd import precedence
|
|
80
|
+
- remove TableSnapshot in favour of standalone table types
|
|
81
|
+
- readers build ObservedColumn for catalog state
|
|
82
|
+
- split observed catalog columns into ObservedColumn
|
|
83
|
+
- fold desired-table preparation into the engine, move DesiredTableSource to ports
|
|
84
|
+
- route reader imports through the sql facade, widen rows.py framing
|
|
85
|
+
- acquire the warehouse cursor lazily inside the execution loop
|
|
86
|
+
- share exception naming via duck-typed py4j probe, delete the injection seam
|
|
87
|
+
- read Spark catalog types through the shared DDL parser, delete spark/types.py
|
|
88
|
+
- move the shared per-column read policy behind column_from_catalog
|
|
89
|
+
- fuse DESCRIBE DETAIL properties mapping into one boundary function
|
|
90
|
+
- share quoted-token reading between backtick names and string literals
|
|
91
|
+
- narrow the exception seam to type naming, delete errors.py
|
|
92
|
+
- share DESCRIBE DETAIL row mapping, tolerant of both shapes
|
|
93
|
+
- rename sql_type_for_data_type to render_data_type
|
|
94
|
+
- record exact statements and exception messages on failures
|
|
95
|
+
- align execution and error-translation vocabulary
|
|
96
|
+
- move information_schema row mappers into the shared sql package
|
|
97
|
+
- extract shared statement-execution loop from the spark executor
|
|
98
|
+
- extract shared exception-summarising core from the spark backend
|
|
99
|
+
- rename the databricks extra to spark and build_engine to build_spark_engine
|
|
100
|
+
- enforce the PySpark-free shared sql core with import-linter
|
|
101
|
+
- rename Databricks reader and executor to Spark names
|
|
102
|
+
- split Spark type parsing out of the shared sql package
|
|
103
|
+
- move Spark-coupled adapter code into a spark subpackage
|
|
104
|
+
- denominate execution results in statements
|
|
105
|
+
- polish the CI report contract before it ships
|
|
106
|
+
- execute compiled statements instead of recompiling the plan
|
|
107
|
+
- fold compile back into the plan phase
|
|
108
|
+
- extract compile into its own sync phase
|
|
109
|
+
- compile plans unconditionally, dropping the empty-plan guard
|
|
110
|
+
- extract action interpretation into diff_entries module
|
|
111
|
+
- reintroduce _SelfReference class in delta_table.py
|
|
112
|
+
- state the FK name-collision fact without API name-generation advice
|
|
113
|
+
- dedupe partition/clustering key-list validation in TableSnapshot
|
|
114
|
+
- name the resolved referenced side of a foreign key
|
|
115
|
+
- merge partition and clustering validators into _validate_layout
|
|
116
|
+
|
|
117
|
+
## v0.3.0 (2026-07-10)
|
|
118
|
+
|
|
119
|
+
### BREAKING CHANGE
|
|
120
|
+
|
|
121
|
+
- DeltaTable(metadata_only=True) is replaced by
|
|
122
|
+
DeltaTable(scope="metadata"). The metadata_only parameter is removed.
|
|
123
|
+
|
|
124
|
+
### Feat
|
|
125
|
+
|
|
126
|
+
- **api**: declare sync scope with a scope parameter
|
|
127
|
+
- declare foreign key pairings as an explicit mapping
|
|
128
|
+
- declare primary keys at table level
|
|
129
|
+
- canonicalize foreign key pair order in the domain
|
|
130
|
+
- derive clustering from Column.cluster_key in DeltaTable
|
|
131
|
+
- read observed clustering from DESCRIBE DETAIL clusteringColumns
|
|
132
|
+
- diff clustering by set and emit AlterClustering
|
|
133
|
+
- render clustering changes with an OPTIMIZE FULL hint
|
|
134
|
+
- compile CLUSTER BY for create and in-place clustering
|
|
135
|
+
- add AlterClustering action and SET_CLUSTERING phase
|
|
136
|
+
- carry clustered_by on table snapshots with invariants
|
|
137
|
+
- add CLUSTERING aspect and Column.cluster_key flag
|
|
138
|
+
|
|
139
|
+
### Fix
|
|
140
|
+
|
|
141
|
+
- reject a bare string primary_key at declaration
|
|
142
|
+
- **notebooks**: migrate walkthrough to the scope parameter
|
|
143
|
+
- update runtime-import CI smoke test to table-level primary_key
|
|
144
|
+
- drop OPTIMIZE FULL hint from clustering removal; note clustering in metadata-only docs
|
|
145
|
+
- reconcile clustering before dropping columns
|
|
146
|
+
- **test**: make DESCRIBE DETAIL reader fakes support asDict()
|
|
147
|
+
|
|
148
|
+
### Refactor
|
|
149
|
+
|
|
150
|
+
- **api**: clean up scope resolution and structure-gated validation
|
|
151
|
+
- wrap ForeignKey.columns in a read-only view; drop stale inference wording
|
|
152
|
+
- drop redundant clustering guard and trim docstrings
|
|
153
|
+
- declare clustering with table-level clustered_by, not a per-column flag
|
|
154
|
+
|
|
1
155
|
## v0.2.0 (2026-07-09)
|
|
2
156
|
|
|
3
157
|
### Feat
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: delta-engine
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Framework for defining, validating, and evolving tables in a consistent, safe, and controlled way.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Tomoscorbin/delta-engine
|
|
6
|
+
Project-URL: Repository, https://github.com/Tomoscorbin/delta-engine
|
|
7
|
+
Project-URL: Documentation, https://tomoscorbin.github.io/delta-engine/
|
|
8
|
+
Project-URL: Issues, https://github.com/Tomoscorbin/delta-engine/issues
|
|
9
|
+
Author-email: Tomos Corbin <tomoscorbin@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: data-engineering,databricks,ddl,delta-lake,migrations,schema
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Provides-Extra: cli
|
|
23
|
+
Requires-Dist: databricks-sdk>=0.70.0; extra == 'cli'
|
|
24
|
+
Requires-Dist: databricks-sql-connector>=4.0.0; extra == 'cli'
|
|
25
|
+
Requires-Dist: typer>=0.12; extra == 'cli'
|
|
26
|
+
Provides-Extra: spark
|
|
27
|
+
Requires-Dist: delta-spark>=4.0.0; extra == 'spark'
|
|
28
|
+
Requires-Dist: pyspark>=4.0.0; extra == 'spark'
|
|
29
|
+
Provides-Extra: sql
|
|
30
|
+
Requires-Dist: databricks-sql-connector>=4.0.0; extra == 'sql'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# delta-engine
|
|
34
|
+
|
|
35
|
+
[](https://github.com/Tomoscorbin/delta-engine/actions/workflows/live.yaml)
|
|
36
|
+
|
|
37
|
+
Declarative, safety-first schema and metadata management for Delta Lake tables on Databricks.
|
|
38
|
+
|
|
39
|
+
Define the state your tables should have in Python. Delta Engine reads their current Unity Catalog state, calculates the difference, validates whether each change is safe, and executes only the DDL needed to reconcile them.
|
|
40
|
+
|
|
41
|
+
There are no migration scripts to maintain and no DDL statements to hand-order. Your declarations remain the source of truth across the lifetime of the table.
|
|
42
|
+
|
|
43
|
+
## What Delta Engine gives you
|
|
44
|
+
|
|
45
|
+
- **Desired-state reconciliation:** declare the complete table state rather than a sequence of migrations.
|
|
46
|
+
- **Safe in-place evolution:** unsafe changes, such as type narrowing, repartitioning or destructive schema changes, are rejected before any SQL runs.
|
|
47
|
+
- **Reviewable plans:** preview semantic changes and compiled DDL with a dry run before applying them.
|
|
48
|
+
- **Drift detection:** compare version-controlled declarations against the live Unity Catalog state in local workflows or CI.
|
|
49
|
+
- **Scoped ownership:** manage governance metadata without taking ownership of a table’s schema or data lifecycle.
|
|
50
|
+
- **Dependency-aware execution:** synchronise groups of tables in the correct order when primary-key and foreign-key relationships exist.
|
|
51
|
+
|
|
52
|
+
Delta Engine does not need to own how a table’s data is produced. It can manage the catalog state around tables populated by PySpark jobs, declarative pipelines, dbt models or other systems.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install delta-engine
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The base package is pure Python with no runtime dependencies: declaring and
|
|
61
|
+
planning schemas needs no PySpark. Running a sync needs either a Spark
|
|
62
|
+
session or a Databricks SQL warehouse connection. Inside a Databricks
|
|
63
|
+
notebook, Spark is already provided; elsewhere, install the `[spark]` extra
|
|
64
|
+
for local development against a Spark session, or the `[sql]` extra to sync
|
|
65
|
+
through a SQL warehouse instead — schema sync from CI without a Spark session
|
|
66
|
+
or cluster. See the
|
|
67
|
+
[installation guide](https://tomoscorbin.github.io/delta-engine/installation.html)
|
|
68
|
+
for both extras and their requirements.
|
|
69
|
+
|
|
70
|
+
Install `delta-engine[cli]` to run
|
|
71
|
+
`delta-engine plan MODULE:ATTRIBUTE` through any standard Databricks
|
|
72
|
+
unified-auth configuration. The CLI always shows the semantic diff, report,
|
|
73
|
+
and planned SQL and never applies the generated plan. See the
|
|
74
|
+
[CLI reference](https://tomoscorbin.github.io/delta-engine/reference-cli.html).
|
|
75
|
+
|
|
76
|
+
## Quickstart
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from delta_engine.databricks import build_spark_engine
|
|
80
|
+
from delta_engine.schema import Column, DeltaTable, Integer, String
|
|
81
|
+
|
|
82
|
+
customers = DeltaTable(
|
|
83
|
+
catalog="dev",
|
|
84
|
+
schema="silver",
|
|
85
|
+
name="customers",
|
|
86
|
+
columns=[
|
|
87
|
+
Column("id", Integer(), nullable=False),
|
|
88
|
+
Column("name", String()),
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
engine = build_spark_engine(spark) # `spark` is provided by your Databricks notebook
|
|
93
|
+
engine.sync(customers) # creates the table, or no-ops if it already matches
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Every sync follows the same process:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
read live state
|
|
100
|
+
→ compare with the declaration
|
|
101
|
+
→ validate the differences
|
|
102
|
+
→ build a deterministic plan
|
|
103
|
+
→ resolve table dependencies
|
|
104
|
+
→ execute
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Validation happens before execution. When a table contains an unsafe change, Delta Engine does not execute a partially valid plan for that table.
|
|
108
|
+
|
|
109
|
+
## Documentation
|
|
110
|
+
|
|
111
|
+
Start with [how a sync works](https://tomoscorbin.github.io/delta-engine/explanation-sync-lifecycle.html)
|
|
112
|
+
for the model, or jump to what you need:
|
|
113
|
+
|
|
114
|
+
**Getting started**
|
|
115
|
+
|
|
116
|
+
- [Installation](https://tomoscorbin.github.io/delta-engine/installation.html)
|
|
117
|
+
- [Getting started tutorial](https://tomoscorbin.github.io/delta-engine/tutorial-getting-started.html) — define a table and run your first sync
|
|
118
|
+
|
|
119
|
+
**Concepts**
|
|
120
|
+
|
|
121
|
+
- [How a sync works](https://tomoscorbin.github.io/delta-engine/explanation-sync-lifecycle.html) — the phases between calling `sync` and getting a report
|
|
122
|
+
- [The safety model](https://tomoscorbin.github.io/delta-engine/explanation-safety-model.html) — what the engine blocks, and why
|
|
123
|
+
|
|
124
|
+
**How-to guides**
|
|
125
|
+
|
|
126
|
+
- [Configure a table](https://tomoscorbin.github.io/delta-engine/how-to-configure-table.html) — properties, tags, comments, keys, and partitioning
|
|
127
|
+
- [Deploy metadata only](https://tomoscorbin.github.io/delta-engine/how-to-deploy-metadata-only.html) — roll out governance metadata with no schema change
|
|
128
|
+
- [Preview changes with a dry run](https://tomoscorbin.github.io/delta-engine/how-to-preview-changes.html)
|
|
129
|
+
- [Gate schema changes in CI](https://tomoscorbin.github.io/delta-engine/how-to-gate-changes-in-ci.html) — report planned changes and fail unreadable or unsafe plans
|
|
130
|
+
- [Handle sync failures](https://tomoscorbin.github.io/delta-engine/how-to-handle-sync-failures.html) — inspect `SyncReport` and act on each status
|
|
131
|
+
|
|
132
|
+
**Reference**
|
|
133
|
+
|
|
134
|
+
- [CLI](https://tomoscorbin.github.io/delta-engine/reference-cli.html) — the read-only plan command, connection contract, output, and exit codes
|
|
135
|
+
- [Capabilities and limitations](https://tomoscorbin.github.io/delta-engine/reference-limitations.html) — what the engine can and cannot manage
|
|
136
|
+
- [Data types](https://tomoscorbin.github.io/delta-engine/reference-data-types.html) — supported types and Spark SQL equivalents
|
|
137
|
+
- [Safe-change rules](https://tomoscorbin.github.io/delta-engine/reference-safe-change-rules.html) — changes the engine blocks at validation
|
|
138
|
+
- [Run report schema](https://tomoscorbin.github.io/delta-engine/reference-run-report.html) — the `to_dict()` payload, field by field
|
|
139
|
+
- [API reference](https://tomoscorbin.github.io/delta-engine/reference-api.html)
|
|
140
|
+
|
|
141
|
+
**Architecture**
|
|
142
|
+
|
|
143
|
+
- [Architecture](https://tomoscorbin.github.io/delta-engine/explanation-architecture.html) — layers, ports and adapters, design decisions
|
|
144
|
+
- [Implement a custom adapter](https://tomoscorbin.github.io/delta-engine/how-to-implement-adapter.html) — the `CatalogStateReader` and `PlanExecutor` ports
|
|
145
|
+
- [Add a new action type](https://tomoscorbin.github.io/delta-engine/how-to-add-action-type.html) — extend `Action`, `ActionPhase`, and the compiler
|