contractforge-databricks 0.1.0__py3-none-any.whl

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 (220) hide show
  1. contractforge_databricks/__init__.py +172 -0
  2. contractforge_databricks/adapter.py +69 -0
  3. contractforge_databricks/annotations/__init__.py +10 -0
  4. contractforge_databricks/annotations/application.py +52 -0
  5. contractforge_databricks/annotations/audit.py +49 -0
  6. contractforge_databricks/annotations/sql.py +142 -0
  7. contractforge_databricks/api.py +65 -0
  8. contractforge_databricks/bundles/__init__.py +9 -0
  9. contractforge_databricks/bundles/assets.py +47 -0
  10. contractforge_databricks/bundles/project.py +213 -0
  11. contractforge_databricks/bundles/project_config.py +133 -0
  12. contractforge_databricks/capabilities/__init__.py +17 -0
  13. contractforge_databricks/capabilities/builders.py +43 -0
  14. contractforge_databricks/capabilities/evaluate.py +162 -0
  15. contractforge_databricks/capabilities/mapping.py +36 -0
  16. contractforge_databricks/capabilities/models.py +44 -0
  17. contractforge_databricks/capabilities/runtime.py +111 -0
  18. contractforge_databricks/capabilities/uc.py +47 -0
  19. contractforge_databricks/cli.py +196 -0
  20. contractforge_databricks/cli_deploy.py +98 -0
  21. contractforge_databricks/cli_governance.py +142 -0
  22. contractforge_databricks/cli_io.py +91 -0
  23. contractforge_databricks/cli_maintenance.py +69 -0
  24. contractforge_databricks/coercion.py +31 -0
  25. contractforge_databricks/contract_extensions.py +70 -0
  26. contractforge_databricks/cost/__init__.py +11 -0
  27. contractforge_databricks/cost/model.py +22 -0
  28. contractforge_databricks/cost/report.py +65 -0
  29. contractforge_databricks/cost/sql.py +136 -0
  30. contractforge_databricks/dashboards/__init__.py +15 -0
  31. contractforge_databricks/dashboards/control_tables.py +150 -0
  32. contractforge_databricks/diagnostics/__init__.py +7 -0
  33. contractforge_databricks/diagnostics/explain.py +40 -0
  34. contractforge_databricks/environment.py +53 -0
  35. contractforge_databricks/evidence/__init__.py +98 -0
  36. contractforge_databricks/evidence/ddl.py +35 -0
  37. contractforge_databricks/evidence/governance_log.py +175 -0
  38. contractforge_databricks/evidence/helpers.py +29 -0
  39. contractforge_databricks/evidence/ops_log.py +210 -0
  40. contractforge_databricks/evidence/records.py +27 -0
  41. contractforge_databricks/evidence/run_log.py +74 -0
  42. contractforge_databricks/evidence/schemas.py +7 -0
  43. contractforge_databricks/evidence/sql.py +144 -0
  44. contractforge_databricks/evidence/tables.py +20 -0
  45. contractforge_databricks/evidence/writer.py +118 -0
  46. contractforge_databricks/execution/__init__.py +70 -0
  47. contractforge_databricks/execution/delta_basic.py +57 -0
  48. contractforge_databricks/execution/hash_diff.py +126 -0
  49. contractforge_databricks/execution/hash_diff_latest.py +142 -0
  50. contractforge_databricks/execution/replace_partitions.py +40 -0
  51. contractforge_databricks/execution/results.py +5 -0
  52. contractforge_databricks/execution/retry.py +36 -0
  53. contractforge_databricks/execution/scd2.py +213 -0
  54. contractforge_databricks/execution/scd2_deletes.py +65 -0
  55. contractforge_databricks/execution/scd2_late.py +30 -0
  56. contractforge_databricks/execution/snapshot.py +77 -0
  57. contractforge_databricks/execution/sql_merge.py +85 -0
  58. contractforge_databricks/execution/tables.py +98 -0
  59. contractforge_databricks/execution/windows.py +58 -0
  60. contractforge_databricks/governance/__init__.py +30 -0
  61. contractforge_databricks/governance/access.py +185 -0
  62. contractforge_databricks/governance/application.py +93 -0
  63. contractforge_databricks/governance/drift.py +49 -0
  64. contractforge_databricks/governance/runtime.py +60 -0
  65. contractforge_databricks/governance/sql.py +31 -0
  66. contractforge_databricks/governance/validation.py +135 -0
  67. contractforge_databricks/lakeflow/__init__.py +21 -0
  68. contractforge_databricks/lakeflow/compatibility.py +194 -0
  69. contractforge_databricks/lakeflow/rendering.py +175 -0
  70. contractforge_databricks/lineage/__init__.py +7 -0
  71. contractforge_databricks/lineage/openlineage.py +182 -0
  72. contractforge_databricks/maintenance/__init__.py +27 -0
  73. contractforge_databricks/maintenance/retention.py +90 -0
  74. contractforge_databricks/maintenance/sql.py +68 -0
  75. contractforge_databricks/metrics/__init__.py +19 -0
  76. contractforge_databricks/metrics/history.py +21 -0
  77. contractforge_databricks/metrics/write.py +63 -0
  78. contractforge_databricks/operations/__init__.py +4 -0
  79. contractforge_databricks/operations/application.py +38 -0
  80. contractforge_databricks/operations/sql.py +95 -0
  81. contractforge_databricks/parity/__init__.py +18 -0
  82. contractforge_databricks/parity/catalog.py +59 -0
  83. contractforge_databricks/parity/models.py +7 -0
  84. contractforge_databricks/parity/scenarios.py +111 -0
  85. contractforge_databricks/partitioning/__init__.py +3 -0
  86. contractforge_databricks/partitioning/predicates.py +28 -0
  87. contractforge_databricks/preparation/__init__.py +47 -0
  88. contractforge_databricks/preparation/deduplicate.py +87 -0
  89. contractforge_databricks/preparation/encoding.py +37 -0
  90. contractforge_databricks/preparation/hashing.py +18 -0
  91. contractforge_databricks/preparation/pyspark.py +178 -0
  92. contractforge_databricks/preparation/pyspark_staging.py +70 -0
  93. contractforge_databricks/preparation/shape.py +209 -0
  94. contractforge_databricks/preparation/shape_validation.py +94 -0
  95. contractforge_databricks/preparation/staging.py +17 -0
  96. contractforge_databricks/preparation/zip_arrays.py +51 -0
  97. contractforge_databricks/presets/__init__.py +3 -0
  98. contractforge_databricks/presets/base.py +24 -0
  99. contractforge_databricks/presets/bronze.py +57 -0
  100. contractforge_databricks/presets/catalog.py +22 -0
  101. contractforge_databricks/presets/core.py +134 -0
  102. contractforge_databricks/presets/gold.py +62 -0
  103. contractforge_databricks/presets/modifiers.py +51 -0
  104. contractforge_databricks/presets/runtime.py +22 -0
  105. contractforge_databricks/presets/silver.py +101 -0
  106. contractforge_databricks/presets/write_engine.py +57 -0
  107. contractforge_databricks/quality/__init__.py +41 -0
  108. contractforge_databricks/quality/evaluation.py +178 -0
  109. contractforge_databricks/quality/persistence.py +81 -0
  110. contractforge_databricks/quality/registry.py +134 -0
  111. contractforge_databricks/quality/results.py +17 -0
  112. contractforge_databricks/quality/sql.py +113 -0
  113. contractforge_databricks/rendering/__init__.py +11 -0
  114. contractforge_databricks/rendering/bundle.py +93 -0
  115. contractforge_databricks/rendering/markdown.py +50 -0
  116. contractforge_databricks/rendering/names.py +56 -0
  117. contractforge_databricks/results.py +15 -0
  118. contractforge_databricks/runtime/__init__.py +101 -0
  119. contractforge_databricks/runtime/available_now.py +147 -0
  120. contractforge_databricks/runtime/bundles.py +211 -0
  121. contractforge_databricks/runtime/cache.py +20 -0
  122. contractforge_databricks/runtime/control_tables.py +19 -0
  123. contractforge_databricks/runtime/deploy.py +197 -0
  124. contractforge_databricks/runtime/detection.py +114 -0
  125. contractforge_databricks/runtime/dry_run.py +46 -0
  126. contractforge_databricks/runtime/errors.py +54 -0
  127. contractforge_databricks/runtime/file_selection.py +109 -0
  128. contractforge_databricks/runtime/finalization.py +168 -0
  129. contractforge_databricks/runtime/governance.py +37 -0
  130. contractforge_databricks/runtime/hooks.py +45 -0
  131. contractforge_databricks/runtime/http_file.py +37 -0
  132. contractforge_databricks/runtime/http_retry.py +15 -0
  133. contractforge_databricks/runtime/http_safety.py +9 -0
  134. contractforge_databricks/runtime/json_materialization.py +97 -0
  135. contractforge_databricks/runtime/lineage.py +164 -0
  136. contractforge_databricks/runtime/maintenance.py +43 -0
  137. contractforge_databricks/runtime/merge_validation.py +98 -0
  138. contractforge_databricks/runtime/metadata.py +21 -0
  139. contractforge_databricks/runtime/metrics.py +34 -0
  140. contractforge_databricks/runtime/models.py +32 -0
  141. contractforge_databricks/runtime/options.py +33 -0
  142. contractforge_databricks/runtime/orchestration_context.py +185 -0
  143. contractforge_databricks/runtime/orchestrator.py +147 -0
  144. contractforge_databricks/runtime/partitioning.py +93 -0
  145. contractforge_databricks/runtime/quality_quarantine.py +92 -0
  146. contractforge_databricks/runtime/rest_api.py +46 -0
  147. contractforge_databricks/runtime/rest_auth.py +21 -0
  148. contractforge_databricks/runtime/rest_pagination.py +21 -0
  149. contractforge_databricks/runtime/run_payload.py +177 -0
  150. contractforge_databricks/runtime/schema.py +106 -0
  151. contractforge_databricks/runtime/source_metadata.py +30 -0
  152. contractforge_databricks/runtime/source_registry.py +43 -0
  153. contractforge_databricks/runtime/source_schema.py +24 -0
  154. contractforge_databricks/runtime/sources.py +208 -0
  155. contractforge_databricks/runtime/spark.py +183 -0
  156. contractforge_databricks/runtime/spark_defaults.py +35 -0
  157. contractforge_databricks/runtime/storage_auth.py +132 -0
  158. contractforge_databricks/runtime/streaming.py +131 -0
  159. contractforge_databricks/runtime/success.py +104 -0
  160. contractforge_databricks/runtime/utils.py +52 -0
  161. contractforge_databricks/runtime/watermark.py +71 -0
  162. contractforge_databricks/runtime/windows.py +184 -0
  163. contractforge_databricks/runtime/write.py +66 -0
  164. contractforge_databricks/runtime/write_flow.py +146 -0
  165. contractforge_databricks/runtime/write_strategy.py +40 -0
  166. contractforge_databricks/schema/__init__.py +21 -0
  167. contractforge_databricks/schema/diff.py +11 -0
  168. contractforge_databricks/schema/policy.py +33 -0
  169. contractforge_databricks/schema/sync.py +23 -0
  170. contractforge_databricks/security/__init__.py +21 -0
  171. contractforge_databricks/security/errors.py +5 -0
  172. contractforge_databricks/security/redaction.py +5 -0
  173. contractforge_databricks/security/secrets.py +114 -0
  174. contractforge_databricks/security/source_policy.py +17 -0
  175. contractforge_databricks/shapes/__init__.py +3 -0
  176. contractforge_databricks/shapes/sql.py +123 -0
  177. contractforge_databricks/sources/__init__.py +67 -0
  178. contractforge_databricks/sources/artifacts.py +100 -0
  179. contractforge_databricks/sources/autoloader.py +48 -0
  180. contractforge_databricks/sources/bounded_streams.py +44 -0
  181. contractforge_databricks/sources/classification.py +115 -0
  182. contractforge_databricks/sources/delta_share.py +21 -0
  183. contractforge_databricks/sources/files.py +48 -0
  184. contractforge_databricks/sources/http_file.py +46 -0
  185. contractforge_databricks/sources/interpret.py +76 -0
  186. contractforge_databricks/sources/jdbc.py +32 -0
  187. contractforge_databricks/sources/metadata.py +18 -0
  188. contractforge_databricks/sources/native_passthrough.py +33 -0
  189. contractforge_databricks/sources/rds_iam.py +15 -0
  190. contractforge_databricks/sources/rds_iam_runtime.py +191 -0
  191. contractforge_databricks/sources/rest_api.py +33 -0
  192. contractforge_databricks/sources/support.py +50 -0
  193. contractforge_databricks/sources/table_refs.py +65 -0
  194. contractforge_databricks/sql/__init__.py +4 -0
  195. contractforge_databricks/sql/identifiers.py +17 -0
  196. contractforge_databricks/sql/literals.py +36 -0
  197. contractforge_databricks/state/__init__.py +39 -0
  198. contractforge_databricks/state/ddl.py +24 -0
  199. contractforge_databricks/state/migrations.py +146 -0
  200. contractforge_databricks/state/queries.py +149 -0
  201. contractforge_databricks/state/sql.py +116 -0
  202. contractforge_databricks/state/tables.py +9 -0
  203. contractforge_databricks/state/writer.py +83 -0
  204. contractforge_databricks/templates/__init__.py +15 -0
  205. contractforge_databricks/templates/catalog.py +205 -0
  206. contractforge_databricks/templates/catalog_parity.py +85 -0
  207. contractforge_databricks/templates/core.py +83 -0
  208. contractforge_databricks/templates/enrichment.py +175 -0
  209. contractforge_databricks/transforms/__init__.py +3 -0
  210. contractforge_databricks/transforms/sql.py +118 -0
  211. contractforge_databricks/watermark/__init__.py +6 -0
  212. contractforge_databricks/watermark/sql.py +91 -0
  213. contractforge_databricks/write_modes/__init__.py +20 -0
  214. contractforge_databricks/write_modes/registry.py +44 -0
  215. contractforge_databricks/write_modes/sql.py +33 -0
  216. contractforge_databricks/write_modes/strategy.py +192 -0
  217. contractforge_databricks-0.1.0.dist-info/METADATA +34 -0
  218. contractforge_databricks-0.1.0.dist-info/RECORD +220 -0
  219. contractforge_databricks-0.1.0.dist-info/WHEEL +4 -0
  220. contractforge_databricks-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,172 @@
