diff --git a/src/specify_cli/presets/_catalog.py b/src/specify_cli/presets/_catalog.py index a4768cc22d..5d359ab685 100644 --- a/src/specify_cli/presets/_catalog.py +++ b/src/specify_cli/presets/_catalog.py @@ -2,6 +2,7 @@ import hashlib import json +import logging import os import tempfile from dataclasses import dataclass @@ -20,6 +21,8 @@ ) from ._manifest import PresetError, PresetValidationError +logger = logging.getLogger(__name__) + @dataclass class PresetCatalogEntry: @@ -302,18 +305,15 @@ def get_active_catalogs(self) -> List[PresetCatalogEntry]: Raises: PresetValidationError: If a catalog URL is invalid """ - import sys - # 1. SPECKIT_PRESET_CATALOG_URL env var replaces all defaults if env_value := os.environ.get("SPECKIT_PRESET_CATALOG_URL"): catalog_url = env_value.strip() self._validate_catalog_url(catalog_url) if catalog_url != self.DEFAULT_CATALOG_URL: if not getattr(self, "_non_default_catalog_warning_shown", False): - print( - "Warning: Using non-default preset catalog. " - "Only use catalogs from sources you trust.", - file=sys.stderr, + logger.warning( + "Using non-default preset catalog. " + "Only use catalogs from sources you trust." ) self._non_default_catalog_warning_shown = True return [PresetCatalogEntry(url=catalog_url, name="custom", priority=1, install_allowed=True, description="Custom catalog via SPECKIT_PRESET_CATALOG_URL")] diff --git a/tests/specify_cli/presets/test_catalog.py b/tests/specify_cli/presets/test_catalog.py index 2ffbddfed9..343d4a5901 100644 --- a/tests/specify_cli/presets/test_catalog.py +++ b/tests/specify_cli/presets/test_catalog.py @@ -2,6 +2,7 @@ import io import json +import logging import tarfile import zipfile from contextlib import contextmanager @@ -251,6 +252,42 @@ def test_env_var_catalog_url(self, project_dir, monkeypatch): catalog = PresetCatalog(project_dir) assert catalog.get_catalog_url() == "https://custom.example.com/catalog.json" + def test_non_default_catalog_url_warns_through_logger_once( + self, project_dir, monkeypatch, caplog, capsys + ): + """The override warning reaches the logging framework, not stderr. + + Regresses the print(..., file=sys.stderr) implementation: that wrote + the message straight to stderr and logging captured nothing. + """ + monkeypatch.setenv( + "SPECKIT_PRESET_CATALOG_URL", "https://custom.example.com/catalog.json" + ) + catalog = PresetCatalog(project_dir) + caplog.set_level(logging.WARNING, logger="specify_cli.presets._catalog") + + assert catalog.get_catalog_url() == "https://custom.example.com/catalog.json" + + message = "Using non-default preset catalog" + assert message in caplog.text + assert message not in capsys.readouterr().err + + # Once per catalog instance: a second lookup must not repeat it. + catalog.get_catalog_url() + assert caplog.text.count(message) == 1 + + def test_default_catalog_url_logs_no_warning( + self, project_dir, monkeypatch, caplog + ): + """The default path must not emit the override warning.""" + monkeypatch.delenv("SPECKIT_PRESET_CATALOG_URL", raising=False) + catalog = PresetCatalog(project_dir) + caplog.set_level(logging.WARNING, logger="specify_cli.presets._catalog") + + catalog.get_catalog_url() + + assert "Using non-default preset catalog" not in caplog.text + # --- _make_request / GitHub auth --- def test_make_request_no_token_no_auth_header(self, project_dir, monkeypatch):