diff --git a/.changes/unreleased/Fixes-20240806-194843.yaml b/.changes/unreleased/Fixes-20240806-194843.yaml new file mode 100644 index 00000000000..7eb5a4bd8d8 --- /dev/null +++ b/.changes/unreleased/Fixes-20240806-194843.yaml @@ -0,0 +1,6 @@ +kind: Fixes +body: respect --quiet and --warn-error-options for flag deprecations +time: 2024-08-06T19:48:43.399453-04:00 +custom: + Author: michelleark + Issue: "10105" diff --git a/core/dbt/cli/flags.py b/core/dbt/cli/flags.py index 4f1bcc2c4df..2b957ed9465 100644 --- a/core/dbt/cli/flags.py +++ b/core/dbt/cli/flags.py @@ -15,7 +15,7 @@ from dbt.cli.types import Command as CliCommand from dbt.config.project import read_project_flags from dbt.contracts.project import ProjectFlags -from dbt.deprecations import renamed_env_var +from dbt.deprecations import fire_buffered_deprecations, renamed_env_var from dbt.events import ALL_EVENT_NAMES from dbt_common import ui from dbt_common.clients import jinja @@ -355,6 +355,8 @@ def fire_deprecations(self): # not get pickled when written to disk as json. object.__delattr__(self, "deprecated_env_var_warnings") + fire_buffered_deprecations() + @classmethod def from_dict(cls, command: CliCommand, args_dict: Dict[str, Any]) -> "Flags": command_arg_list = command_params(command, args_dict) diff --git a/core/dbt/config/project.py b/core/dbt/config/project.py index e1dfac4e10c..313cdb0bddf 100644 --- a/core/dbt/config/project.py +++ b/core/dbt/config/project.py @@ -821,8 +821,8 @@ def read_project_flags(project_dir: str, profiles_dir: str) -> ProjectFlags: if profile_project_flags: # This can't use WARN_ERROR or WARN_ERROR_OPTIONS because they're in - # the config that we're loading. Uses special "warn" method. - deprecations.warn("project-flags-moved") + # the config that we're loading. Uses special "buffer" method and fired after flags are initialized in preflight. + deprecations.buffer("project-flags-moved") project_flags = profile_project_flags if project_flags is not None: diff --git a/core/dbt/deprecations.py b/core/dbt/deprecations.py index 53f95ee1e65..114af126ea0 100644 --- a/core/dbt/deprecations.py +++ b/core/dbt/deprecations.py @@ -1,9 +1,9 @@ import abc -from typing import ClassVar, Dict, List, Optional, Set +from typing import Callable, ClassVar, Dict, List, Optional, Set import dbt.tracking from dbt.events import types as core_types -from dbt_common.events.functions import fire_event, warn_or_error +from dbt_common.events.functions import warn_or_error class DBTDeprecation: @@ -107,15 +107,6 @@ class ProjectFlagsMovedDeprecation(DBTDeprecation): _name = "project-flags-moved" _event = "ProjectFlagsMovedDeprecation" - def show(self, *args, **kwargs) -> None: - if self.name not in active_deprecations: - event = self.event(**kwargs) - # We can't do warn_or_error because the ProjectFlags - # is where that is set up and we're just reading it. - fire_event(event) - self.track_deprecation_warn() - active_deprecations.add(self.name) - class PackageMaterializationOverrideDeprecation(DBTDeprecation): _name = "package-materialization-override" @@ -155,6 +146,13 @@ def warn(name, *args, **kwargs): deprecations[name].show(*args, **kwargs) +def buffer(name: str, *args, **kwargs): + def show_callback(): + deprecations[name].show(*args, **kwargs) + + buffered_deprecations.append(show_callback) + + # these are globally available # since modules are only imported once, active_deprecations is a singleton @@ -178,6 +176,13 @@ def warn(name, *args, **kwargs): deprecations: Dict[str, DBTDeprecation] = {d.name: d for d in deprecations_list} +buffered_deprecations: List[Callable] = [] + def reset_deprecations(): active_deprecations.clear() + + +def fire_buffered_deprecations(): + [dep_fn() for dep_fn in buffered_deprecations] + buffered_deprecations.clear() diff --git a/tests/functional/deprecations/test_deprecations.py b/tests/functional/deprecations/test_deprecations.py index 8082be7b911..95779338d8a 100644 --- a/tests/functional/deprecations/test_deprecations.py +++ b/tests/functional/deprecations/test_deprecations.py @@ -3,7 +3,8 @@ import dbt_common from dbt import deprecations -from dbt.tests.util import run_dbt, write_file +from dbt.tests.util import run_dbt, run_dbt_and_capture, write_file +from dbt_common.exceptions import EventCompilationError from tests.functional.deprecations.fixtures import ( bad_name_yaml, models_trivial__model_sql, @@ -143,6 +144,45 @@ def models(self): def test_profile_config_deprecation(self, project): deprecations.reset_deprecations() assert deprecations.active_deprecations == set() - run_dbt(["parse"]) - expected = {"project-flags-moved"} - assert expected == deprecations.active_deprecations + + _, logs = run_dbt_and_capture(["parse"]) + + assert ( + "User config should be moved from the 'config' key in profiles.yml to the 'flags' key in dbt_project.yml." + in logs + ) + assert deprecations.active_deprecations == {"project-flags-moved"} + + +class TestProjectFlagsMovedDeprecationQuiet(TestProjectFlagsMovedDeprecation): + def test_profile_config_deprecation(self, project): + deprecations.reset_deprecations() + assert deprecations.active_deprecations == set() + + _, logs = run_dbt_and_capture(["--quiet", "parse"]) + + assert ( + "User config should be moved from the 'config' key in profiles.yml to the 'flags' key in dbt_project.yml." + not in logs + ) + assert deprecations.active_deprecations == {"project-flags-moved"} + + +class TestProjectFlagsMovedDeprecationWarnErrorOptions(TestProjectFlagsMovedDeprecation): + def test_profile_config_deprecation(self, project): + deprecations.reset_deprecations() + with pytest.raises(EventCompilationError): + run_dbt(["--warn-error-options", "{'include': 'all'}", "parse"]) + + with pytest.raises(EventCompilationError): + run_dbt( + ["--warn-error-options", "{'include': ['ProjectFlagsMovedDeprecation']}", "parse"] + ) + + _, logs = run_dbt_and_capture( + ["--warn-error-options", "{'silence': ['ProjectFlagsMovedDeprecation']}", "parse"] + ) + assert ( + "User config should be moved from the 'config' key in profiles.yml to the 'flags' key in dbt_project.yml." + not in logs + ) diff --git a/tests/unit/test_deprecations.py b/tests/unit/test_deprecations.py new file mode 100644 index 00000000000..3b773cce070 --- /dev/null +++ b/tests/unit/test_deprecations.py @@ -0,0 +1,36 @@ +import pytest + +import dbt.deprecations as deprecations + + +@pytest.fixture(scope="function") +def active_deprecations(): + assert not deprecations.active_deprecations + + yield deprecations.active_deprecations + + deprecations.reset_deprecations() + + +@pytest.fixture(scope="function") +def buffered_deprecations(): + assert not deprecations.buffered_deprecations + + yield deprecations.buffered_deprecations + + deprecations.buffered_deprecations.clear() + + +def test_buffer_deprecation(active_deprecations, buffered_deprecations): + deprecations.buffer("project-flags-moved") + + assert active_deprecations == set() + assert len(buffered_deprecations) == 1 + + +def test_fire_buffered_deprecations(active_deprecations, buffered_deprecations): + deprecations.buffer("project-flags-moved") + deprecations.fire_buffered_deprecations() + + assert active_deprecations == set(["project-flags-moved"]) + assert len(buffered_deprecations) == 0