Repository: dbt-labs/dbt-core

Issue:
Add deprecation warning when providing `packages` argument to `adapter.dispatch`
### Describe the feature
Add a deprecation warning if users provide the packages argument to dispatch.


Discussion:

Resolving patch:
diff --git a/core/dbt/clients/jinja_static.py b/core/dbt/clients/jinja_static.py
--- a/core/dbt/clients/jinja_static.py
+++ b/core/dbt/clients/jinja_static.py
@@ -96,7 +96,7 @@ def statically_parse_adapter_dispatch(func_call, ctx, db_wrapper):
         possible_macro_calls.append(func_name)
 
     # packages positional argument
-    packages = []
+    packages = None
     macro_namespace = None
     packages_arg = None
     packages_arg_type = None
@@ -163,6 +163,7 @@ def statically_parse_adapter_dispatch(func_call, ctx, db_wrapper):
                         default_packages = [package_name]
 
                     namespace_names = get_dispatch_list(ctx, var_name, default_packages)
+                    packages = []
                     if namespace_names:
                         packages.extend(namespace_names)
                 else:
@@ -190,21 +191,23 @@ def statically_parse_adapter_dispatch(func_call, ctx, db_wrapper):
                     default_namespaces.append(item.value)
             if namespace_var:
                 namespace_names = get_dispatch_list(ctx, namespace_var, default_namespaces)
+                packages = []
                 if namespace_names:
                     packages.extend(namespace_names)
 
     if db_wrapper:
-        if not packages:
-            if macro_namespace:
-                packages = macro_namespace
-            else:
-                packages = None  # empty list behaves differently than None...
-        macro = db_wrapper.dispatch(func_name, packages).macro
+        macro = db_wrapper.dispatch(
+            func_name,
+            packages=packages,
+            macro_namespace=macro_namespace
+        ).macro
         func_name = f'{macro.package_name}.{macro.name}'
         possible_macro_calls.append(func_name)
     else:  # this is only for test/unit/test_macro_calls.py
         if macro_namespace:
             packages = [macro_namespace]
+        if packages is None:
+            packages = []
         for package_name in packages:
             possible_macro_calls.append(f'{package_name}.{func_name}')
 
diff --git a/core/dbt/context/providers.py b/core/dbt/context/providers.py
--- a/core/dbt/context/providers.py
+++ b/core/dbt/context/providers.py
@@ -117,8 +117,10 @@ def _get_adapter_macro_prefixes(self) -> List[str]:
         return search_prefixes
 
     def dispatch(
-            self, macro_name: str, packages: Optional[List[str]] = None,
-            macro_namespace: Optional[str] = None,
+        self,
+        macro_name: str,
+        macro_namespace: Optional[str] = None,
+        packages: Optional[List[str]] = None,
     ) -> MacroGenerator:
         search_packages: List[Optional[str]]
 
@@ -132,6 +134,9 @@ def dispatch(
             )
             raise CompilationException(msg)
 
+        if packages is not None:
+            deprecations.warn('dispatch-packages', macro_name=macro_name)
+
         namespace = packages if packages else macro_namespace
 
         if namespace is None:
