Skip to content

Commit 437a603

Browse files
Remove dbt-core dependency (#81)
Get rid of the dependency `dbt-core`. In order to keep the number of dependencies as low as possible, to keep python environments small, get rid of the dbt-core dependency and add it to the dev dependencies. This is possible because `dbt-score` only needs a `manifest.json` in order to run, although `dbt-score` is able to run `dbt parse` by itself. --------- Co-authored-by: Matthieu Caneill <[email protected]>
1 parent f8acb46 commit 437a603

File tree

7 files changed

+876
-647
lines changed

7 files changed

+876
-647
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ and this project adheres to
88

99
## [Unreleased]
1010

11+
- **Breaking**: The rule `public_model_has_example_sql` has been renamed
12+
`has_example_sql` and applies by default to all models.
13+
- **Breaking**: Remove `dbt-core` from dependencies. Since it is not mandatory
14+
for `dbt-score` to execute `dbt`, remove the dependency.
15+
- **Breaking**: Stop using `MultiOption` selection type.
16+
1117
## [0.6.0] - 2024-08-23
1218

1319
- **Breaking**: Improve error handling in CLI. Log messages are written in

docs/get_started.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ Installation of `dbt-score` is simple:
1111
pip install dbt-score
1212
```
1313

14-
If a virtual environment is used to run dbt, make sure to install `dbt-score` in
15-
the same environment.
14+
In order to run `dbt-score` with all its features, be sure to install
15+
`dbt-score` in the same environment as `dbt-core`.
1616

1717
## Usage
1818

pdm.lock

Lines changed: 811 additions & 635 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ classifiers = [
2323
]
2424

2525
dependencies = [
26-
"dbt-core>=1.5",
2726
"click>=7.1.1, <9.0.0",
2827
"tomli>=1.1.0; python_version<'3.11'",
2928
]
@@ -37,6 +36,7 @@ dbt-score = "dbt_score.__main__:main"
3736
[tool.pdm]
3837
[tool.pdm.dev-dependencies]
3938
dev = [
39+
"dbt-core>=1.5",
4040
"tox-pdm~=0.7.2",
4141
"tox~=4.13",
4242
]

src/dbt_score/cli.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77

88
import click
99
from click.core import ParameterSource
10-
from dbt.cli.options import MultiOption
1110

1211
from dbt_score.config import Config
13-
from dbt_score.dbt_utils import DbtParseException, dbt_parse, get_default_manifest_path
12+
from dbt_score.dbt_utils import (
13+
DbtParseException,
14+
dbt_parse,
15+
get_default_manifest_path,
16+
)
1417
from dbt_score.lint import lint_dbt_project
1518
from dbt_score.rule_catalog import display_catalog
1619

@@ -48,8 +51,6 @@ def cli() -> None:
4851
"--select",
4952
"-s",
5053
help="Specify the nodes to include.",
51-
cls=MultiOption,
52-
type=tuple,
5354
multiple=True,
5455
)
5556
@click.option(

src/dbt_score/dbt_utils.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,20 @@
22

33
import contextlib
44
import os
5+
from functools import wraps
56
from pathlib import Path
6-
from typing import Iterable, Iterator, cast
7+
from typing import Any, Callable, Iterable, Iterator, cast
78

8-
from dbt.cli.main import dbtRunner, dbtRunnerResult
9+
# Conditionally import dbt objects.
10+
try:
11+
DBT_INSTALLED = True
12+
from dbt.cli.main import dbtRunner, dbtRunnerResult # type: ignore
13+
except ImportError:
14+
DBT_INSTALLED = False
15+
16+
17+
class DbtNotInstalledException(Exception):
18+
"""Raised when trying to run dbt when dbt is not installed."""
919

1020

1121
class DbtParseException(Exception):
@@ -16,13 +26,29 @@ class DbtLsException(Exception):
1626
"""Raised when dbt ls fails."""
1727

1828

29+
def dbt_required(func: Callable[..., Any]) -> Callable[..., Any]:
30+
"""Decorator for methods that require dbt to be installed."""
31+
32+
@wraps(func)
33+
def wrapper(*args: Any, **kwargs: Any) -> Any:
34+
if not DBT_INSTALLED:
35+
raise DbtNotInstalledException(
36+
"This option requires dbt to be installed in the same Python"
37+
"environment as dbt-score."
38+
)
39+
return func(*args, **kwargs)
40+
41+
return wrapper
42+
43+
1944
@contextlib.contextmanager
2045
def _disable_dbt_stdout() -> Iterator[None]:
2146
with contextlib.redirect_stdout(None):
2247
yield
2348

2449

25-
def dbt_parse() -> dbtRunnerResult:
50+
@dbt_required
51+
def dbt_parse() -> "dbtRunnerResult":
2652
"""Parse a dbt project.
2753
2854
Returns:
@@ -32,22 +58,23 @@ def dbt_parse() -> dbtRunnerResult:
3258
DbtParseException: dbt parse failed.
3359
"""
3460
with _disable_dbt_stdout():
35-
result: dbtRunnerResult = dbtRunner().invoke(["parse"])
61+
result: "dbtRunnerResult" = dbtRunner().invoke(["parse"])
3662

3763
if not result.success:
3864
raise DbtParseException("dbt parse failed.") from result.exception
3965

4066
return result
4167

4268

69+
@dbt_required
4370
def dbt_ls(select: Iterable[str] | None) -> Iterable[str]:
4471
"""Run dbt ls."""
4572
cmd = ["ls", "--resource-type", "model", "--output", "name"]
4673
if select:
4774
cmd += ["--select", *select]
4875

4976
with _disable_dbt_stdout():
50-
result: dbtRunnerResult = dbtRunner().invoke(cmd)
77+
result: "dbtRunnerResult" = dbtRunner().invoke(cmd)
5178

5279
if not result.success:
5380
raise DbtLsException("dbt ls failed.") from result.exception

tests/test_cli.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,25 @@ def test_lint_dbt_parse_exception(caplog):
5959
assert "dbt failed to parse project" in caplog.text
6060

6161

62+
def test_lint_dbt_not_installed(caplog, manifest_path):
63+
"""Test lint with a valid manifest when dbt is not installed."""
64+
runner = CliRunner()
65+
66+
with patch("dbt_score.dbt_utils.DBT_INSTALLED", new=False):
67+
result = runner.invoke(lint, ["-m", manifest_path], catch_exceptions=False)
68+
assert result.exit_code == 0
69+
70+
71+
def test_lint_dbt_not_installed_v(caplog):
72+
"""Test lint with dbt parse when dbt is not installed."""
73+
runner = CliRunner()
74+
75+
with patch("dbt_score.dbt_utils.DBT_INSTALLED", new=False):
76+
result = runner.invoke(lint, ["-p"])
77+
assert result.exit_code == 2
78+
assert "DbtNotInstalledException" in caplog.text
79+
80+
6281
def test_lint_other_exception(manifest_path, caplog):
6382
"""Test lint with an unexpected error."""
6483
runner = CliRunner()

0 commit comments

Comments
 (0)