Skip to content

Commit 1d61667

Browse files
committed
fix: remove import-time logging side effects (closes z3z1ma#136)
Previously, importing dbt-core-interface would immediately configure logging to DEBUG level and add a RichHandler, polluting user's console when dbt-core scanned for plugins. This was caused by module-level execution of logger.setLevel() and logger.addHandler(). Changes: - Moved logging configuration from module-level to DbtProject.setup_logging() classmethod that must be called explicitly - Moved disable_tracking() and _set_invocation_context() calls into DbtProject.__init__() instead of executing at import time - Added tests to verify logging is not configured at import and that setup_logging() works correctly and idempotently - Bumped version to 1.1.6 The module can now be imported without side effects, making it safe for dbt-core's plugin scanner which imports any package starting with dbt- or dbt_.
1 parent 3485fb4 commit 1d61667

File tree

4 files changed

+89
-13
lines changed

4 files changed

+89
-13
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "dbt-core-interface"
3-
version = "1.1.5"
3+
version = "1.1.6"
44
dynamic = []
55
description = "Dbt Core Interface"
66
authors = [

src/dbt_core_interface/project.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,22 +66,25 @@ def _patched_adapter_accessor(config: t.Any) -> t.Any:
6666
from sqlfluff.core.linter.linted_dir import LintingRecord
6767

6868

69-
disable_tracking()
70-
71-
7269
def _set_invocation_context() -> None:
7370
set_invocation_context(get_env())
7471

7572

76-
_set_invocation_context()
73+
def _setup_logging(level: int = logging.DEBUG) -> logging.Logger:
74+
logger = logging.getLogger(__name__)
75+
logger.setLevel(level)
7776

78-
logger = logging.getLogger(__name__)
79-
logger.setLevel(logging.DEBUG)
80-
logger.addHandler(rich.logging.RichHandler())
77+
if not any(isinstance(h, rich.logging.RichHandler) for h in logger.handlers):
78+
logger.addHandler(rich.logging.RichHandler())
79+
80+
add_logger_to_manager(
81+
LoggerConfig(name=__name__, logger=logger),
82+
)
83+
84+
return logger
8185

82-
add_logger_to_manager(
83-
LoggerConfig(name=__name__, logger=logger),
84-
)
86+
87+
logger = logging.getLogger(__name__)
8588

8689
__all__ = ["DbtProject", "DbtConfiguration"]
8790

@@ -253,6 +256,9 @@ def __init__(
253256
if hasattr(self, "_args"):
254257
return
255258

259+
disable_tracking()
260+
_set_invocation_context()
261+
256262
project_dir = project_dir or _get_profiles_dir()
257263
profiles_dir = profiles_dir or _get_profiles_dir(project_dir)
258264

@@ -321,6 +327,20 @@ def __repr__(self) -> str: # pyright: ignore[reportImplicitOverride]
321327

322328
set_invocation_context = staticmethod(_set_invocation_context)
323329

330+
@classmethod
331+
def setup_logging(cls, level: int = logging.DEBUG) -> logging.Logger:
332+
logger = logging.getLogger(__name__)
333+
logger.setLevel(level)
334+
335+
if not any(isinstance(h, rich.logging.RichHandler) for h in logger.handlers):
336+
logger.addHandler(rich.logging.RichHandler())
337+
338+
add_logger_to_manager(
339+
LoggerConfig(name=__name__, logger=logger),
340+
)
341+
342+
return logger
343+
324344
@classmethod
325345
def from_config(cls, config: DbtConfiguration) -> DbtProject:
326346
"""Create project from configuration."""

tests/test_main.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Test cases for the project module."""
22

3+
import logging
4+
35

46
def test_import_succeeds() -> None:
57
"""A simple test to ensure that the project module can be imported.
@@ -11,3 +13,57 @@ def test_import_succeeds() -> None:
1113
from dbt_core_interface import project
1214

1315
_ = project
16+
17+
18+
def test_logging_not_configured_at_import() -> None:
19+
"""Verify logging is NOT configured at import time.
20+
21+
This is critical for issue #136 - importing dbt-core-interface
22+
should not cause side effects like setting DEBUG log level or
23+
adding RichHandler to dbt's logger.
24+
"""
25+
from dbt_core_interface import project
26+
27+
logger = logging.getLogger("dbt_core_interface.project")
28+
29+
assert logger.level == 0 or logger.level == logging.NOTSET, (
30+
f"Logger should NOT be configured at import, but level is {logger.level}"
31+
)
32+
assert len(logger.handlers) == 0, (
33+
f"Logger should have NO handlers at import, but has {len(logger.handlers)}"
34+
)
35+
36+
37+
def test_setup_logging_works() -> None:
38+
"""Verify DbtProject.setup_logging() correctly configures logging."""
39+
from dbt_core_interface import DbtProject
40+
41+
DbtProject.setup_logging(level=logging.DEBUG)
42+
43+
logger = logging.getLogger("dbt_core_interface.project")
44+
45+
assert logger.level == logging.DEBUG, (
46+
f"Logger level should be DEBUG after setup_logging, but is {logger.level}"
47+
)
48+
assert len(logger.handlers) > 0, "Logger should have handlers after setup_logging"
49+
assert any(h.__class__.__name__ == "RichHandler" for h in logger.handlers), (
50+
"Logger should have RichHandler after setup_logging"
51+
)
52+
53+
54+
def test_setup_logging_idempotent() -> None:
55+
"""Verify DbtProject.setup_logging() can be called multiple times safely."""
56+
from dbt_core_interface import DbtProject
57+
58+
DbtProject.setup_logging(level=logging.DEBUG)
59+
handler_count_after_first_call = len(logging.getLogger("dbt_core_interface.project").handlers)
60+
61+
DbtProject.setup_logging(level=logging.INFO)
62+
63+
logger = logging.getLogger("dbt_core_interface.project")
64+
65+
handler_count_after_second_call = len(logger.handlers)
66+
67+
assert handler_count_after_second_call == handler_count_after_first_call, (
68+
"setup_logging should be idempotent - should not add duplicate handlers"
69+
)

uv.lock

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

0 commit comments

Comments
 (0)