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.
Files changed (212) hide show
  1. {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/CLAUDE.md +38 -33
  2. {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/rules/documentation.md +1 -1
  3. {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/rules/testing.md +6 -7
  4. {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/skills/add-tests/SKILL.md +4 -3
  5. {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/workflows/ci.yaml +2 -1
  6. delta_engine-0.4.0/.github/workflows/docs.yaml +52 -0
  7. delta_engine-0.4.0/.github/workflows/live.yaml +92 -0
  8. {delta_engine-0.2.0 → delta_engine-0.4.0}/.gitignore +1 -0
  9. {delta_engine-0.2.0 → delta_engine-0.4.0}/CHANGELOG.md +154 -0
  10. delta_engine-0.4.0/PKG-INFO +145 -0
  11. delta_engine-0.4.0/README.md +113 -0
  12. delta_engine-0.4.0/body.md +116 -0
  13. delta_engine-0.4.0/docs/conf.py +81 -0
  14. delta_engine-0.4.0/docs/explanation-architecture.md +758 -0
  15. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/explanation-safety-model.md +19 -12
  16. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/explanation-sync-lifecycle.md +28 -25
  17. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-add-action-type.md +59 -18
  18. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-configure-table.md +288 -67
  19. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-deploy-metadata-only.md +19 -17
  20. delta_engine-0.4.0/docs/how-to-gate-changes-in-ci.md +145 -0
  21. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-handle-sync-failures.md +7 -5
  22. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/how-to-implement-adapter.md +40 -12
  23. delta_engine-0.4.0/docs/how-to-preview-changes.md +66 -0
  24. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/index.md +11 -6
  25. delta_engine-0.4.0/docs/installation.md +129 -0
  26. delta_engine-0.4.0/docs/reference-api.md +40 -0
  27. delta_engine-0.4.0/docs/reference-cli.md +153 -0
  28. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/reference-data-types.md +23 -5
  29. delta_engine-0.4.0/docs/reference-limitations.md +113 -0
  30. delta_engine-0.4.0/docs/reference-run-report.md +106 -0
  31. delta_engine-0.4.0/docs/reference-safe-change-rules.md +98 -0
  32. delta_engine-0.4.0/docs/todo/business-logic-delta-databricks-correctness-review.md +334 -0
  33. delta_engine-0.4.0/docs/todo/roadmap.md +319 -0
  34. delta_engine-0.4.0/docs/todo/todo.md +28 -0
  35. {delta_engine-0.2.0 → delta_engine-0.4.0}/docs/tutorial-getting-started.md +8 -9
  36. {delta_engine-0.2.0 → delta_engine-0.4.0}/notebooks/delta_engine_walkthrough.py +80 -58
  37. {delta_engine-0.2.0 → delta_engine-0.4.0}/pyproject.toml +92 -8
  38. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/__init__.py +15 -1
  39. delta_engine-0.4.0/src/delta_engine/adapters/databricks/__init__.py +15 -0
  40. delta_engine-0.4.0/src/delta_engine/adapters/databricks/errors.py +55 -0
  41. delta_engine-0.4.0/src/delta_engine/adapters/databricks/execution.py +87 -0
  42. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/log_config.py +6 -6
  43. delta_engine-0.4.0/src/delta_engine/adapters/databricks/spark/__init__.py +9 -0
  44. delta_engine-0.4.0/src/delta_engine/adapters/databricks/spark/executor.py +29 -0
  45. {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
  46. delta_engine-0.4.0/src/delta_engine/adapters/databricks/spark/reader.py +244 -0
  47. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/sql/__init__.py +27 -8
  48. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/sql/compile.py +66 -43
  49. delta_engine-0.4.0/src/delta_engine/adapters/databricks/sql/parse.py +261 -0
  50. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/sql/queries.py +39 -0
  51. delta_engine-0.4.0/src/delta_engine/adapters/databricks/sql/rows.py +229 -0
  52. delta_engine-0.4.0/src/delta_engine/adapters/databricks/sql/types.py +85 -0
  53. delta_engine-0.4.0/src/delta_engine/adapters/databricks/warehouse/executor.py +65 -0
  54. delta_engine-0.4.0/src/delta_engine/adapters/databricks/warehouse/factory.py +26 -0
  55. delta_engine-0.4.0/src/delta_engine/adapters/databricks/warehouse/reader.py +178 -0
  56. delta_engine-0.4.0/src/delta_engine/api/delta_table.py +594 -0
  57. delta_engine-0.4.0/src/delta_engine/application/__init__.py +38 -0
  58. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/dependency_resolution.py +17 -7
  59. delta_engine-0.4.0/src/delta_engine/application/diff_entries.py +261 -0
  60. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/engine.py +71 -45
  61. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/errors.py +10 -0
  62. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/failures.py +19 -9
  63. delta_engine-0.4.0/src/delta_engine/application/planning.py +55 -0
  64. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/ports.py +62 -24
  65. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/properties.py +15 -4
  66. delta_engine-0.4.0/src/delta_engine/application/rendering.py +187 -0
  67. delta_engine-0.4.0/src/delta_engine/application/report.py +202 -0
  68. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/application/validation.py +194 -70
  69. delta_engine-0.4.0/src/delta_engine/cli/__init__.py +25 -0
  70. delta_engine-0.4.0/src/delta_engine/cli/__main__.py +6 -0
  71. delta_engine-0.4.0/src/delta_engine/cli/app.py +113 -0
  72. delta_engine-0.4.0/src/delta_engine/cli/connection.py +197 -0
  73. delta_engine-0.4.0/src/delta_engine/cli/declarations.py +134 -0
  74. delta_engine-0.4.0/src/delta_engine/cli/errors.py +10 -0
  75. delta_engine-0.4.0/src/delta_engine/cli/rendering.py +48 -0
  76. delta_engine-0.4.0/src/delta_engine/databricks.py +50 -0
  77. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/model/__init__.py +3 -4
  78. delta_engine-0.4.0/src/delta_engine/domain/model/column.py +83 -0
  79. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/model/constraints.py +20 -9
  80. delta_engine-0.4.0/src/delta_engine/domain/model/table.py +314 -0
  81. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/plan/__init__.py +11 -39
  82. delta_engine-0.4.0/src/delta_engine/domain/plan/actions.py +420 -0
  83. delta_engine-0.4.0/src/delta_engine/domain/plan/diff.py +471 -0
  84. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/schema.py +1 -1
  85. {delta_engine-0.2.0/tests/adapters/databricks → delta_engine-0.4.0/tests/adapters/databricks/spark}/test_build_engine.py +25 -2
  86. {delta_engine-0.2.0/tests/adapters/databricks → delta_engine-0.4.0/tests/adapters/databricks/spark}/test_executor.py +55 -139
  87. {delta_engine-0.2.0/tests/adapters/databricks → delta_engine-0.4.0/tests/adapters/databricks/spark}/test_reader.py +70 -43
  88. delta_engine-0.4.0/tests/adapters/databricks/spark/test_reader_mappers.py +93 -0
  89. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/adapters/databricks/sql/test_compile.py +127 -76
  90. delta_engine-0.4.0/tests/adapters/databricks/sql/test_parse.py +184 -0
  91. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/adapters/databricks/sql/test_queries.py +28 -0
  92. 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
  93. delta_engine-0.4.0/tests/adapters/databricks/sql/test_types.py +51 -0
  94. delta_engine-0.4.0/tests/adapters/databricks/test_errors.py +56 -0
  95. delta_engine-0.4.0/tests/adapters/databricks/test_execution.py +115 -0
  96. delta_engine-0.4.0/tests/adapters/databricks/warehouse/test_build_engine.py +17 -0
  97. delta_engine-0.4.0/tests/adapters/databricks/warehouse/test_executor.py +125 -0
  98. delta_engine-0.4.0/tests/adapters/databricks/warehouse/test_reader.py +264 -0
  99. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/api/test_delta_table.py +338 -49
  100. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/api/test_foreign_key.py +229 -97
  101. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_dependency_resolution.py +94 -9
  102. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_engine.py +218 -60
  103. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_errors.py +18 -11
  104. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_failures.py +30 -11
  105. delta_engine-0.4.0/tests/application/test_planning.py +346 -0
  106. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_ports.py +24 -27
  107. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_properties.py +7 -2
  108. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_public_api.py +37 -3
  109. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_rendering.py +171 -65
  110. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_report.py +183 -22
  111. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/application/test_validation.py +332 -85
  112. delta_engine-0.4.0/tests/builders.py +26 -0
  113. delta_engine-0.4.0/tests/cli/__init__.py +0 -0
  114. delta_engine-0.4.0/tests/cli/conftest.py +133 -0
  115. delta_engine-0.4.0/tests/cli/test_app_plan.py +297 -0
  116. delta_engine-0.4.0/tests/cli/test_connection.py +290 -0
  117. delta_engine-0.4.0/tests/cli/test_declarations.py +226 -0
  118. delta_engine-0.4.0/tests/cli/test_entry.py +31 -0
  119. delta_engine-0.4.0/tests/domain/__init__.py +0 -0
  120. delta_engine-0.4.0/tests/domain/model/__init__.py +0 -0
  121. delta_engine-0.4.0/tests/domain/model/test_column.py +90 -0
  122. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_foreign_key.py +53 -0
  123. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_table.py +253 -54
  124. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_table_aspect.py +9 -0
  125. delta_engine-0.4.0/tests/domain/plan/__init__.py +0 -0
  126. delta_engine-0.4.0/tests/domain/plan/test_actions.py +309 -0
  127. delta_engine-0.4.0/tests/domain/plan/test_diff.py +984 -0
  128. delta_engine-0.4.0/tests/e2e/__init__.py +0 -0
  129. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/e2e/conftest.py +2 -2
  130. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/e2e/test_engine_e2e.py +261 -27
  131. delta_engine-0.4.0/tests/live/__init__.py +0 -0
  132. delta_engine-0.4.0/tests/live/conftest.py +167 -0
  133. delta_engine-0.4.0/tests/live/databricks_connection.py +73 -0
  134. delta_engine-0.4.0/tests/live/sql_warehouse_live_helpers.py +148 -0
  135. delta_engine-0.4.0/tests/live/test_sql_warehouse_live.py +303 -0
  136. delta_engine-0.4.0/tests/live/test_sql_warehouse_live_constraints.py +237 -0
  137. delta_engine-0.4.0/tests/live/test_sql_warehouse_live_platform_assumptions.py +223 -0
  138. delta_engine-0.4.0/tests/live/test_sql_warehouse_live_renames.py +327 -0
  139. delta_engine-0.4.0/tests/live/test_sql_warehouse_live_safety.py +495 -0
  140. delta_engine-0.4.0/tests/live/test_sql_warehouse_live_scopes.py +111 -0
  141. delta_engine-0.4.0/tests/live/test_sql_warehouse_live_types_and_layout.py +357 -0
  142. delta_engine-0.4.0/tests/test_packaging.py +62 -0
  143. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/test_public_api.py +32 -0
  144. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/test_public_imports.py +31 -6
  145. {delta_engine-0.2.0 → delta_engine-0.4.0}/uv.lock +575 -6
  146. delta_engine-0.2.0/PKG-INFO +0 -116
  147. delta_engine-0.2.0/README.md +0 -90
  148. delta_engine-0.2.0/body.md +0 -28
  149. delta_engine-0.2.0/docs/conf.py +0 -31
  150. delta_engine-0.2.0/docs/explanation-architecture.md +0 -630
  151. delta_engine-0.2.0/docs/how-to-preview-changes.md +0 -57
  152. delta_engine-0.2.0/docs/installation.md +0 -59
  153. delta_engine-0.2.0/docs/reference-api.md +0 -120
  154. delta_engine-0.2.0/docs/reference-limitations.md +0 -72
  155. delta_engine-0.2.0/docs/reference-safe-change-rules.md +0 -48
  156. delta_engine-0.2.0/docs/todo/todo.md +0 -25
  157. delta_engine-0.2.0/src/delta_engine/adapters/databricks/__init__.py +0 -8
  158. delta_engine-0.2.0/src/delta_engine/adapters/databricks/errors.py +0 -53
  159. delta_engine-0.2.0/src/delta_engine/adapters/databricks/executor.py +0 -120
  160. delta_engine-0.2.0/src/delta_engine/adapters/databricks/reader.py +0 -386
  161. delta_engine-0.2.0/src/delta_engine/adapters/databricks/sql/types.py +0 -185
  162. delta_engine-0.2.0/src/delta_engine/api/delta_table.py +0 -436
  163. delta_engine-0.2.0/src/delta_engine/application/__init__.py +0 -23
  164. delta_engine-0.2.0/src/delta_engine/application/desired_tables.py +0 -50
  165. delta_engine-0.2.0/src/delta_engine/application/rendering.py +0 -355
  166. delta_engine-0.2.0/src/delta_engine/application/report.py +0 -97
  167. delta_engine-0.2.0/src/delta_engine/databricks.py +0 -35
  168. delta_engine-0.2.0/src/delta_engine/domain/model/column.py +0 -46
  169. delta_engine-0.2.0/src/delta_engine/domain/model/table.py +0 -236
  170. delta_engine-0.2.0/src/delta_engine/domain/plan/actions.py +0 -337
  171. delta_engine-0.2.0/src/delta_engine/domain/plan/changes.py +0 -431
  172. delta_engine-0.2.0/src/delta_engine/domain/plan/diff.py +0 -370
  173. delta_engine-0.2.0/tests/adapters/databricks/sql/test_types.py +0 -163
  174. delta_engine-0.2.0/tests/adapters/databricks/test_errors.py +0 -38
  175. delta_engine-0.2.0/tests/application/test_desired_tables.py +0 -108
  176. delta_engine-0.2.0/tests/domain/model/test_column.py +0 -59
  177. delta_engine-0.2.0/tests/domain/plan/test_actions.py +0 -279
  178. delta_engine-0.2.0/tests/domain/plan/test_diff.py +0 -891
  179. {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/agents/ousterhout-design-reviewer.md +0 -0
  180. {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/agents/test-reviewer.md +0 -0
  181. {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/rules/coding-style.md +0 -0
  182. {delta_engine-0.2.0 → delta_engine-0.4.0}/.claude/rules/domain-architecture.md +0 -0
  183. {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/pull_request_template.md +0 -0
  184. {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/workflows/pr-title.yaml +0 -0
  185. {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/workflows/publish-testpypi.yaml +0 -0
  186. {delta_engine-0.2.0 → delta_engine-0.4.0}/.github/workflows/release.yaml +0 -0
  187. {delta_engine-0.2.0 → delta_engine-0.4.0}/.pre-commit-config.yaml +0 -0
  188. {delta_engine-0.2.0 → delta_engine-0.4.0}/LICENSE +0 -0
  189. {delta_engine-0.2.0 → delta_engine-0.4.0}/notebooks/README.md +0 -0
  190. {delta_engine-0.2.0 → delta_engine-0.4.0}/notebooks/catalog_inspector.py +0 -0
  191. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/__init__.py +0 -0
  192. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/adapters/databricks/sql/dialect.py +0 -0
  193. {delta_engine-0.2.0/src/delta_engine/domain → delta_engine-0.4.0/src/delta_engine/adapters/databricks/warehouse}/__init__.py +0 -0
  194. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/api/__init__.py +0 -0
  195. {delta_engine-0.2.0/tests → delta_engine-0.4.0/src/delta_engine/domain}/__init__.py +0 -0
  196. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/model/data_type.py +0 -0
  197. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/domain/model/qualified_name.py +0 -0
  198. {delta_engine-0.2.0 → delta_engine-0.4.0}/src/delta_engine/py.typed +0 -0
  199. {delta_engine-0.2.0/tests/adapters/databricks → delta_engine-0.4.0/tests}/__init__.py +0 -0
  200. {delta_engine-0.2.0/tests/adapters/databricks/sql → delta_engine-0.4.0/tests/adapters}/__init__.py +0 -0
  201. {delta_engine-0.2.0/tests/api → delta_engine-0.4.0/tests/adapters/databricks}/__init__.py +0 -0
  202. {delta_engine-0.2.0/tests/application → delta_engine-0.4.0/tests/adapters/databricks/spark}/__init__.py +0 -0
  203. {delta_engine-0.2.0/tests/domain → delta_engine-0.4.0/tests/adapters/databricks/sql}/__init__.py +0 -0
  204. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/adapters/databricks/sql/test_dialect.py +0 -0
  205. {delta_engine-0.2.0/tests/domain/model → delta_engine-0.4.0/tests/adapters/databricks/warehouse}/__init__.py +0 -0
  206. {delta_engine-0.2.0/tests/domain/plan → delta_engine-0.4.0/tests/api}/__init__.py +0 -0
  207. {delta_engine-0.2.0/tests/e2e → delta_engine-0.4.0/tests/application}/__init__.py +0 -0
  208. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/config.py +0 -0
  209. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/conftest.py +0 -0
  210. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_data_type.py +0 -0
  211. {delta_engine-0.2.0 → delta_engine-0.4.0}/tests/domain/model/test_primary_key.py +0 -0
  212. {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
- * `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 current adapter is Databricks/Spark.
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
- * `tests`: unit, integration, adapter, and end-to-end tests.
20
- * `docs`: Sphinx/MyST documentation.
21
-
22
- The library should remain importable for schema declaration and planning without requiring PySpark unless the Databricks adapter is actually used.
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
- Do not run credentialed Databricks tests unless explicitly asked.
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
- * `domain` must stay backend-free, immutable, and deterministic.
104
- * `application` owns orchestration, ports, safety policy, dependency resolution, reports, and failure propagation.
105
- * `adapters` own backend integration, SQL compilation, Spark/Databricks parsing, identifier quoting, and backend exception translation.
106
- * `api` owns public declaration implementation and lowers declarations into domain snapshots.
107
- * `schema.py` and `databricks.py` are user-facing facades; keep them thin.
108
- * PySpark, Delta, Py4J, Spark SQL details, and Databricks-specific assumptions must not leak into `domain` or `application`.
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
- A separate import-linter contract forbids `delta` and `pyspark` imports from `schema`, `api`, `application`, and `domain`.
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
- * `CatalogStateReader.fetch_state(...)` returns `TablePresent`, `TableAbsent`, or `ReadFailed`.
138
- * `PlanExecutor.execute(...)` returns `ExecutionSummary`.
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
- * Properties use exact-declaration semantics. A declared `None` asserts absence and plans an unset when present.
157
- * Tags use full-state semantics. An observed-only tag is drift and should be unset when tags are managed.
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
- * `docs/explanation-architecture.md`
186
- * `docs/how-to-implement-adapter.md`
187
- * `docs/how-to-add-action-type.md`
188
- * `docs/reference-safe-change-rules.md`
189
- * `docs/how-to-handle-sync-failures.md`
190
- * `docs/how-to-deploy-metadata-only.md`
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
- * what changed
213
- * what checks were run
214
- * what risks or follow-ups remain
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 build_engine
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
- Credentialed Databricks tests must remain opt-in through the `databricks_e2e` marker.
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
- Do not run credentialed Databricks tests unless explicitly asked.
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 = DatabricksExecutor(spark)
321
+ executor = SparkExecutor(spark)
322
322
  plan = ActionPlan((SetTableComment("new comment"),))
323
323
 
324
324
  # when
325
- executor.execute(QualifiedName("catalog", "schema", "table"), plan)
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, primary_key=True),
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
@@ -46,3 +46,4 @@ htmlcov/
46
46
  .superpowers/
47
47
  docs/superpowers/
48
48
  docs/_build/
49
+ docs/generated/
@@ -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
+ [![Live Databricks Tests](https://github.com/Tomoscorbin/delta-engine/actions/workflows/live.yaml/badge.svg?branch=main)](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