@@ -1190,14 +1195,13 @@ def adapter_macro(self, name: str, *args, **kwargs):
         """
         deprecations.warn('adapter-macro', macro_name=name)
         original_name = name
-        package_names: Optional[List[str]] = None
+        package_name = None
         if '.' in name:
             package_name, name = name.split('.', 1)
-            package_names = [package_name]
 
         try:
             macro = self.db_wrapper.dispatch(
-                macro_name=name, packages=package_names
+                macro_name=name, macro_namespace=package_name
             )
         except CompilationException as exc:
             raise CompilationException(
diff --git a/core/dbt/deprecations.py b/core/dbt/deprecations.py
--- a/core/dbt/deprecations.py
+++ b/core/dbt/deprecations.py
@@ -43,6 +43,20 @@ def show(self, *args, **kwargs) -> None:
             active_deprecations.add(self.name)
 
 
+class DispatchPackagesDeprecation(DBTDeprecation):
+    _name = 'dispatch-packages'
+    _description = '''\
+    The "packages" argument of adapter.dispatch() has been deprecated.
+    Use the "macro_namespace" argument instead.
+
+    Raised during dispatch for: {macro_name}
+
+    For more information, see:
+
+    https://docs.getdbt.com/reference/dbt-jinja-functions/dispatch
+    '''
+
+
 class MaterializationReturnDeprecation(DBTDeprecation):
     _name = 'materialization-return'
 
@@ -155,6 +169,7 @@ def warn(name, *args, **kwargs):
 active_deprecations: Set[str] = set()
 
 deprecations_list: List[DBTDeprecation] = [
+    DispatchPackagesDeprecation(),
     MaterializationReturnDeprecation(),
     NotADictionaryDeprecation(),
     ColumnQuotingDeprecation(),


Test patch:
diff --git a/test/integration/012_deprecation_tests/dispatch-macros/cast.sql b/test/integration/012_deprecation_tests/dispatch-macros/cast.sql
new file mode 100644
--- /dev/null
+++ b/test/integration/012_deprecation_tests/dispatch-macros/cast.sql
@@ -0,0 +1,13 @@
+
+
+{% macro string_literal(s) -%}
+  {{ adapter.dispatch('string_literal', packages=['test'])(s) }}
+{%- endmacro %}
+
+{% macro default__string_literal(s) %}
+    '{{ s }}'::text
+{% endmacro %}
+
+{% macro bigquery__string_literal(s) %}
+    cast('{{ s }}' as string)
+{% endmacro %}
diff --git a/test/integration/012_deprecation_tests/dispatch-macros/expect_value.sql b/test/integration/012_deprecation_tests/dispatch-macros/expect_value.sql
new file mode 100644
--- /dev/null
+++ b/test/integration/012_deprecation_tests/dispatch-macros/expect_value.sql
@@ -0,0 +1,10 @@
+
+-- cross-db compatible test, similar to accepted_values
+
+{% test expect_value(model, field, value) %}
+
+select *
+from {{ model }}
+where {{ field }} != '{{ value }}'
+
+{% endtest %}
diff --git a/test/integration/012_deprecation_tests/dispatch-models/alias_in_project.sql b/test/integration/012_deprecation_tests/dispatch-models/alias_in_project.sql
new file mode 100644
--- /dev/null
+++ b/test/integration/012_deprecation_tests/dispatch-models/alias_in_project.sql
@@ -0,0 +1,2 @@
+
+select {{ string_literal(this.name) }} as tablename
diff --git a/test/integration/012_deprecation_tests/dispatch-models/alias_in_project_with_override.sql b/test/integration/012_deprecation_tests/dispatch-models/alias_in_project_with_override.sql
new file mode 100644
--- /dev/null
+++ b/test/integration/012_deprecation_tests/dispatch-models/alias_in_project_with_override.sql
@@ -0,0 +1,4 @@
+
+{{ config(alias='override_alias') }}
+
+select {{ string_literal(this.name) }} as tablename
diff --git a/test/integration/012_deprecation_tests/dispatch-models/foo_alias.sql b/test/integration/012_deprecation_tests/dispatch-models/foo_alias.sql
new file mode 100644
--- /dev/null
+++ b/test/integration/012_deprecation_tests/dispatch-models/foo_alias.sql
@@ -0,0 +1,9 @@
+
+{{
+    config(
+        alias='foo',
+        materialized='table'
+    )
+}}
+
+select {{ string_literal(this.name) }} as tablename
diff --git a/test/integration/012_deprecation_tests/dispatch-models/ref_foo_alias.sql b/test/integration/012_deprecation_tests/dispatch-models/ref_foo_alias.sql
new file mode 100644
--- /dev/null
+++ b/test/integration/012_deprecation_tests/dispatch-models/ref_foo_alias.sql
@@ -0,0 +1,16 @@
+
+{{
+    config(
+        materialized='table'
+    )
+}}
+
+with trigger_ref as (
+
+  -- we should still be able to ref a model by its filepath
+  select * from {{ ref('foo_alias') }}
+
+)
+
+-- this name should still be the filename
+select {{ string_literal(this.name) }} as tablename
diff --git a/test/integration/012_deprecation_tests/dispatch-models/schema.yml b/test/integration/012_deprecation_tests/dispatch-models/schema.yml
new file mode 100644
--- /dev/null
+++ b/test/integration/012_deprecation_tests/dispatch-models/schema.yml
@@ -0,0 +1,22 @@
+version: 2
+models:
+- name: foo_alias
+  tests:
+  - expect_value:
+      field: tablename
+      value: foo
+- name: ref_foo_alias
+  tests:
+  - expect_value:
+      field: tablename
+      value: ref_foo_alias
+- name: alias_in_project
+  tests:
+  - expect_value:
+      field: tablename
+      value: project_alias
+- name: alias_in_project_with_override
+  tests:
+  - expect_value:
+      field: tablename
+      value: override_alias
diff --git a/test/integration/012_deprecation_tests/test_deprecations.py b/test/integration/012_deprecation_tests/test_deprecations.py
--- a/test/integration/012_deprecation_tests/test_deprecations.py
+++ b/test/integration/012_deprecation_tests/test_deprecations.py
@@ -149,3 +149,41 @@ def test_bigquery_adapter_macro_pkg(self):
             self.run_dbt(strict=False, expect_pass=False)
         exc_str = ' '.join(str(exc.exception).split())  # flatten all whitespace
         assert 'not allowed' in exc_str  # we saw the default macro
+
+
+class TestDispatchPackagesDeprecation(BaseTestDeprecations):
+    @property
+    def models(self):
+        return self.dir('dispatch-models')
+
+    @property
+    def project_config(self):
+        return {
+            'config-version': 2,
+            "macro-paths": [self.dir('dispatch-macros')],
+            "models": {
+                "test": {
+                    "alias_in_project": {
+                        "alias": 'project_alias',
+                    },
+                    "alias_in_project_with_override": {
+                        "alias": 'project_alias',
+                    },
+                }
+            }
+        }
+
+    @use_profile('postgres')
+    def test_postgres_adapter_macro(self):
+        self.assertEqual(deprecations.active_deprecations, set())
+        self.run_dbt(strict=False)
+        expected = {'dispatch-packages'}
+        self.assertEqual(expected, deprecations.active_deprecations)
+
+    @use_profile('postgres')
+    def test_postgres_adapter_macro_fail(self):
+        self.assertEqual(deprecations.active_deprecations, set())
+        with self.assertRaises(dbt.exceptions.CompilationException) as exc:
+            self.run_dbt(strict=True)
+        exc_str = ' '.join(str(exc.exception).split())  # flatten all whitespace
+        assert 'Raised during dispatch for: string_literal' in exc_str
diff --git a/test/integration/026_aliases_test/macros/cast.sql b/test/integration/026_aliases_test/macros/cast.sql
--- a/test/integration/026_aliases_test/macros/cast.sql
+++ b/test/integration/026_aliases_test/macros/cast.sql
@@ -1,7 +1,7 @@
 
 
 {% macro string_literal(s) -%}
-  {{ adapter.dispatch('string_literal', packages=['test'])(s) }}
+  {{ adapter.dispatch('string_literal', macro_namespace='test')(s) }}
 {%- endmacro %}
 
 {% macro default__string_literal(s) %}
diff --git a/test/integration/043_custom_aliases_test/macros-configs/macros.sql b/test/integration/043_custom_aliases_test/macros-configs/macros.sql
--- a/test/integration/043_custom_aliases_test/macros-configs/macros.sql
+++ b/test/integration/043_custom_aliases_test/macros-configs/macros.sql
@@ -9,7 +9,7 @@
 {%- endmacro %}
 
 {% macro string_literal(s) -%}
-  {{ adapter.dispatch('string_literal', packages=['test'])(s) }}
+  {{ adapter.dispatch('string_literal', macro_namespace='test')(s) }}
 {%- endmacro %}
 
 {% macro default__string_literal(s) %}
diff --git a/test/integration/043_custom_aliases_test/macros/macros.sql b/test/integration/043_custom_aliases_test/macros/macros.sql
--- a/test/integration/043_custom_aliases_test/macros/macros.sql
+++ b/test/integration/043_custom_aliases_test/macros/macros.sql
@@ -9,7 +9,7 @@
 
 
 {% macro string_literal(s) -%}
-  {{ adapter.dispatch('string_literal', packages=['test'])(s) }}
+  {{ adapter.dispatch('string_literal', macro_namespace='test')(s) }}
 {%- endmacro %}
 
 {% macro default__string_literal(s) %}