pydantic-typed-config-loader
Skill0 starsChanged 7 months agoMITView on GitHub
Environment-based config via pydantic-settings with type safety, fail-fast validation, and environment separation (dev/prod). Use when setting up any Python project configuration, when user says "add config", "environment variables", "settings", or needs typed configuration management.
- Reads credentials
In this repository37 other files it gives its agents.
- ablation-study-frameworkSkill
- async-http-websocket-patternsSkill
- brain-dumpSkill
- chaos-controlled-behavior-systemSkill
- daily-digestSkill
- database-reset-and-seedSkill
- duckdb-analytics-layerSkill
- event-driven-backtest-engineSkill
- event-study-full-pipelineSkill
- event-study-stats-suiteSkill
- failure-learning-systemSkill
- fastapi-sse-live-dashboardSkill
- goal-task-decompositionSkill
- high-information-gain-interviewerSkill
- idea-extraction-batch-pipelineSkill
- idea-grouping-taxonomySkill
- interview-belief-extractorSkill
- journal-captureSkill
- journal-reviewSkill
- keyword-filter-frameworkSkill
- langgraph-agentic-pipelineSkill
- llm-as-judge-eval-frameworkSkill
- multi-tab-dashboard-rendererSkill
- multi-tier-memory-systemSkill
- parquet-storage-with-walSkill
- project-insightsSkill
- quality-trend-detectionSkill
- research-gap-finderSkill
- safe-commitSkill
- simtom-knowledge-isolationSkill
- statistical-signal-detectionSkill
- streaming-dataset-pipelineSkill
- structured-data-extraction-pipelineSkill
- structured-logging-setupSkill
- system-health-monitoringSkill
- textual-tui-reactiveSkill
- vector-db-ingestionSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 37
Create an enum for environment modes.
- Very specific: line 93
Use field_validator for paths that need expansion, files that must exist, or values that need transformation.
- Very specific: line 120
Never auto-construct settings.
- Very specific: line 191
Fail fast: All validation happens at startup in load_settings(), not when a field is first accessed at runtime.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Pydantic Typed Config Loader
Set up typed, validated, environment-aware configuration for Python projects using pydantic-settings. Fail-fast on bad config at startup, not at runtime.
When to Use
- Starting a new Python project that reads environment variables
- Replacing raw
os.getenv()calls with typed config - Setting up dev/prod environment separation
- Any service that needs validated configuration at startup
Architecture
project/
config.py # All settings classes + load_settings()
secrets/
.env.shared # Shared defaults (committed)
.env.demo # Demo/dev overrides (committed)
.env.production # Prod secrets (gitignored)Step-by-Step Implementation
Step 1: Define an environment enum
Create an enum for environment modes. This prevents typos and gives autocomplete.
from enum import Enum
class AppEnv(str, Enum):
DEV = "dev"
PRODUCTION = "production"Step 2: Create settings classes with pydantic-settings
Each logical group of config gets its own class. Use env_prefix to namespace environment variables.
from pathlib import Path
from typing import Optional
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class AppSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="APP_")
env: AppEnv = AppEnv.DEV
data_dir: Path = Path("data")
log_level: str = "INFO"
@property
def is_production(self) -> bool:
return self.env == AppEnv.PRODUCTION
class DatabaseSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="DB_")
host: str = "localhost"
port: int = 5432
name: str = "myapp"
user: str = "postgres"
password: str = ""
@property
def url(self) -> str:
return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.name}"
class ExternalAPISettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="API_")
key: str = ""
base_url: str = "https://api.example.com/v1"
rate_limit_rps: int = 10Step 3: Add validators for complex fields
Use field_validator for paths that need expansion, files that must exist, or values that need transformation.
class CredentialSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="CRED_")
key_path: Path = Path.home() / ".credentials" / "key.pem"
@field_validator("key_path", mode="before")
@classmethod
def expand_path(cls, v: str | Path) -> Path:
return Path(v).expanduser()
def validate_key_exists(self) -> None:
"""Call at startup to fail fast on missing credentials."""
if not self.key_path.exists():
raise FileNotFoundError(f"Key not found: {self.key_path}")
mode = self.key_path.stat().st_mode & 0o777
if mode & 0o077:
raise PermissionError(
f"Key {self.key_path} has insecure permissions ({oct(mode)}). "
f"Run: chmod 600 {self.key_path}"
)Step 4: Module-level singletons with explicit initialization
Never auto-construct settings. Use a load_settings() function that must be called once at startup. Accessor functions raise if called before init.
_app: Optional[AppSettings] = None
_db: Optional[DatabaseSettings] = None
def settings() -> AppSettings:
if _app is None:
raise RuntimeError("Call load_settings() at startup before accessing settings.")
return _app
def db_settings() -> DatabaseSettings:
if _db is None:
raise RuntimeError("Call load_settings() at startup before accessing settings.")
return _dbStep 5: Implement load_settings() with environment layering
Load .env files in order: shared defaults first, then environment-specific overrides. Validate everything before returning.
import os
import sys
def load_settings(env_override: Optional[str] = None) -> AppSettings:
"""Load and validate all settings. Fails fast on missing/invalid config."""
global _app, _db
from dotenv import load_dotenv
env = env_override or os.getenv("APP_ENV", "dev")
os.environ.setdefault("APP_ENV", env)
secrets_dir = Path("secrets")
load_dotenv(secrets_dir / ".env.shared", override=False)
load_dotenv(secrets_dir / f".env.{env}", override=True)
_app = AppSettings()
_db = DatabaseSettings()
if _app.is_production:
_production_confirmation()
return _app
def _production_confirmation() -> None:
"""Safety gate for production mode."""
print("=" * 60, file=sys.stderr)
print(" WARNING: RUNNING IN PRODUCTION MODE", file=sys.stderr)
print("=" * 60, file=sys.stderr)
confirm = input("Type 'PRODUCTION' to confirm: ")
if confirm != "PRODUCTION":
sys.exit("Aborted.")Step 6: Use in application entrypoint
# main.py
from config import load_settings, settings, db_settings
def main():
cfg = load_settings()
print(f"Running in {cfg.env.value} mode")
print(f"Database: {db_settings().url}")
print(f"Log level: {settings().log_level}")Key Principles
- Fail fast: All validation happens at startup in
load_settings(), not when a field is first accessed at runtime. - Explicit init: Settings are
Noneuntilload_settings()is called. Accessing them early raises immediately rather than silently using defaults. - Environment layering:
.env.sharedfor defaults,.env.{env}for overrides. Production secrets never committed. - Type safety: Pydantic coerces and validates types automatically.
port: intrejects"abc"at load time. - Grouped by concern: Each external service or subsystem gets its own settings class with its own
env_prefix.
Dependencies
pydantic>=2.0
pydantic-settings>=2.0
python-dotenvDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

