設定を読み込む用クラス(ConfigReader)#
haniwersの設定ファイルを読み込むための統一的なインターフェースを提供する設定ファイルは、特定のディレクトリから自動検出、もしくはCLIオプションで明示的に指定する
設定ファイルの優先度#
CLIオプション
--config PATH
カレントディレクトリ
./hnw.toml./config.toml./config/*toml
XDG準拠(
platformdirsパッケージを利用)./.config/haniwers/hnw.toml./.config/haniwers/config.toml./.config/haniwers/*.toml
この中で最初に見つかったパスを採用する
クラス設計#
# config_loader.py
from pathlib import Path
from typing import Optional, Union
from haniwers.config_model import HaniwersConfig, load_config
from platformdirs import user_config_dir
class ConfigLoader:
def __init__(self, config_path: Optional[Path] = None):
self._config_path = config_path or self._get_default_config_path()
self._config = self._load_file(self.config_path)
@property
def config(self) -> HaniwersConfig:
"""Get configuration
@propetryに設定することで、configに直接書き込むことを防ぐ効果がある
"""
return self._config
def _load_file(self, path:Path) -> HaniwersConfig:
if not path.exists():
raise FileNotFoundError(f"Config file not found: {path}")
return load_config(path)
@classmethod
def get_default_config_path(cls) -> Path:
"""Find the default config file in the standard locations.
Returns:
Path: Path to the config file.
Raises:
FileNotFoundError: If no config file could be found.
"""
config_dir = Path(user_config_dir("haniwers"))
# -> Linux: ~/.config/haniwers/
# ->
candidates = [
Path("./haniwers.toml"),
Path("./config.toml"),
*sorted(Path("./config/").glob("*.toml")),
config_dir / "hnw.toml",
config_dir / "config.toml",
*sorted(config_dir.glob("*.toml")),
]
# Add platform-specific paths
if sys.platform == "win32":
candidates.append(
Path(os.environ.get("LOCALAPPDATA", "")) / "haniwers" / "config.toml"
)
for path in candidates:
if path.exists():
return path
# Build a more helpful error message
searched_paths = "\n - ".join([str(p) for p in candidates])
raise FileNotFoundError(
f"No config file found in expected locations:\n - {searched_paths}\n"
"Run 'haniwers init' to create one in the current directory."
)
def get_default_config_path() -> Path:
return ConfigLoader.get_default_config_path()
if __name__ == "__main__":
"""Self Test.
uv run src/haniwers/config_loader.py
"""
cfg = ConfigLoader()
cfg.config # -> HaniwersConfig
設定ファイルのパスを指定して、設定を読み込めるようにする
設定ファイルのパスしていがない場合は、デフォルトのパスを検索する
デフォルトパスの検索機能は、外部からも呼び出すことができるようにした
ConfigLoader.get_default_config_pathをクラスメソッドにしたconfig_loader.get_default_config_pathで外部から利用できるようにしたplatformdirsパッケージを利用してクロスプラットフォーム対応する
読み込んだ設定情報は、内部変数
_configとして保持する@propetry configで呼び出せるようにする。再代入による書き込みを禁止する
今後の設計方針#
CLI統合との連携
--configによるパス指定に対応し、CLI全体に設定を適用typer.Option()などと併用して動的な設定変更も視野に
ConfigPatcher の導入(次フェーズ)
CLIオプションでの閾値 (threshold) などの上書き処理を
ConfigLoaderとは分離ConfigPatcher.apply(cfg, overrides)のような形式で整合的な変更を可能に