Skip to content

Commit 54d21eb

Browse files
Fix(dbt_cli): Add global error handling group for dbt subcommands (#5239)
1 parent db58405 commit 54d21eb

File tree

3 files changed

+74
-2
lines changed

3 files changed

+74
-2
lines changed

sqlmesh_dbt/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import sys
33
import click
44
from sqlmesh_dbt.operations import DbtOperations, create
5-
from sqlmesh_dbt.error import cli_global_error_handler
5+
from sqlmesh_dbt.error import cli_global_error_handler, ErrorHandlingGroup
66
from pathlib import Path
77
from sqlmesh_dbt.options import YamlParamType
88
import functools
@@ -43,7 +43,7 @@ def _cleanup() -> None:
4343
exclude_option = click.option("--exclude", multiple=True, help="Specify the nodes to exclude.")
4444

4545

46-
@click.group(invoke_without_command=True)
46+
@click.group(cls=ErrorHandlingGroup, invoke_without_command=True)
4747
@click.option("--profile", help="Which existing profile to load. Overrides output.profile")
4848
@click.option("-t", "--target", help="Which target to load for the given profile")
4949
@click.option(

sqlmesh_dbt/error.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,10 @@ def wrapper(*args: t.List[t.Any], **kwargs: t.Any) -> t.Any:
2727
raise
2828

2929
return wrapper
30+
31+
32+
class ErrorHandlingGroup(click.Group):
33+
def add_command(self, cmd: click.Command, name: t.Optional[str] = None) -> None:
34+
if cmd.callback:
35+
cmd.callback = cli_global_error_handler(cmd.callback)
36+
super().add_command(cmd, name=name)

tests/dbt/cli/test_global_flags.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import typing as t
22
from pathlib import Path
33
import pytest
4+
from pytest_mock import MockerFixture
45
from click.testing import Result
6+
from sqlmesh.utils.errors import SQLMeshError
7+
from sqlglot.errors import SqlglotError
58

69
pytestmark = pytest.mark.slow
710

@@ -28,3 +31,65 @@ def test_profile_and_target(jaffle_shop_duckdb: Path, invoke_cli: t.Callable[...
2831
result = invoke_cli(["--profile", "jaffle_shop", "--target", "dev"])
2932
assert result.exit_code == 0
3033
assert "No command specified" in result.output
34+
35+
36+
def test_run_error_handler(
37+
jaffle_shop_duckdb: Path, invoke_cli: t.Callable[..., Result], mocker: MockerFixture
38+
) -> None:
39+
mock_run = mocker.patch("sqlmesh_dbt.operations.DbtOperations.run")
40+
mock_run.side_effect = SQLMeshError("Test error message")
41+
42+
result = invoke_cli(["run"])
43+
assert result.exit_code == 1
44+
assert "Error: Test error message" in result.output
45+
assert "Traceback" not in result.output
46+
47+
# test SqlglotError in run command
48+
mock_run = mocker.patch("sqlmesh_dbt.operations.DbtOperations.run")
49+
mock_run.side_effect = SqlglotError("Invalid SQL syntax")
50+
51+
result = invoke_cli(["run"])
52+
53+
assert result.exit_code == 1
54+
assert "Error: Invalid SQL syntax" in result.output
55+
assert "Traceback" not in result.output
56+
57+
# test ValueError in run command
58+
mock_run = mocker.patch("sqlmesh_dbt.operations.DbtOperations.run")
59+
mock_run.side_effect = ValueError("Invalid configuration value")
60+
61+
result = invoke_cli(["run"])
62+
63+
assert result.exit_code == 1
64+
assert "Error: Invalid configuration value" in result.output
65+
assert "Traceback" not in result.output
66+
67+
# test SQLMeshError in list command
68+
mock_list = mocker.patch("sqlmesh_dbt.operations.DbtOperations.list_")
69+
mock_list.side_effect = SQLMeshError("List command error")
70+
71+
result = invoke_cli(["list"])
72+
73+
assert result.exit_code == 1
74+
assert "Error: List command error" in result.output
75+
assert "Traceback" not in result.output
76+
77+
# test SQLMeshError in main command without subcommand
78+
mock_create = mocker.patch("sqlmesh_dbt.cli.create")
79+
mock_create.side_effect = SQLMeshError("Failed to load project")
80+
result = invoke_cli(["--profile", "jaffle_shop"])
81+
82+
assert result.exit_code == 1
83+
assert "Error: Failed to load project" in result.output
84+
assert "Traceback" not in result.output
85+
mocker.stopall()
86+
87+
# test error with select option
88+
mock_run_select = mocker.patch("sqlmesh_dbt.operations.DbtOperations.run")
89+
mock_run_select.side_effect = SQLMeshError("Error with selector")
90+
91+
result = invoke_cli(["run", "--select", "model1"])
92+
93+
assert result.exit_code == 1
94+
assert "Error: Error with selector" in result.output
95+
assert "Traceback" not in result.output

0 commit comments

Comments
 (0)