mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-11 02:33:51 +08:00
fix(config): log configuration loading errors instead of silently ignoring
Replaces bare exception handler in load_settings() with logging.warning() to help users debug configuration file issues (syntax errors, permissions). Maintains backward compatibility - errors do not break initialization. Solution-ID: SOL-1735385400001 Issue-ID: ISS-1766921318981-1 Task-ID: T1
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
@@ -18,6 +19,8 @@ WORKSPACE_DIR_NAME = ".codexlens"
|
|||||||
# Settings file name
|
# Settings file name
|
||||||
SETTINGS_FILE_NAME = "settings.json"
|
SETTINGS_FILE_NAME = "settings.json"
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _default_global_dir() -> Path:
|
def _default_global_dir() -> Path:
|
||||||
"""Get global CodexLens data directory."""
|
"""Get global CodexLens data directory."""
|
||||||
@@ -200,7 +203,15 @@ class Config:
|
|||||||
# Load embedding settings
|
# Load embedding settings
|
||||||
embedding = settings.get("embedding", {})
|
embedding = settings.get("embedding", {})
|
||||||
if "backend" in embedding:
|
if "backend" in embedding:
|
||||||
self.embedding_backend = embedding["backend"]
|
backend = embedding["backend"]
|
||||||
|
if backend in {"fastembed", "litellm"}:
|
||||||
|
self.embedding_backend = backend
|
||||||
|
else:
|
||||||
|
log.warning(
|
||||||
|
"Invalid embedding backend in %s: %r (expected 'fastembed' or 'litellm')",
|
||||||
|
self.settings_path,
|
||||||
|
backend,
|
||||||
|
)
|
||||||
if "model" in embedding:
|
if "model" in embedding:
|
||||||
self.embedding_model = embedding["model"]
|
self.embedding_model = embedding["model"]
|
||||||
if "use_gpu" in embedding:
|
if "use_gpu" in embedding:
|
||||||
@@ -224,8 +235,13 @@ class Config:
|
|||||||
self.llm_timeout_ms = llm["timeout_ms"]
|
self.llm_timeout_ms = llm["timeout_ms"]
|
||||||
if "batch_size" in llm:
|
if "batch_size" in llm:
|
||||||
self.llm_batch_size = llm["batch_size"]
|
self.llm_batch_size = llm["batch_size"]
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
pass # Silently ignore errors
|
log.warning(
|
||||||
|
"Failed to load settings from %s (%s): %s",
|
||||||
|
self.settings_path,
|
||||||
|
type(exc).__name__,
|
||||||
|
exc,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls) -> "Config":
|
def load(cls) -> "Config":
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
"""Tests for CodexLens configuration system."""
|
"""Tests for CodexLens configuration system."""
|
||||||
|
|
||||||
|
import builtins
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -224,6 +227,99 @@ class TestConfig:
|
|||||||
del os.environ["CODEXLENS_DATA_DIR"]
|
del os.environ["CODEXLENS_DATA_DIR"]
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfigLoadSettings:
|
||||||
|
"""Tests for Config.load_settings behavior and logging."""
|
||||||
|
|
||||||
|
def test_load_settings_logs_warning_on_malformed_json(self, caplog):
|
||||||
|
"""Malformed JSON in settings file should trigger warning log."""
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
config = Config(data_dir=Path(tmpdir))
|
||||||
|
config.settings_path.write_text("{", encoding="utf-8")
|
||||||
|
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
config.load_settings()
|
||||||
|
|
||||||
|
records = [r for r in caplog.records if r.name == "codexlens.config"]
|
||||||
|
assert any("Failed to load settings from" in r.message for r in records)
|
||||||
|
assert any("JSONDecodeError" in r.message for r in records)
|
||||||
|
assert any(str(config.settings_path) in r.message for r in records)
|
||||||
|
|
||||||
|
def test_load_settings_logs_warning_on_permission_error(self, monkeypatch, caplog):
|
||||||
|
"""Permission errors opening settings file should trigger warning log."""
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
config = Config(data_dir=Path(tmpdir))
|
||||||
|
config.settings_path.write_text("{}", encoding="utf-8")
|
||||||
|
|
||||||
|
real_open = builtins.open
|
||||||
|
|
||||||
|
def guarded_open(path, mode="r", *args, **kwargs):
|
||||||
|
if Path(path) == config.settings_path and "r" in mode:
|
||||||
|
raise PermissionError("Permission denied")
|
||||||
|
return real_open(path, mode, *args, **kwargs)
|
||||||
|
|
||||||
|
monkeypatch.setattr(builtins, "open", guarded_open)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
config.load_settings()
|
||||||
|
|
||||||
|
records = [r for r in caplog.records if r.name == "codexlens.config"]
|
||||||
|
assert any("Failed to load settings from" in r.message for r in records)
|
||||||
|
assert any("PermissionError" in r.message for r in records)
|
||||||
|
|
||||||
|
def test_load_settings_loads_valid_settings_without_warning(self, caplog):
|
||||||
|
"""Valid settings should load without warning logs."""
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
config = Config(data_dir=Path(tmpdir))
|
||||||
|
config.settings_path.write_text(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"embedding": {
|
||||||
|
"backend": "fastembed",
|
||||||
|
"model": "multilingual",
|
||||||
|
"use_gpu": False,
|
||||||
|
},
|
||||||
|
"llm": {
|
||||||
|
"enabled": True,
|
||||||
|
"tool": "gemini",
|
||||||
|
"timeout_ms": 1234,
|
||||||
|
"batch_size": 7,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
config.load_settings()
|
||||||
|
|
||||||
|
records = [r for r in caplog.records if r.name == "codexlens.config"]
|
||||||
|
assert not records
|
||||||
|
assert config.embedding_backend == "fastembed"
|
||||||
|
assert config.embedding_model == "multilingual"
|
||||||
|
assert config.embedding_use_gpu is False
|
||||||
|
assert config.llm_enabled is True
|
||||||
|
assert config.llm_tool == "gemini"
|
||||||
|
assert config.llm_timeout_ms == 1234
|
||||||
|
assert config.llm_batch_size == 7
|
||||||
|
|
||||||
|
def test_load_settings_logs_warning_on_invalid_embedding_backend(self, caplog):
|
||||||
|
"""Invalid embedding backend should trigger warning log and keep default."""
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
config = Config(data_dir=Path(tmpdir))
|
||||||
|
default_backend = config.embedding_backend
|
||||||
|
config.settings_path.write_text(
|
||||||
|
json.dumps({"embedding": {"backend": "invalid-backend"}}),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
config.load_settings()
|
||||||
|
|
||||||
|
records = [r for r in caplog.records if r.name == "codexlens.config"]
|
||||||
|
assert any("Invalid embedding backend in" in r.message for r in records)
|
||||||
|
assert config.embedding_backend == default_backend
|
||||||
|
|
||||||
|
|
||||||
class TestWorkspaceConfig:
|
class TestWorkspaceConfig:
|
||||||
"""Tests for WorkspaceConfig class."""
|
"""Tests for WorkspaceConfig class."""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user