diff --git a/packages/data-designer-config/src/data_designer/logging.py b/packages/data-designer-config/src/data_designer/logging.py index 3a280eae7..fe5850253 100644 --- a/packages/data-designer-config/src/data_designer/logging.py +++ b/packages/data-designer-config/src/data_designer/logging.py @@ -12,6 +12,8 @@ from pythonjsonlogger.json import JsonFormatter +_logging_configured = False + @dataclass class LoggerConfig: @@ -115,6 +117,7 @@ def working() -> str: def configure_logging(config: LoggingConfig | None = None) -> None: + global _logging_configured config = config or LoggingConfig.default() root_logger = logging.getLogger() @@ -137,6 +140,12 @@ def configure_logging(config: LoggingConfig | None = None) -> None: for name in config.to_silence: quiet_noisy_logger(name) + _logging_configured = True + + +def is_logging_configured() -> bool: + return _logging_configured + def quiet_noisy_logger(name: str) -> None: logger = logging.getLogger(name) diff --git a/packages/data-designer-config/tests/test_logging.py b/packages/data-designer-config/tests/test_logging.py index e52c0f1e1..db8d10c1c 100644 --- a/packages/data-designer-config/tests/test_logging.py +++ b/packages/data-designer-config/tests/test_logging.py @@ -8,12 +8,14 @@ import pytest +import data_designer.logging as logging_mod from data_designer.logging import ( LoggerConfig, LoggingConfig, OutputConfig, RandomEmoji, configure_logging, + is_logging_configured, quiet_noisy_logger, ) @@ -105,6 +107,16 @@ def test_configure_logging_basic(stub_default_logging_config): assert ndd_logger.level == logging.INFO +def test_configure_logging_marks_logging_configured(stub_default_logging_config, monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(logging_mod, "_logging_configured", False) + + assert is_logging_configured() is False + + configure_logging(stub_default_logging_config) + + assert is_logging_configured() is True + + def test_configure_logging_with_file(): with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".log") as tmp_file: tmp_path = Path(tmp_file.name) diff --git a/packages/data-designer/src/data_designer/interface/data_designer.py b/packages/data-designer/src/data_designer/interface/data_designer.py index 270fff96c..0ce02b266 100644 --- a/packages/data-designer/src/data_designer/interface/data_designer.py +++ b/packages/data-designer/src/data_designer/interface/data_designer.py @@ -73,7 +73,7 @@ DataDesignerProfilingError, ) from data_designer.interface.results import DatasetCreationResults -from data_designer.logging import LOG_INDENT, RandomEmoji, configure_logging +from data_designer.logging import LOG_INDENT, RandomEmoji, configure_logging, is_logging_configured from data_designer.plugins.plugin import PluginType from data_designer.plugins.registry import PluginRegistry @@ -92,7 +92,8 @@ def _initialize_interface_runtime() -> None: global _interface_runtime_initialized if _interface_runtime_initialized: return - configure_logging() + if not is_logging_configured(): + configure_logging() resolve_seed_default_model_settings() _interface_runtime_initialized = True diff --git a/packages/data-designer/tests/interface/test_data_designer.py b/packages/data-designer/tests/interface/test_data_designer.py index 67fdf9dfb..e233cc8d5 100644 --- a/packages/data-designer/tests/interface/test_data_designer.py +++ b/packages/data-designer/tests/interface/test_data_designer.py @@ -17,6 +17,7 @@ import data_designer.interface.data_designer as dd_mod import data_designer.lazy_heavy_imports as lazy +import data_designer.logging as dd_logging from data_designer.config.column_configs import ( CustomColumnConfig, ExpressionColumnConfig, @@ -1519,6 +1520,7 @@ def test_validate_raises_error_when_seed_collides( def test_initialize_interface_runtime_runs_once(monkeypatch: pytest.MonkeyPatch) -> None: """_initialize_interface_runtime only runs initialization once.""" monkeypatch.setattr(dd_mod, "_interface_runtime_initialized", False) + monkeypatch.setattr(dd_logging, "_logging_configured", False) with ( patch("data_designer.interface.data_designer.configure_logging") as mock_logging, @@ -1530,6 +1532,40 @@ def test_initialize_interface_runtime_runs_once(monkeypatch: pytest.MonkeyPatch) mock_resolve.assert_called_once() +def test_initialize_interface_runtime_respects_preconfigured_logging(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(dd_mod, "_interface_runtime_initialized", False) + monkeypatch.setattr(dd_logging, "_logging_configured", True) + + with ( + patch("data_designer.interface.data_designer.configure_logging") as mock_logging, + patch("data_designer.interface.data_designer.resolve_seed_default_model_settings") as mock_resolve, + ): + dd_mod._initialize_interface_runtime() + + mock_logging.assert_not_called() + mock_resolve.assert_called_once() + + +def test_init_preserves_preconfigured_logging( + monkeypatch: pytest.MonkeyPatch, + stub_artifact_path: Path, + stub_model_providers: list[ModelProvider], +) -> None: + monkeypatch.setattr(dd_mod, "_interface_runtime_initialized", False) + monkeypatch.setattr(dd_logging, "_logging_configured", False) + data_designer_logger = logging.getLogger("data_designer") + previous_level = data_designer_logger.level + + try: + dd_logging.configure_logging(dd_logging.LoggingConfig.debug()) + + DataDesigner(artifact_path=stub_artifact_path, model_providers=stub_model_providers) + + assert data_designer_logger.level == logging.DEBUG + finally: + data_designer_logger.setLevel(previous_level) + + def test_create_dataset_e2e_with_directory_seed_source( stub_artifact_path: Path, stub_model_providers: list[ModelProvider],