Skip to content

Commit 8a62c53

Browse files
Fix: Add support for args syntax in dbt config function
1 parent a3e7bda commit 8a62c53

File tree

3 files changed

+101
-7
lines changed

3 files changed

+101
-7
lines changed

sqlmesh/dbt/builtin.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,17 @@ class Config:
168168
def __init__(self, config_dict: t.Dict[str, t.Any]) -> None:
169169
self._config = config_dict
170170

171-
def __call__(self, **kwargs: t.Any) -> str:
172-
self._config.update(**kwargs)
171+
def __call__(self, *args: t.Any, **kwargs: t.Any) -> str:
172+
if args and not kwargs and len(args) == 1 and isinstance(args[0], dict):
173+
# Single dict argument: config({"materialized": "table"})
174+
self._config.update(args[0])
175+
elif not args and kwargs:
176+
# Keyword arguments: config(materialized="table")
177+
self._config.update(kwargs)
178+
elif args:
179+
raise ConfigError(
180+
f"Invalid config usage: expected either a single dict or keyword arguments"
181+
)
173182
return ""
174183

175184
def set(self, name: str, value: t.Any) -> str:

tests/dbt/test_transformation.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import pytest
1212
from dbt.adapters.base import BaseRelation
13+
from jinja2 import Template
1314

1415
if DBT_VERSION >= (1, 4, 0):
1516
from dbt.exceptions import CompilationError
@@ -42,7 +43,7 @@
4243
OnAdditiveChange,
4344
)
4445
from sqlmesh.core.state_sync.db.snapshot import _snapshot_to_json
45-
from sqlmesh.dbt.builtin import _relation_info_to_relation
46+
from sqlmesh.dbt.builtin import _relation_info_to_relation, Config
4647
from sqlmesh.dbt.common import Dependencies
4748
from sqlmesh.dbt.column import (
4849
ColumnConfig,
@@ -1052,6 +1053,89 @@ def test_config_jinja(sushi_test_project: Project):
10521053
assert model.render_pre_statements()[0].sql() == '"bar"'
10531054

10541055

1056+
@pytest.mark.xdist_group("dbt_manifest")
1057+
def test_config_dict_syntax():
1058+
# Test dictionary syntax
1059+
config = Config({})
1060+
result = config({"materialized": "table", "alias": "dict_table"})
1061+
assert result == ""
1062+
assert config._config["materialized"] == "table"
1063+
assert config._config["alias"] == "dict_table"
1064+
1065+
# Test kwargs syntax still works
1066+
config2 = Config({})
1067+
result = config2(materialized="view", alias="kwargs_table")
1068+
assert result == ""
1069+
assert config2._config["materialized"] == "view"
1070+
assert config2._config["alias"] == "kwargs_table"
1071+
1072+
# Test that mixing args and kwargs is rejected
1073+
config3 = Config({})
1074+
try:
1075+
config3({"materialized": "table"}, alias="mixed")
1076+
assert False, "Should have raised ConfigError"
1077+
except Exception as e:
1078+
assert "Invalid config usage" in str(e)
1079+
1080+
# Test nested dicts
1081+
config4 = Config({})
1082+
config4({"meta": {"owner": "data_team", "priority": 1}, "tags": ["daily", "critical"]})
1083+
assert config4._config["meta"]["owner"] == "data_team"
1084+
assert config4._config["tags"] == ["daily", "critical"]
1085+
1086+
1087+
def test_config_dict_in_jinja():
1088+
# Test dict syntax directly with Config class
1089+
config = Config({})
1090+
template = Template("{{ config({'materialized': 'table', 'unique_key': 'id'}) }}done")
1091+
result = template.render(config=config)
1092+
assert result == "done"
1093+
assert config._config["materialized"] == "table"
1094+
assert config._config["unique_key"] == "id"
1095+
1096+
# Test with nested dict and list values
1097+
config2 = Config({})
1098+
complex_template = Template("""{{ config({
1099+
'tags': ['test', 'dict'],
1100+
'meta': {'owner': 'data_team'}
1101+
}) }}result""")
1102+
result = complex_template.render(config=config2)
1103+
assert result == "result"
1104+
assert config2._config["tags"] == ["test", "dict"]
1105+
assert config2._config["meta"]["owner"] == "data_team"
1106+
1107+
# Test that kwargs still work
1108+
config3 = Config({})
1109+
kwargs_template = Template("{{ config(materialized='view', alias='my_view') }}done")
1110+
result = kwargs_template.render(config=config3)
1111+
assert result == "done"
1112+
assert config3._config["materialized"] == "view"
1113+
assert config3._config["alias"] == "my_view"
1114+
1115+
1116+
@pytest.mark.xdist_group("dbt_manifest")
1117+
def test_config_dict_syntax_in_sushi_project(sushi_test_project: Project):
1118+
assert sushi_test_project is not None
1119+
assert sushi_test_project.context is not None
1120+
1121+
sushi_package = sushi_test_project.packages.get("sushi")
1122+
assert sushi_package is not None
1123+
1124+
top_waiters_found = False
1125+
for model_config in sushi_package.models.values():
1126+
if model_config.name == "top_waiters":
1127+
# top_waiters model now uses dict config syntax with:
1128+
# config({'materialized': 'view', 'limit_value': var('top_waiters:limit'), 'meta': {...}})
1129+
top_waiters_found = True
1130+
assert model_config.materialized == "view"
1131+
assert model_config.meta is not None
1132+
assert model_config.meta.get("owner") == "analytics_team"
1133+
assert model_config.meta.get("priority") == "high"
1134+
break
1135+
1136+
assert top_waiters_found
1137+
1138+
10551139
@pytest.mark.xdist_group("dbt_manifest")
10561140
def test_config_jinja_get_methods(sushi_test_project: Project):
10571141
model_config = ModelConfig(

tests/fixtures/dbt/sushi_test/models/top_waiters.sql

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{{
2-
config(
3-
materialized='view',
4-
limit_value=var('top_waiters:limit'),
5-
)
2+
config({
3+
'materialized': 'view',
4+
'limit_value': var('top_waiters:limit'),
5+
'meta': {'owner': 'analytics_team', 'priority': 'high'}
6+
})
67
}}
78

89
{% set columns = model.columns %}

0 commit comments

Comments
 (0)