# YAML Configuration Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Migrate FastAPI configuration from .env to YAML using pydantic-yaml **Architecture:** Create new `config/` module with YAML-based settings using `pydantic-yaml`, preserving type validation and improving readability through nested configuration structure. **Tech Stack:** pydantic-yaml, pyyaml, pytest --- ## Task 1: Add pydantic-yaml Dependency **Files:** - Modify: `requirements.txt` **Step 1: Add pydantic-yaml to requirements.txt** Add this line to `requirements.txt`: ```txt pydantic-yaml>=0.12.0 ``` **Step 2: Install the dependency** Run: `.venv/Scripts/pip install pydantic-yaml` Expected: Successfully installs pydantic-yaml and its dependencies **Step 3: Verify installation** Run: `.venv/Scripts/python -c "import pydantic_yaml; print(pydantic_yaml.__version__)"` Expected: Prints version number without errors **Step 4: Commit** ```bash git add requirements.txt git commit -m "deps: add pydantic-yaml for YAML configuration support" ``` --- ## Task 2: Create config Directory Structure **Files:** - Create: `config/__init__.py` - Create: `config/settings.py` - Create: `config/settings.yaml` **Step 1: Create config directory** Run: `mkdir config` **Step 2: Create config/__init__.py** ```python from config.settings import settings, Settings, load_settings __all__ = ["settings", "Settings", "load_settings"] ``` **Step 3: Create config/settings.py with configuration classes** ```python from pathlib import Path from sqlalchemy.engine import URL from pydantic import BaseModel from pydantic_yaml import YamlModel class SqlServerConfig(BaseModel): """SQL Server 连接配置""" host: str port: int = 1433 database: str username: str password: str driver: str = "{ODBC Driver 18 for SQL Server}" trust_server_certificate: str = "yes" class Settings(YamlModel): """应用配置""" database: SqlServerConfig @property def database_url(self) -> URL: """构建数据库连接 URL""" conf = self.database.sql_server return URL.create( "mssql+pyodbc", username=conf.username, password=conf.password, host=conf.host, port=conf.port, database=conf.database, query={ "driver": conf.driver.strip("{}"), "TrustServerCertificate": conf.trust_server_certificate, }, ) def load_settings(config_path: str = "config/settings.yaml") -> Settings: """加载 YAML 配置文件""" path = Path(config_path) if not path.exists(): raise FileNotFoundError( f"配置文件不存在: {config_path}\n" f"请确保文件存在于项目根目录或指定正确路径" ) return Settings.parse_yaml_file(path) # 全局配置单例 settings = load_settings() ``` **Step 4: Create config/settings.yaml with current configuration** ```yaml # 数据库配置 database: sql_server: host: 192.168.110.114 port: 1433 database: CompanyDB username: peng password: Cqbld123456. driver: "{ODBC Driver 18 for SQL Server}" trust_server_certificate: yes ``` **Step 5: Commit** ```bash git add config/ git commit -m "feat: add YAML configuration module with pydantic-yaml" ``` --- ## Task 3: Write Configuration Loading Tests **Files:** - Create: `tests/test_config.py` - Create: `tests/fixtures/test_config.yaml` **Step 1: Create tests/fixtures directory** Run: `mkdir tests/fixtures` **Step 2: Create tests/fixtures/test_config.yaml** ```yaml # 测试配置 database: sql_server: host: localhost port: 1433 database: TestDB username: test_user password: test_pass driver: "{ODBC Driver 18 for SQL Server}" trust_server_certificate: yes ``` **Step 3: Write the failing test for settings loading** Create `tests/test_config.py`: ```python import pytest from config.settings import load_settings def test_load_settings_success(): """测试成功加载配置文件""" settings = load_settings("tests/fixtures/test_config.yaml") assert settings.database.sql_server.port == 1433 assert settings.database.sql_server.host == "localhost" assert settings.database.sql_server.database == "TestDB" def test_load_settings_file_not_found(): """测试配置文件不存在时抛出异常""" with pytest.raises(FileNotFoundError): load_settings("nonexistent.yaml") def test_database_url_property(): """测试 database_url 属性生成""" settings = load_settings("tests/fixtures/test_config.yaml") url = settings.database_url assert "mssql+pyodbc" in str(url) assert "test_user" in str(url) assert "TestDB" in str(url) ``` **Step 4: Run tests to verify they pass** Run: `.venv/Scripts/pytest tests/test_config.py -v` Expected: All tests PASS **Step 5: Commit** ```bash git add tests/test_config.py tests/fixtures/test_config.yaml git commit -m "test: add configuration loading tests" ``` --- ## Task 4: Update database.py to Use New Config **Files:** - Modify: `app/core/database.py` **Step 1: Read current database.py to understand usage** Check the current import and usage of settings **Step 2: Update import statement** Change: ```python from app.core.config import settings ``` To: ```python from config.settings import settings ``` **Step 3: Verify the rest of the code works unchanged** The database_url property should work exactly as before **Step 4: Run existing tests to ensure no breakage** Run: `.venv/Scripts/pytest tests/ -v` Expected: All existing tests still PASS **Step 5: Commit** ```bash git add app/core/database.py git commit -m "refactor: update database.py to use new config module" ``` --- ## Task 5: Update API Module Imports **Files:** - Modify: `app/api/v1/box.py` - Modify: `app/api/v1/location.py` - Modify: `app/services/box_service.py` - Modify: `app/services/location_service.py` **Step 1: Check all files that import from app.core.config** Run: `grep -r "from app.core.config import" app/` **Step 2: Update each file's import** Change: ```python from app.core.config import settings ``` To: ```python from config.settings import settings ``` **Step 3: Run tests after each file change** Run: `.venv/Scripts/pytest tests/ -v` Expected: All tests PASS **Step 4: Commit all import changes** ```bash git add app/api/v1/box.py app/api/v1/location.py app/services/ git commit -m "refactor: update all imports to use new config module" ``` --- ## Task 6: Update Test Fixtures **Files:** - Modify: `tests/conftest.py` **Step 1: Read current conftest.py** Check for any references to app.core.config **Step 2: Update imports in conftest.py** Change any: ```python from app.core.config import settings ``` To: ```python from config.settings import settings ``` **Step 3: Run tests** Run: `.venv/Scripts/pytest tests/ -v` Expected: All tests PASS **Step 4: Commit** ```bash git add tests/conftest.py git commit -m "test: update conftest imports" ``` --- ## Task 7: Update Main Application Entry Point **Files:** - Modify: `app/main.py` **Step 1: Read current main.py** Check for any config-related initialization **Step 2: Add error handling for configuration loading** Add at the top of main.py or update existing initialization: ```python import sys from config.settings import load_settings try: settings = load_settings() except FileNotFoundError as e: print(f"配置文件错误: {e}") sys.exit(1) except Exception as e: print(f"加载配置失败: {e}") sys.exit(1) ``` **Step 3: Test application startup** Run: `.venv/Scripts/python -m app.main` Expected: Application starts without errors **Step 4: Commit** ```bash git add app/main.py git commit -m "feat: add config error handling on startup" ``` --- ## Task 8: Remove Old Configuration Files **Files:** - Delete: `app/core/config.py` - Delete: `.env` **Step 1: Verify no remaining imports of old config** Run: `grep -r "from app.core.config import" .` Expected: No results (or only in .git directory) **Step 2: Remove old config.py file** Run: `rm app/core/config.py` **Step 3: Remove .env file** Run: `rm .env` **Step 4: Add .env to .gitignore if not already present** Ensure `.env` is in `.gitignore` **Step 5: Run final test suite** Run: `.venv/Scripts/pytest tests/ -v` Expected: All tests PASS **Step 6: Run application to verify** Run: `.venv/Scripts/python -m app.main` Expected: Application starts successfully **Step 7: Commit cleanup** ```bash git add app/core/config.py .env .gitignore git commit -m "refactor: remove old .env and config.py files" ``` --- ## Task 9: Update .gitignore for YAML Config **Files:** - Modify: `.gitignore` (if it exists, otherwise create) **Step 1: Check if .gitignore exists** Run: `ls -la .gitignore` **Step 2: Add or verify config/settings.local.yaml is ignored** Add to `.gitignore`: ```gitignore # Local configuration overrides config/settings.local.yaml ``` This allows developers to have local overrides without committing them **Step 3: Commit** ```bash git add .gitignore git commit -m "chore: ignore local YAML config overrides" ``` --- ## Task 10: Final Verification and Documentation **Files:** - Update: `README.md` (if exists) **Step 1: Run complete test suite** Run: `.venv/Scripts/pytest tests/ -v --cov=app --cov-report=term-missing` Expected: All tests pass, coverage maintained **Step 2: Start application and verify database connection** Run: `.venv/Scripts/python -m app.main` Expected: Application connects to database successfully **Step 3: Update README.md with configuration instructions** Add to README.md: ```markdown ## Configuration Configuration is managed via YAML files in the `config/` directory. ### Configuration File Edit `config/settings.yaml` to configure your environment: ```yaml database: sql_server: host: your-host port: 1433 database: your-database username: your-username password: your-password ``` ### Local Overrides For local development, create `config/settings.local.yaml` to override specific values without committing them. ``` **Step 4: Final commit** ```bash git add README.md git commit -m "docs: update README with YAML configuration instructions" ``` --- ## Verification Checklist After completing all tasks: - [ ] All tests pass: `pytest tests/ -v` - [ ] Application starts successfully - [ ] Database connection works - [ ] No references to `app.core.config` remain - [ ] `.env` file removed - [ ] YAML config file exists and is valid - [ ] README updated with configuration instructions