Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/rules/pydabs-acceptance-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ paths:
- "acceptance/bundle/python/**"
---

**RULE: Before adding a PyDABs resource acceptance test, read `acceptance/bundle/python/README.md`.** It covers the `<plural>-support/` fixture layout, how to source and adapt realistic field values, the version/engine `test.toml` knobs, and the determinism re-run. Every PyDABs resource needs one (enforced by `test_python_support_coverage`).
**RULE: Before adding a PyDABs resource acceptance test, read `acceptance/bundle/python/README.md`.** It covers the `<plural>-support/` fixture layout, how to source and adapt realistic field values, the version/engine `test.toml` knobs, the determinism re-run, and how to cover interface (`Any`) fields in both authoring forms. Every PyDABs resource needs one (enforced by `test_python_support_coverage`).
1 change: 1 addition & 0 deletions .nextchanges/bundles/pydabs-interface-resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added PyDABs (Python) support for cluster policies, dashboards, and Genie spaces. ([#6585](https://github.com/databricks/cli/pull/6585))
18 changes: 18 additions & 0 deletions acceptance/bundle/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,23 @@ files:
test that only passes with `-update` is nondeterministic (usually a `$VAR` or a
volatile field left in); fix it before finishing.

## Interface (`Any`) fields

A field generated as `VariableOrOptional[Any]` comes from a Go `interface{}` (schema ref
`#/$defs/interface`) — an untyped blob (e.g. a serialized JSON document) that a Go config
mutator normalizes to a string at deploy time. From Python it accepts two forms — an inline
`dict` and a serialized JSON `str` — and the two take different `_transform` code paths, so
a fixture must exercise **both through `add_<singular>`**. Covering only one leaves the
other path silently brittle, so add two Python instances:

- one sets the field as a **serialized JSON string** (`my_<name>_2`), and
- one sets it as an **inline dict** (`my_<name>_3`).

The YAML instance (`my_<name>_1`) may use either form, but it exercises the YAML loader, not
the Python `Any` path, so it does not substitute for either Python instance. The golden
shows all of them converging: a dict is marshalled to a compact JSON string, a string passes
through unchanged. See `cluster_policies-support/` for an example. Apply this to every
resource with an interface field.

Note: `bundle validate` normalizes the `python:` key to `experimental.python` in the
output — that's expected.
21 changes: 21 additions & 0 deletions acceptance/bundle/python/cluster_policies-support/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
bundle:
name: my_project

sync: {paths: []} # don't need to copy files

python:
resources:
- "resources:load_resources"
mutators:
- "mutators:update_cluster_policy"

resources:
cluster_policies:
my_cluster_policy_1:
name: "my_cluster_policy_1"
description: "My cluster policy"
# interface{} field authored inline as a YAML map
definition:
spark_version:
type: fixed
value: "13.3.x-scala2.12"
11 changes: 11 additions & 0 deletions acceptance/bundle/python/cluster_policies-support/mutators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from dataclasses import replace

from databricks.bundles.cluster_policies import ClusterPolicy
from databricks.bundles.core import cluster_policy_mutator


@cluster_policy_mutator
def update_cluster_policy(cluster_policy: ClusterPolicy) -> ClusterPolicy:
assert isinstance(cluster_policy.description, str)

return replace(cluster_policy, description=f"{cluster_policy.description} (updated)")

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions acceptance/bundle/python/cluster_policies-support/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

>>> uv run [UV_ARGS] -q [CLI] bundle validate --output json
{
"experimental": {
"python": {
"mutators": [
"mutators:update_cluster_policy"
],
"resources": [
"resources:load_resources"
]
}
},
"resources": {
"cluster_policies": {
"my_cluster_policy_1": {
"definition": "{\"spark_version\":{\"type\":\"fixed\",\"value\":\"13.3.x-scala2.12\"}}",
"description": "My cluster policy (updated)",
"name": "my_cluster_policy_1"
},
"my_cluster_policy_2": {
"definition": "{\"spark_version\": {\"type\": \"fixed\", \"value\": \"13.3.x-scala2.12\"}}",
"description": "My cluster policy (2) (updated)",
"name": "my_cluster_policy_2"
},
"my_cluster_policy_3": {
"definition": "{\"spark_version\":{\"type\":\"fixed\",\"value\":\"13.3.x-scala2.12\"}}",
"description": "My cluster policy (3) (updated)",
"name": "my_cluster_policy_3"
}
}
}
}
32 changes: 32 additions & 0 deletions acceptance/bundle/python/cluster_policies-support/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from databricks.bundles.core import Resources


def load_resources() -> Resources:
resources = Resources()

# interface{} field authored as a serialized JSON string
resources.add_cluster_policy(
"my_cluster_policy_2",
{
"name": "my_cluster_policy_2",
"description": "My cluster policy (2)",
"definition": '{"spark_version": {"type": "fixed", "value": "13.3.x-scala2.12"}}',
},
)

# interface{} field authored as an inline dict
resources.add_cluster_policy(
"my_cluster_policy_3",
{
"name": "my_cluster_policy_3",
"description": "My cluster policy (3)",
"definition": {
"spark_version": {
"type": "fixed",
"value": "13.3.x-scala2.12",
},
},
},
)

return resources
5 changes: 5 additions & 0 deletions acceptance/bundle/python/cluster_policies-support/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

trace uv run $UV_ARGS -q $CLI bundle validate --output json | \
jq "pick(.experimental.python, .resources)"

rm -fr .databricks __pycache__
7 changes: 7 additions & 0 deletions acceptance/bundle/python/cluster_policies-support/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Cloud = false # tests don't interact with APIs

# cluster_policies are only supported in the current version of the wheel
EnvMatrix.PYDAB_VERSION = ["current"]

# cluster_policies are only supported on the direct deployment engine
EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"]
22 changes: 22 additions & 0 deletions acceptance/bundle/python/dashboards-support/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
bundle:
name: my_project

sync: {paths: []} # don't need to copy files

python:
resources:
- "resources:load_resources"
mutators:
- "mutators:update_dashboard"

resources:
dashboards:
my_dashboard_1:
display_name: "my_dashboard_1"
warehouse_id: "abc123"
parent_path: "/Workspace/Users/me"
# interface{} field authored inline as a YAML map
serialized_dashboard:
pages:
- name: main
displayName: "Main"
11 changes: 11 additions & 0 deletions acceptance/bundle/python/dashboards-support/mutators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from dataclasses import replace

from databricks.bundles.core import dashboard_mutator
from databricks.bundles.dashboards import Dashboard


@dashboard_mutator
def update_dashboard(dashboard: Dashboard) -> Dashboard:
assert isinstance(dashboard.display_name, str)

return replace(dashboard, display_name=f"{dashboard.display_name} (updated)")
4 changes: 4 additions & 0 deletions acceptance/bundle/python/dashboards-support/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions acceptance/bundle/python/dashboards-support/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

>>> uv run [UV_ARGS] -q [CLI] bundle validate --output json
{
"experimental": {
"python": {
"mutators": [
"mutators:update_dashboard"
],
"resources": [
"resources:load_resources"
]
}
},
"resources": {
"dashboards": {
"my_dashboard_1": {
"display_name": "my_dashboard_1 (updated)",
"embed_credentials": false,
"parent_path": "/Workspace/Users/me",
"serialized_dashboard": "{\"pages\":[{\"displayName\":\"Main\",\"name\":\"main\"}]}",
"warehouse_id": "abc123"
},
"my_dashboard_2": {
"display_name": "my_dashboard_2 (updated)",
"embed_credentials": false,
"parent_path": "/Workspace/Users/me",
"serialized_dashboard": "{\"pages\": [{\"name\": \"main\", \"displayName\": \"Main\"}]}",
"warehouse_id": "abc123"
},
"my_dashboard_3": {
"display_name": "my_dashboard_3 (updated)",
"embed_credentials": false,
"parent_path": "/Workspace/Users/me",
"serialized_dashboard": "{\"pages\":[{\"displayName\":\"Main\",\"name\":\"main\"}]}",
"warehouse_id": "abc123"
}
}
}
}
36 changes: 36 additions & 0 deletions acceptance/bundle/python/dashboards-support/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from databricks.bundles.core import Resources


def load_resources() -> Resources:
resources = Resources()

# interface{} field authored as a serialized JSON string
resources.add_dashboard(
"my_dashboard_2",
{
"display_name": "my_dashboard_2",
"warehouse_id": "abc123",
"parent_path": "/Workspace/Users/me",
"serialized_dashboard": '{"pages": [{"name": "main", "displayName": "Main"}]}',
},
)

# interface{} field authored as an inline dict
resources.add_dashboard(
"my_dashboard_3",
{
"display_name": "my_dashboard_3",
"warehouse_id": "abc123",
"parent_path": "/Workspace/Users/me",
"serialized_dashboard": {
"pages": [
{
"name": "main",
"displayName": "Main",
},
],
},
},
)

return resources
5 changes: 5 additions & 0 deletions acceptance/bundle/python/dashboards-support/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

trace uv run $UV_ARGS -q $CLI bundle validate --output json | \
jq "pick(.experimental.python, .resources)"

rm -fr .databricks __pycache__
4 changes: 4 additions & 0 deletions acceptance/bundle/python/dashboards-support/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Cloud = false # tests don't interact with APIs

# dashboards are only supported in the current version of the wheel
EnvMatrix.PYDAB_VERSION = ["current"]
22 changes: 22 additions & 0 deletions acceptance/bundle/python/genie_spaces-support/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
bundle:
name: my_project

sync: {paths: []} # don't need to copy files

python:
resources:
- "resources:load_resources"
mutators:
- "mutators:update_genie_space"

resources:
genie_spaces:
my_genie_space_1:
title: "my_genie_space_1"
description: "My genie space"
warehouse_id: "abc123"
parent_path: "/Workspace/Users/me"
# interface{} field authored inline as a YAML map
serialized_space:
instructions:
- "Answer questions about sales data"
11 changes: 11 additions & 0 deletions acceptance/bundle/python/genie_spaces-support/mutators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from dataclasses import replace

from databricks.bundles.core import genie_space_mutator
from databricks.bundles.genie_spaces import GenieSpace


@genie_space_mutator
def update_genie_space(genie_space: GenieSpace) -> GenieSpace:
assert isinstance(genie_space.description, str)

return replace(genie_space, description=f"{genie_space.description} (updated)")
4 changes: 4 additions & 0 deletions acceptance/bundle/python/genie_spaces-support/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions acceptance/bundle/python/genie_spaces-support/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

>>> uv run [UV_ARGS] -q [CLI] bundle validate --output json
{
"experimental": {
"python": {
"mutators": [
"mutators:update_genie_space"
],
"resources": [
"resources:load_resources"
]
}
},
"resources": {
"genie_spaces": {
"my_genie_space_1": {
"description": "My genie space (updated)",
"parent_path": "/Workspace/Users/me",
"serialized_space": "{\"instructions\":[\"Answer questions about sales data\"]}",
"title": "my_genie_space_1",
"warehouse_id": "abc123"
},
"my_genie_space_2": {
"description": "My genie space (2) (updated)",
"parent_path": "/Workspace/Users/me",
"serialized_space": "{\"instructions\": [\"Answer questions about sales data\"]}",
"title": "my_genie_space_2",
"warehouse_id": "abc123"
},
"my_genie_space_3": {
"description": "My genie space (3) (updated)",
"parent_path": "/Workspace/Users/me",
"serialized_space": "{\"instructions\":[\"Answer questions about sales data\"]}",
"title": "my_genie_space_3",
"warehouse_id": "abc123"
}
}
}
}
35 changes: 35 additions & 0 deletions acceptance/bundle/python/genie_spaces-support/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from databricks.bundles.core import Resources


def load_resources() -> Resources:
resources = Resources()

# interface{} field authored as a serialized JSON string
resources.add_genie_space(
"my_genie_space_2",
{
"title": "my_genie_space_2",
"description": "My genie space (2)",
"warehouse_id": "abc123",
"parent_path": "/Workspace/Users/me",
"serialized_space": '{"instructions": ["Answer questions about sales data"]}',
},
)

# interface{} field authored as an inline dict
resources.add_genie_space(
"my_genie_space_3",
{
"title": "my_genie_space_3",
"description": "My genie space (3)",
"warehouse_id": "abc123",
"parent_path": "/Workspace/Users/me",
"serialized_space": {
"instructions": [
"Answer questions about sales data",
],
},
},
)

return resources
Loading
Loading