from __future__ import annotations import importlib.util import os import sys from pathlib import Path from types import ModuleType from typing import Any ENV_NAME = "PAPER_DB_CONFIG" COMMON_ROOT = Path(__file__).resolve().parent if str(COMMON_ROOT) not in sys.path: sys.path.insert(0, str(COMMON_ROOT)) def resolve_config_path(value: str | Path | None = None) -> Path: raw = value or os.environ.get(ENV_NAME) path = Path(raw).expanduser() if raw else Path.cwd() / "config.py" path = path.resolve() if not path.is_file(): raise FileNotFoundError(f"config.pyがありません: {path}") return path def load_module(path: Path) -> ModuleType: spec = importlib.util.spec_from_file_location( "paper_db_project_config", path ) if spec is None or spec.loader is None: raise ImportError(f"config.pyを読み込めません: {path}") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def load_config(value: str | Path | None = None) -> tuple[Path, Any]: path = resolve_config_path(value) module = load_module(path) if not hasattr(module, "CONFIG"): raise AttributeError(f"CONFIGがありません: {path}") config = module.CONFIG if Path(config.path.project_root).resolve() != path.parent: raise ValueError( "project_rootはconfig.pyのディレクトリにしてください" ) return path, config