Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix macro namespace search packages #5804

Merged
merged 8 commits into from
Jun 27, 2023
Merged
7 changes: 7 additions & 0 deletions .changes/unreleased/Fixes-20220909-164413.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Fixes
body: Raise better error message when dispatching a package that is not installed
time: 2022-09-09T16:44:13.382685-06:00
custom:
Author: "dbeatty10"
Issue: "5801"
PR: "5804"
36 changes: 21 additions & 15 deletions core/dbt/context/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,25 @@ def _get_adapter_macro_prefixes(self) -> List[str]:
search_prefixes = get_adapter_type_names(self._adapter.type()) + ["default"]
return search_prefixes

def _get_search_packages(self, namespace: Optional[str] = None) -> List[Optional[str]]:
search_packages: List[Optional[str]] = [None]

if namespace is None:
search_packages = [None]
elif isinstance(namespace, str):
macro_search_order = self._adapter.config.get_macro_search_order(namespace)
if macro_search_order:
search_packages = macro_search_order
elif not macro_search_order and namespace in self._adapter.config.dependencies:
search_packages = [self.config.project_name, namespace]
else:
raise CompilationError(
f"In adapter.dispatch, got a {type(namespace)} macro_namespace argument "
f'("{namespace}"), but macro_namespace should be None or a string.'
)

return search_packages

def dispatch(
self,
macro_name: str,
Expand All @@ -154,20 +173,7 @@ def dispatch(
if packages is not None:
raise MacroDispatchArgError(macro_name)

namespace = macro_namespace

if namespace is None:
search_packages = [None]
elif isinstance(namespace, str):
search_packages = self._adapter.config.get_macro_search_order(namespace)
if not search_packages and namespace in self._adapter.config.dependencies:
search_packages = [self.config.project_name, namespace]
else:
# Not a string and not None so must be a list
raise CompilationError(
f"In adapter.dispatch, got a list macro_namespace argument "
f'("{macro_namespace}"), but macro_namespace should be None or a string.'
)
search_packages = self._get_search_packages(macro_namespace)

attempts = []

Expand All @@ -191,7 +197,7 @@ def dispatch(
return macro

searched = ", ".join(repr(a) for a in attempts)
msg = f"In dispatch: No macro named '{macro_name}' found\n Searched for: {searched}"
msg = f"In dispatch: No macro named '{macro_name}' found within namespace: '{macro_namespace}'\n Searched for: {searched}"
raise CompilationError(msg)


Expand Down
25 changes: 25 additions & 0 deletions tests/functional/macros/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,28 @@
{{ adapter_macro('some_macro', arg1, arg2) }}
{%- endmacro %}
"""

macros__incorrect_dispatch = """
{% macro cowsay() %}
{{ return(adapter.dispatch('cowsay', 'farm_utils')()) }}
{%- endmacro %}
{% macro default__cowsay() %}
'moo'
{% endmacro %}
"""

# Note the difference between `test_utils` below and `farm_utils` above
models__incorrect_dispatch = """
select {{ test_utils.cowsay() }} as cowsay
"""

dbt_project__incorrect_dispatch = """
name: 'test_utils'
version: '1.0'
config-version: 2
profile: 'default'
macro-paths: ["macros"]
"""
41 changes: 41 additions & 0 deletions tests/functional/macros/test_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@
check_relations_equal,
)

from dbt.tests.fixtures.project import write_project_files
from tests.functional.macros.fixtures import (
dbt_project__incorrect_dispatch,
models__dep_macro,
models__with_undefined_macro,
models__local_macro,
models__ref_macro,
models__override_get_columns_macros,
models__deprecated_adapter_macro_model,
models__incorrect_dispatch,
macros__my_macros,
macros__no_default_macros,
macros__override_get_columns_macros,
macros__package_override_get_columns_macros,
macros__deprecated_adapter_macro,
macros__incorrect_dispatch,
)


Expand Down Expand Up @@ -203,6 +207,43 @@ def test_overrides(self, project):
run_dbt()


class TestMisnamedMacroNamespace:
@pytest.fixture(scope="class", autouse=True)
def setUp(self, project_root):
test_utils_files = {
"dbt_project.yml": dbt_project__incorrect_dispatch,
"macros": {
"cowsay.sql": macros__incorrect_dispatch,
},
}
write_project_files(project_root, "test_utils", test_utils_files)

@pytest.fixture(scope="class")
def models(self):
return {
"my_model.sql": models__incorrect_dispatch,
}

@pytest.fixture(scope="class")
def packages(self):
return {
"packages": [
{"local": "test_utils"},
]
}

def test_misnamed_macro_namespace(
self,
project,
):
run_dbt(["deps"])

with pytest.raises(dbt.exceptions.CompilationError) as exc:
run_dbt()

assert "In dispatch: No macro named 'cowsay' found" in str(exc.value)


class TestAdapterMacroDeprecated:
@pytest.fixture(scope="class")
def models(self):
Expand Down