1
+ """Databricks adapter package for ContractForge Core."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version as _pkg_version
4
+
5
+ try:
6
+ __version__ = _pkg_version("contractforge-databricks")
7
+ except PackageNotFoundError: # pragma: no cover - editable installs without metadata
8
+ __version__ = "0.0.0+unknown"
9
+
10
+ from contractforge_databricks.adapter import DatabricksAdapter
11
+ from contractforge_databricks.api import plan_databricks_contract, render_databricks_contract
12
+ from contractforge_databricks.capabilities.evaluate import evaluate_databricks_capabilities
13
+ from contractforge_databricks.capabilities.mapping import to_core_capabilities
14
+ from contractforge_databricks.capabilities.models import CapabilityEvidence, DatabricksCapabilities, NativeCapability
15
+ from contractforge_databricks.capabilities.uc import uc_capability_issues
16
+ from contractforge_databricks.cost import CostModel, build_operational_cost_report, render_operational_cost_query
17
+ from contractforge_databricks.dashboards import render_control_dashboard_artifacts, render_control_dashboard_sql
18
+ from contractforge_databricks.environment import DatabricksEnvironment
19
+ from contractforge_databricks.bundles import render_databricks_project_bundle, render_databricks_project_bundle_yaml
20
+ from contractforge_databricks.governance import (
21
+ access_drift_report,
22
+ apply_access_contract,
23
+ apply_governance_contract,
24
+ check_governance_contract,
25
+ governance_referenced_columns,
26
+ render_access_sql,
27
+ render_governance_sql,
28
+ validate_governance_contract,
29
+ )
30
+ from contractforge_databricks.lakeflow import (
31
+ LakeflowAutoCdcArtifact,
32
+ LakeflowCompatibility,
33
+ evaluate_lakeflow_compatibility,
34
+ render_lakeflow_auto_cdc_artifact,
35
+ render_lakeflow_auto_cdc_python,
36
+ )
37
+ from contractforge_databricks.maintenance import build_control_retention_plan, execute_control_retention_plan
38
+ from contractforge_databricks.metrics import render_delta_history_query, resolve_write_metrics
39
+ from contractforge_databricks.parity import (
40
+ ParityMetricExpectation,
41
+ WriteEngineParityScenario,
42
+ build_write_engine_parity_plan,
43
+ get_write_engine_parity_scenario,
44
+ list_write_engine_parity_scenarios,
45
+ scenarios_for_engine,
46
+ scenarios_for_mode,
47
+ )
48
+ from contractforge_databricks.presets import apply_preset, get_preset, list_presets, preset_details, register_preset
49
+ from contractforge_databricks.preparation import apply_shape
50
+ from contractforge_databricks.quality import (
51
+ get_quality_rule,
52
+ list_quality_rules,
53
+ register_quality_rule,
54
+ unregister_quality_rule,
55
+ )
56
+ from contractforge_databricks.runtime import (
57
+ DatabricksIngestOptions,
58
+ DatabricksIngestionHooks,
59
+ PreparedViewInput,
60
+ apply_databricks_access_bundle,
61
+ apply_databricks_annotations_bundle,
62
+ apply_databricks_governance_bundle,
63
+ detect_databricks_capabilities,
64
+ deploy_databricks_bundle,
65
+ deploy_databricks_project,
66
+ get_source_resolver,
67
+ ingest_databricks_bundle,
68
+ ingest_databricks_contract,
69
+ list_source_resolvers,
70
+ register_source_resolver,
71
+ render_databricks_project_bundle_file,
72
+ resolve_source_dataframe,
73
+ run_available_now_stream,
74
+ unregister_source_resolver,
75
+ )
76
+ from contractforge_databricks.templates import (
77
+ contract_template_details,
78
+ contract_template_files,
79
+ get_contract_template,
80
+ list_contract_templates,
81
+ recommend_contract_templates,
82
+ )
83
+ from contractforge_databricks.write_modes.registry import (
84
+ get_write_mode,
85
+ list_write_modes,
86
+ register_write_mode,
87
+ unregister_write_mode,
88
+ )
89
+ from contractforge_databricks.write_modes.strategy import WriteStrategy, choose_write_strategy
90
+
91
+ __all__ = [
92
+ "CapabilityEvidence",
93
+ "CostModel",
94
+ "DatabricksAdapter",
95
+ "DatabricksCapabilities",
96
+ "DatabricksEnvironment",
97
+ "DatabricksIngestOptions",
98
+ "DatabricksIngestionHooks",
99
+ "LakeflowAutoCdcArtifact",
100
+ "LakeflowCompatibility",
101
+ "NativeCapability",
102
+ "ParityMetricExpectation",
103
+ "PreparedViewInput",
104
+ "WriteEngineParityScenario",
105
+ "WriteStrategy",
106
+ "access_drift_report",
107
+ "apply_access_contract",
108
+ "apply_databricks_access_bundle",
109
+ "apply_databricks_annotations_bundle",
110
+ "apply_databricks_governance_bundle",
111
+ "apply_governance_contract",
112
+ "apply_preset",
113
+ "apply_shape",
114
+ "build_control_retention_plan",
115
+ "build_operational_cost_report",
116
+ "build_write_engine_parity_plan",
117
+ "check_governance_contract",
118
+ "choose_write_strategy",
119
+ "contract_template_details",
120
+ "contract_template_files",
121
+ "detect_databricks_capabilities",
122
+ "deploy_databricks_bundle",
123
+ "deploy_databricks_project",
124
+ "evaluate_databricks_capabilities",
125
+ "evaluate_lakeflow_compatibility",
126
+ "execute_control_retention_plan",
127
+ "get_contract_template",
128
+ "get_preset",
129
+ "get_quality_rule",
130
+ "get_source_resolver",
131
+ "get_write_engine_parity_scenario",
132
+ "get_write_mode",
133
+ "governance_referenced_columns",
134
+ "ingest_databricks_bundle",
135
+ "ingest_databricks_contract",
136
+ "list_contract_templates",
137
+ "list_presets",
138
+ "list_quality_rules",
139
+ "list_source_resolvers",
140
+ "list_write_engine_parity_scenarios",
141
+ "list_write_modes",
142
+ "plan_databricks_contract",
143
+ "preset_details",
144
+ "recommend_contract_templates",
145
+ "register_preset",
146
+ "register_quality_rule",
147
+ "register_source_resolver",
148
+ "register_write_mode",
149
+ "render_access_sql",
150
+ "render_control_dashboard_artifacts",
151
+ "render_control_dashboard_sql",
152
+ "render_databricks_contract",
153
+ "render_databricks_project_bundle",
154
+ "render_databricks_project_bundle_file",
155
+ "render_databricks_project_bundle_yaml",
156
+ "render_delta_history_query",
157
+ "render_governance_sql",
158
+ "render_lakeflow_auto_cdc_artifact",
159
+ "render_lakeflow_auto_cdc_python",
160
+ "render_operational_cost_query",
161
+ "resolve_source_dataframe",
162
+ "resolve_write_metrics",
163
+ "run_available_now_stream",
164
+ "scenarios_for_engine",
165
+ "scenarios_for_mode",
166
+ "to_core_capabilities",
167
+ "uc_capability_issues",
168
+ "unregister_source_resolver",
169
+ "unregister_quality_rule",
170
+ "unregister_write_mode",
171
+ "validate_governance_contract",
172
+ ]
@@ -0,0 +1,69 @@
1
+ """ContractForge Core adapter implementation for Databricks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any
7
+
8
+ from contractforge_core.adapters import RenderedArtifacts
9
+ from contractforge_core.capabilities import PlatformCapabilities
10
+ from contractforge_core.planner import ExecutionPlan, PlanningResult, plan_contract
11
+ from contractforge_core.semantic import SemanticContract
12
+ from contractforge_databricks.capabilities import DatabricksCapabilities, evaluate_databricks_capabilities, to_core_capabilities
13
+ from contractforge_databricks.contract_extensions import databricks_extension_warnings
14
+ from contractforge_databricks.environment import DatabricksEnvironment
15
+ from contractforge_databricks.rendering import render_databricks_artifacts
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class DatabricksAdapter:
20
+ """Dry-planning Databricks adapter.
21
+
22
+ Execution belongs in a later runtime module. This adapter only declares
23
+ capabilities, plans via the core, and renders reviewable native artifacts.
24
+ """
25
+
26
+ native_capabilities: DatabricksCapabilities
27
+ environment: DatabricksEnvironment = DatabricksEnvironment()
28
+ name: str = "databricks"
29
+
30
+ @classmethod
31
+ def from_evidence(
32
+ cls,
33
+ *,
34
+ target_table: str | None = None,
35
+ runtime_type: str | None = None,
36
+ spark_version: str | None = None,
37
+ spark_conf: dict[str, str] | None = None,
38
+ environment: dict[str, Any] | None = None,
39
+ ) -> "DatabricksAdapter":
40
+ env = DatabricksEnvironment.from_contract(environment)
41
+ return cls(
42
+ evaluate_databricks_capabilities(
43
+ target_table=target_table,
44
+ runtime_type=runtime_type or env.runtime_kind,
45
+ spark_version=spark_version,
46
+ spark_conf=spark_conf,
47
+ ),
48
+ environment=env,
49
+ )
50
+
51
+ def capabilities(self) -> PlatformCapabilities:
52
+ return to_core_capabilities(self.native_capabilities)
53
+
54
+ def plan(self, contract: SemanticContract) -> PlanningResult:
55
+ result = plan_contract(contract, self.capabilities())
56
+ extension_warnings = databricks_extension_warnings(contract)
57
+ if not extension_warnings:
58
+ return result
59
+ warnings = result.warnings + extension_warnings
60
+ if result.status == "SUPPORTED":
61
+ return PlanningResult(status="SUPPORTED_WITH_WARNINGS", plan=result.plan, blockers=result.blockers, warnings=warnings)
62
+ return PlanningResult(status=result.status, plan=result.plan, blockers=result.blockers, warnings=warnings)
63
+
64
+ def render(self, plan: ExecutionPlan) -> RenderedArtifacts:
65
+ raise NotImplementedError("Use render_contract(contract) so Databricks artifacts include contract context.")
66
+
67
+ def render_contract(self, contract: SemanticContract) -> RenderedArtifacts:
68
+ planning = self.plan(contract)
69
+ return render_databricks_artifacts(contract, planning, self.native_capabilities, environment=self.environment)
@@ -0,0 +1,10 @@
1
+ from contractforge_databricks.annotations.application import apply_annotations_contract
2
+ from contractforge_databricks.annotations.audit import render_annotations_audit_insert_sql
3
+ from contractforge_databricks.annotations.sql import annotation_steps, render_annotations_sql
4
+
5
+ __all__ = [
6
+ "annotation_steps",
7
+ "apply_annotations_contract",
8
+ "render_annotations_audit_insert_sql",
9
+ "render_annotations_sql",
10
+ ]
@@ -0,0 +1,52 @@
1
+ """Apply Databricks annotations with an injected SQL runner."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from contractforge_core.semantic import SemanticContract
6
+ from contractforge_core.results import GovernanceApplyResult
7
+ from contractforge_databricks.annotations.sql import annotation_steps
8
+ from contractforge_databricks.execution.sql_merge import SqlRunner
9
+ from contractforge_databricks.security import exception_message
10
+
11
+
12
+ def apply_annotations_contract(*, runner: SqlRunner, contract: SemanticContract) -> GovernanceApplyResult:
13
+ steps = annotation_steps(contract)
14
+ if not steps:
15
+ return GovernanceApplyResult(status="NOT_CONFIGURED")
16
+ policy = _policy(contract)
17
+ sql_preview = tuple(str(step["sql"]) for step in steps)
18
+ if policy == "ignore":
19
+ return GovernanceApplyResult(status="IGNORED", ignored=len(steps), sql_preview=sql_preview)
20
+
21
+ applied = 0
22
+ errors: list[str] = []
23
+ for statement in sql_preview:
24
+ try:
25
+ runner.sql(statement)
26
+ applied += 1
27
+ except Exception as exc:
28
+ errors.append(exception_message(exc))
29
+ if policy == "fail":
30
+ return GovernanceApplyResult(
31
+ status="FAILED",
32
+ applied=applied,
33
+ failed=len(errors),
34
+ sql_preview=sql_preview,
35
+ errors=tuple(errors),
36
+ )
37
+ if errors:
38
+ return GovernanceApplyResult(
39
+ status="WARNED",
40
+ applied=applied,
41
+ failed=len(errors),
42
+ sql_preview=sql_preview,
43
+ errors=tuple(errors),
44
+ )
45
+ return GovernanceApplyResult(status="SUCCESS", applied=applied, sql_preview=sql_preview)
46
+
47
+
48
+ def _policy(contract: SemanticContract) -> str:
49
+ annotations = contract.governance.annotations if contract.governance else None
50
+ if isinstance(annotations, dict):
51
+ return str(annotations.get("policy", "warn"))
52
+ return "warn"
@@ -0,0 +1,49 @@
1
+ """Render annotation audit SQL for Databricks evidence tables."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+
7
+ from contractforge_core.semantic import SemanticContract
8
+ from contractforge_databricks.annotations.sql import annotation_steps
9
+ from contractforge_databricks.evidence.tables import evidence_table_names
10
+ from contractforge_databricks.rendering.names import target_full_name
11
+ from contractforge_databricks.sql import quote_table_name, sql_string
12
+
13
+
14
+ def render_annotations_audit_insert_sql(
15
+ contract: SemanticContract,
16
+ *,
17
+ run_id: str = "${run_id}",
18
+ status: str = "PLANNED",
19
+ captured_at_utc: datetime | None = None,
20
+ catalog: str = "main",
21
+ schema: str = "ops",
22
+ ) -> str:
23
+ steps = annotation_steps(contract)
24
+ if not steps:
25
+ return "-- No annotations intent declared.\n"
26
+ table = evidence_table_names(catalog, schema)["annotations"]
27
+ captured_at_utc = captured_at_utc or datetime(1970, 1, 1, 0, 0, 0)
28
+ statements = [
29
+ _audit_insert(table, run_id, target_full_name(contract), step, status, captured_at_utc)
30
+ for step in steps
31
+ ]
32
+ return ";\n".join(statements) + ";\n"
33
+
34
+
35
+ def _audit_insert(table: str, run_id: str, target: str, step: dict[str, object], status: str, captured_at_utc: datetime) -> str:
36
+ columns = "run_id, target_table, annotation_scope, annotation_type, column_name, key, value, status, applied_sql, annotation_ts_utc"
37
+ values = [
38
+ sql_string(run_id),
39
+ sql_string(target),
40
+ sql_string(step.get("annotation_scope")),
41
+ sql_string(step.get("annotation_type")),
42
+ sql_string(step.get("column_name")),
43
+ sql_string(step.get("key")),
44
+ sql_string(step.get("value")),
45
+ sql_string(status),
46
+ sql_string(step.get("sql")),
47
+ f"TIMESTAMP {sql_string(captured_at_utc.strftime('%Y-%m-%d %H:%M:%S'))}",
48
+ ]
49
+ return f"INSERT INTO {quote_table_name(table)} ({columns}) VALUES ({', '.join(values)})"
@@ -0,0 +1,142 @@
1
+ """Render Unity Catalog annotations SQL."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from contractforge_core.semantic import SemanticContract
8
+ from contractforge_databricks.coercion import mapping, string_list, string_map
9
+ from contractforge_databricks.rendering.names import target_full_name
10
+ from contractforge_databricks.sql import quote_identifier, quote_table_name, sql_string
11
+
12
+
13
+ def annotation_steps(contract: SemanticContract) -> list[dict[str, Any]]:
14
+ annotations = contract.governance.annotations if contract.governance else None
15
+ if not isinstance(annotations, dict):
16
+ return []
17
+
18
+ target = target_full_name(contract)
19
+ quoted_target = quote_table_name(target)
20
+ steps: list[dict[str, Any]] = []
21
+ table = mapping(annotations.get("table"))
22
+ if table.get("description"):
23
+ steps.append(
24
+ {
25
+ "annotation_scope": "table",
26
+ "annotation_type": "description",
27
+ "column_name": None,
28
+ "key": "description",
29
+ "value": str(table["description"]),
30
+ "sql": f"COMMENT ON TABLE {quoted_target} IS {sql_string(table['description'])}",
31
+ }
32
+ )
33
+ _append_tag_step(steps, quoted_target, None, _table_tags(table))
34
+ for column, config in mapping(annotations.get("columns")).items():
35
+ _append_column_steps(steps, quoted_target, str(column), mapping(config))
36
+ return steps
37
+
38
+
39
+ def render_annotations_sql(contract: SemanticContract) -> str:
40
+ annotations = contract.governance.annotations if contract.governance else None
41
+ if not isinstance(annotations, dict):
42
+ return "-- No annotations intent declared.\n"
43
+
44
+ target = target_full_name(contract)
45
+ lines = [
46
+ "-- Review before execution. Unity Catalog tags require UC-enabled targets.",
47
+ f"-- Target: {target}",
48
+ "",
49
+ ]
50
+ lines.extend(f"{step['sql']};" for step in annotation_steps(contract))
51
+ return "\n".join(lines) + "\n"
52
+
53
+
54
+ def _append_column_steps(
55
+ steps: list[dict[str, Any]],
56
+ quoted_target: str,
57
+ column: str,
58
+ config: dict[str, Any],
59
+ ) -> None:
60
+ quoted_column = quote_identifier(column)
61
+ if config.get("description"):
62
+ steps.append(
63
+ {
64
+ "annotation_scope": "column",
65
+ "annotation_type": "description",
66
+ "column_name": column,
67
+ "key": "description",
68
+ "value": str(config["description"]),
69
+ "sql": f"ALTER TABLE {quoted_target} ALTER COLUMN {quoted_column} COMMENT {sql_string(config['description'])}",
70
+ }
71
+ )
72
+ _append_tag_step(steps, quoted_target, column, _column_tags(config))
73
+
74
+
75
+ def _append_tag_step(steps: list[dict[str, Any]], quoted_target: str, column: str | None, tags: dict[str, str]) -> None:
76
+ if tags:
77
+ sql = f"ALTER TABLE {quoted_target} SET TAGS ({_tag_sql(tags)})"
78
+ if column is not None:
79
+ sql = f"ALTER TABLE {quoted_target} ALTER COLUMN {quote_identifier(column)} SET TAGS ({_tag_sql(tags)})"
80
+ steps.append(
81
+ {
82
+ "annotation_scope": "column" if column else "table",
83
+ "annotation_type": "tags",
84
+ "column_name": column,
85
+ "key": "tags",
86
+ "value": _json(tags),
87
+ "sql": sql,
88
+ }
89
+ )
90
+
91
+
92
+ def _table_tags(table: dict[str, Any]) -> dict[str, str]:
93
+ return {
94
+ **string_map(table.get("tags")),
95
+ **_alias_tags(table.get("aliases")),
96
+ **_deprecated_tags(table.get("deprecated")),
97
+ }
98
+
99
+
100
+ def _column_tags(config: dict[str, Any]) -> dict[str, str]:
101
+ return {
102
+ **string_map(config.get("tags")),
103
+ **_alias_tags(config.get("aliases")),
104
+ **_pii_tags(config.get("pii")),
105
+ **_deprecated_tags(config.get("deprecated")),
106
+ }
107
+
108
+
109
+ def _alias_tags(value: object) -> dict[str, str]:
110
+ return {f"alias_{idx}": alias for idx, alias in enumerate(string_list(value, sep="|"), start=1)}
111
+
112
+
113
+ def _deprecated_tags(value: object) -> dict[str, str]:
114
+ deprecated = mapping(value)
115
+ if not deprecated:
116
+ return {}
117
+ tags = {"deprecated": "true"}
118
+ for key in ("since", "replacement", "removal_date"):
119
+ if deprecated.get(key):
120
+ tags[f"deprecated_{key}"] = str(deprecated[key])
121
+ return tags
122
+
123
+
124
+ def _pii_tags(value: object) -> dict[str, str]:
125
+ pii = mapping(value)
126
+ if not pii:
127
+ return {}
128
+ return {
129
+ "pii": str(pii.get("enabled", True)).lower(),
130
+ "pii_type": str(pii.get("type", "unknown")),
131
+ "sensitivity": str(pii.get("sensitivity", "internal")),
132
+ }
133
+
134
+
135
+ def _tag_sql(tags: dict[str, str]) -> str:
136
+ return ", ".join(f"{sql_string(key)} = {sql_string(value)}" for key, value in tags.items())
137
+
138
+
139
+ def _json(value: dict[str, str]) -> str:
140
+ import json
141
+
142
+ return json.dumps(value, sort_keys=True, separators=(",", ":"))
@@ -0,0 +1,65 @@
1
+ """High-level Databricks adapter API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from contractforge_core.adapters import RenderedArtifacts
8
+ from contractforge_core.contracts import semantic_contract_from_mapping
9
+ from contractforge_core.planner import PlanningResult
10
+ from contractforge_databricks.adapter import DatabricksAdapter
11
+ from contractforge_databricks.contract_extensions import normalize_databricks_contract
12
+
13
+
14
+ def plan_databricks_contract(
15
+ contract: dict[str, Any],
16
+ *,
17
+ target_table: str | None = None,
18
+ runtime_type: str | None = None,
19
+ spark_version: str | None = None,
20
+ spark_conf: dict[str, str] | None = None,
21
+ environment: dict[str, Any] | None = None,
22
+ ) -> PlanningResult:
23
+ semantic = semantic_contract_from_mapping(normalize_databricks_contract(contract))
24
+ adapter = DatabricksAdapter.from_evidence(
25
+ target_table=target_table or _target_table(contract),
26
+ runtime_type=runtime_type,
27
+ spark_version=spark_version,
28
+ spark_conf=spark_conf,
29
+ environment=environment,
30
+ )
31
+ return adapter.plan(semantic)
32
+
33
+
34
+ def render_databricks_contract(
35
+ contract: dict[str, Any],
36
+ *,
37
+ target_table: str | None = None,
38
+ runtime_type: str | None = None,
39
+ spark_version: str | None = None,
40
+ spark_conf: dict[str, str] | None = None,
41
+ environment: dict[str, Any] | None = None,
42
+ ) -> RenderedArtifacts:
43
+ semantic = semantic_contract_from_mapping(normalize_databricks_contract(contract))
44
+ adapter = DatabricksAdapter.from_evidence(
45
+ target_table=target_table or _target_table(contract),
46
+ runtime_type=runtime_type,
47
+ spark_version=spark_version,
48
+ spark_conf=spark_conf,
49
+ environment=environment,
50
+ )
51
+ return adapter.render_contract(semantic)
52
+
53
+
54
+ def _target_table(contract: dict[str, Any]) -> str | None:
55
+ target = contract.get("target")
56
+ if isinstance(target, dict):
57
+ parts = [target.get("catalog"), target.get("schema"), target.get("table")]
58
+ if all(parts):
59
+ return ".".join(str(part) for part in parts)
60
+ table = contract.get("target_table")
61
+ catalog = contract.get("catalog")
62
+ schema = contract.get("target_schema")
63
+ if table and catalog and schema:
64
+ return f"{catalog}.{schema}.{table}"
65
+ return str(table) if table else None
@@ -0,0 +1,9 @@
1
+ from contractforge_databricks.bundles.assets import DatabricksJobSpec, render_databricks_asset_bundle
2
+ from contractforge_databricks.bundles.project import render_databricks_project_bundle, render_databricks_project_bundle_yaml
3
+
4
+ __all__ = [
5
+ "DatabricksJobSpec",
6
+ "render_databricks_asset_bundle",
7
+ "render_databricks_project_bundle",
8
+ "render_databricks_project_bundle_yaml",
9
+ ]
@@ -0,0 +1,47 @@
1
+ """Databricks Asset Bundle rendering."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class DatabricksJobSpec:
10
+ bundle_name: str
11
+ job_name: str
12
+ task_key: str
13
+ notebook_path: str
14
+ target: str = "dev"
15
+
16
+
17
+ def render_databricks_asset_bundle(spec: DatabricksJobSpec) -> str:
18
+ _require_non_empty(spec.bundle_name, "bundle_name")
19
+ _require_non_empty(spec.job_name, "job_name")
20
+ _require_non_empty(spec.task_key, "task_key")
21
+ _require_non_empty(spec.notebook_path, "notebook_path")
22
+ return "\n".join(
23
+ [
24
+ "bundle:",
25
+ f" name: {spec.bundle_name}",
26
+ "",
27
+ "resources:",
28
+ " jobs:",
29
+ f" {spec.job_name}:",
30
+ f" name: {spec.job_name}",
31
+ " tasks:",
32
+ f" - task_key: {spec.task_key}",
33
+ " notebook_task:",
34
+ f" notebook_path: {spec.notebook_path}",
35
+ "",
36
+ "targets:",
37
+ f" {spec.target}:",
38
+ " mode: development",
39
+ "",
40
+ ]
41
+ )
42
+
43
+
44
+ def _require_non_empty(value: str, field_name: str) -> None:
45
+ if not value or not value.strip():
46
+ raise ValueError(f"{field_name} must not be empty")
47
+