Getting Started
Install and run your first configuration loader in 5 minutes.
Installation
uv add utilityhub_config
Optional dependencies:
# For YAML support
uv add pyyaml
# For .env file support
uv add python-dotenv
Your First Config
Create a simple configuration:
from pydantic import BaseModel
from utilityhub_config import load_settings
class Config(BaseModel):
database_url: str = "sqlite:///default.db"
debug: bool = False
workers: int = 4
# Load settings from all available sources
settings, metadata = load_settings(Config)
# Use your settings
print(f"Database: {settings.database_url}")
print(f"Debug mode: {settings.debug}")
print(f"Workers: {settings.workers}")
That's it! load_settings() automatically:
- Uses model field defaults
- Checks
~/.config/config/config.tomland~/.config/config/config.yaml - Checks
./config.toml,./config.yaml, and files in./config/ - Reads
.envin current directory - Checks environment variables (
DATABASE_URL,DEBUG,WORKERS)
See Where Values Come From
settings, metadata = load_settings(Config)
# Check the source of a field
source = metadata.get_source("database_url")
print(f"Came from: {source.source}") # "env", "project", "defaults", etc.
print(f"File: {source.source_path}") # Full path or "ENV:DATABASE_URL"
print(f"Raw value: {source.raw_value}") # Original value before validation
Next Steps
Choose your path:
👉 Want to understand precedence? Read Precedence Order
👉 Ready to use files? Jump to Configuration Files guide
👉 Using environment? See Environment Variables guide
👉 Need specific patterns? Browse Usage Guides
👉 Troubleshooting? Check Troubleshooting guide
Common Tasks
Use a Config File
from pathlib import Path
settings, _ = load_settings(
Config,
config_file=Path("./production.yaml")
)
See Explicit Config Files guide
Use Environment Variable Prefix
settings, _ = load_settings(
Config,
env_prefix="MYAPP" # Looks for MYAPP_DATABASE_URL, etc.
)
See Environment Variables guide
Disable Environment Variables
settings, _ = load_settings(
Config,
env_vars=False
)
No environment variable lookup is performed when env_vars=False, even if env_prefix is provided.
Dynamic Extension Schemas
from pydantic import BaseModel
class ComponentConfig(BaseModel):
threshold: float = 0.75
model_path: str = "~/default/path"
class AppConfig(BaseModel):
app_name: str = "myapp"
plugins: dict[str, object] = {}
settings, metadata = load_settings(
AppConfig,
extension_root="plugins",
extension_schemas={"component_a": ComponentConfig},
)
# Recommended access pattern for validated extension sections:
component_a = metadata.extension_configs["component_a"]
print(component_a.threshold)
print(component_a.model_path)
This validates plugins.component_a against ComponentConfig and preserves the typed extension configuration. Use metadata.extension_configs to access the resulting validated extension models.
Override at Runtime
settings, _ = load_settings(
Config,
overrides={
"debug": True,
"workers": 8
}
)
Handle Errors
from utilityhub_config.errors import ConfigValidationError
try:
settings, _ = load_settings(Config)
except ConfigValidationError as e:
print(f"Configuration error: {e}")
What's Different?
utilityhub_config is explicit, not magical:
- ✅ Precedence is clear and documented
- ✅ You know exactly where each setting came from (metadata)
- ✅ Type safety via Pydantic
- ✅ No hidden behavior - what you see is what you get
- ✅ Rich errors when validation fails