feat: config model and yaml loader
Add Pydantic config models (SqlServerConfig, AccessConfig, RuntimeConfig, FileMapping, SyncConfig) and a YAML loader (load_config). FileMapping provides source_path() and target_table() helpers; number-typed YAML keys/values (e.g. root: 2026) are coerced to str via coerce_numbers_to_str. Includes config.example.yaml template (config.yaml with real credentials stays gitignored) and pyproject.toml pytest config (pythonpath=src). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
50
src/sync/config.py
Normal file
50
src/sync/config.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
class SqlServerConfig(BaseModel):
|
||||
conn_str: str
|
||||
sync_queue_table: str = "dbo.SyncQueue"
|
||||
|
||||
class AccessConfig(BaseModel):
|
||||
model_config = ConfigDict(coerce_numbers_to_str=True)
|
||||
driver: str
|
||||
roots: dict[str, str]
|
||||
|
||||
class RuntimeConfig(BaseModel):
|
||||
poll_interval_seconds: int = 10
|
||||
capture_batch_size: int = 500
|
||||
apply_batch_size: int = 200
|
||||
max_retries: int = 5
|
||||
retry_backoff_seconds: int = 30
|
||||
cleanup_batch_size: int = 200
|
||||
cleanup_lock_retries: int = 3
|
||||
|
||||
class FileMapping(BaseModel):
|
||||
model_config = ConfigDict(coerce_numbers_to_str=True)
|
||||
file: str
|
||||
root: str
|
||||
schema: str
|
||||
year_suffix: str = ""
|
||||
exclude_tables: list[str] = Field(default_factory=list)
|
||||
include_tables: list[str] | None = None
|
||||
|
||||
def source_path(self, cfg: "SyncConfig") -> str:
|
||||
base = cfg.access.roots[self.root]
|
||||
return f"{base}\\{self.file}"
|
||||
|
||||
def target_table(self, access_table: str) -> str:
|
||||
return f"{access_table}{self.year_suffix}"
|
||||
|
||||
class SyncConfig(BaseModel):
|
||||
sql_server: SqlServerConfig
|
||||
access: AccessConfig
|
||||
runtime: RuntimeConfig
|
||||
files: list[FileMapping]
|
||||
logging: dict | None = None
|
||||
|
||||
def load_config(path: str) -> SyncConfig:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
data = yaml.safe_load(f)
|
||||
return SyncConfig(**data)
|
||||
Reference in New Issue
Block a user