From 908d5b038e9b3e6afa3f31ed4571154f32778d5b Mon Sep 17 00:00:00 2001 From: Themis Valtinos <73662635+themisvaltinos@users.noreply.github.com> Date: Thu, 18 Sep 2025 23:00:43 +0300 Subject: [PATCH] Fix(dbt): Update dispatch signature to match dbt method --- sqlmesh/dbt/adapter.py | 20 ++++++++++++-------- tests/dbt/test_adapter.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/sqlmesh/dbt/adapter.py b/sqlmesh/dbt/adapter.py index 236d4cee6b..12e38e4749 100644 --- a/sqlmesh/dbt/adapter.py +++ b/sqlmesh/dbt/adapter.py @@ -115,28 +115,32 @@ def quote_as_configured(self, value: str, component_type: str) -> str: """Returns the value quoted according to the quote policy.""" return self.quote(value) if getattr(self.quote_policy, component_type, False) else value - def dispatch(self, name: str, package: t.Optional[str] = None) -> t.Callable: + def dispatch( + self, + macro_name: str, + macro_namespace: t.Optional[str] = None, + ) -> t.Callable: """Returns a dialect-specific version of a macro with the given name.""" target_type = self.jinja_globals["target"]["type"] - macro_suffix = f"__{name}" + macro_suffix = f"__{macro_name}" def _relevance(package_name_pair: t.Tuple[t.Optional[str], str]) -> t.Tuple[int, int]: """Lower scores more relevant.""" - macro_package, macro_name = package_name_pair + macro_package, name = package_name_pair - package_score = 0 if macro_package == package else 1 + package_score = 0 if macro_package == macro_namespace else 1 name_score = 1 - if macro_name.startswith("default"): + if name.startswith("default"): name_score = 2 - elif macro_name.startswith(target_type): + elif name.startswith(target_type): name_score = 0 return name_score, package_score jinja_env = self.jinja_macros.build_environment(**self.jinja_globals).globals packages_to_check: t.List[t.Optional[str]] = [ - package, + macro_namespace, *(k for k in jinja_env if k.startswith("dbt")), ] candidates = {} @@ -156,7 +160,7 @@ def _relevance(package_name_pair: t.Tuple[t.Optional[str], str]) -> t.Tuple[int, sorted_candidates = sorted(candidates, key=_relevance) return candidates[sorted_candidates[0]] - raise ConfigError(f"Macro '{name}', package '{package}' was not found.") + raise ConfigError(f"Macro '{macro_name}', package '{macro_namespace}' was not found.") def type(self) -> str: return self.project_dialect or "" diff --git a/tests/dbt/test_adapter.py b/tests/dbt/test_adapter.py index 85dfa29559..5617d8c5c3 100644 --- a/tests/dbt/test_adapter.py +++ b/tests/dbt/test_adapter.py @@ -243,6 +243,34 @@ def test_adapter_dispatch(sushi_test_project: Project, runtime_renderer: t.Calla assert renderer("{{ adapter.dispatch('current_timestamp')() }}") == "now()" assert renderer("{{ adapter.dispatch('current_timestamp', 'dbt')() }}") == "now()" + # test with keyword arguments + assert ( + renderer( + "{{ adapter.dispatch(macro_name='current_engine', macro_namespace='customers')() }}" + ) + == "duckdb" + ) + assert renderer("{{ adapter.dispatch(macro_name='current_timestamp')() }}") == "now()" + assert ( + renderer("{{ adapter.dispatch(macro_name='current_timestamp', macro_namespace='dbt')() }}") + == "now()" + ) + + # mixing positional and keyword arguments + assert ( + renderer("{{ adapter.dispatch('current_engine', macro_namespace='customers')() }}") + == "duckdb" + ) + assert ( + renderer("{{ adapter.dispatch('current_timestamp', macro_namespace=None)() }}") == "now()" + ) + assert ( + renderer("{{ adapter.dispatch('current_timestamp', macro_namespace='dbt')() }}") == "now()" + ) + + with pytest.raises(ConfigError, match=r"Macro 'current_engine'.*was not found."): + renderer("{{ adapter.dispatch(macro_name='current_engine')() }}") + with pytest.raises(ConfigError, match=r"Macro 'current_engine'.*was not found."): renderer("{{ adapter.dispatch('current_engine')() }}")