Simply Sane Settings
Born of the desire of having app config behaving in an explicit way, to zero in and fix missing config very quickly
- Explicit is better than implicit - Never silently use defaults when you meant to configure something
- Fail fast with clarity - Missing env vars show you exactly what was expected
- Debuggability - Debug logs highlight when defaults are used, helping catch typos
- Security - Secret values are masked in logs automatically
pip install sane-settingsRequires Python 3.10 or higher.
from dataclasses import dataclass
from sane_settings import EnvConfigBase, env_field, prefix_field, SecretStr
@dataclass
class DatabaseSettings(EnvConfigBase):
host: str = env_field("HOST", default="localhost")
port: int = env_field("PORT", default=5432)
password: SecretStr = env_field("PASSWORD") # Required - will fail if missing
@dataclass
class AppSettings(EnvConfigBase):
database: DatabaseSettings = prefix_field("DB")
debug: bool = env_field("DEBUG", default=False)
# Load from environment
settings = AppSettings.load_from_env(app_prefix="MYAPP")
# Access nested settings
print(settings.database.host) # Uses MYAPP_DB_HOST
print(settings.database.password) # SecretStr('**********')- Having an extremely explicit settings library that minimise the time spent fighting with settings
- For attributes without defaults, if SaneSettings does not find a env var, it will fail loading and tell you EXACTLY what env var it was expecting.
- Any attributes with default that does NOT get replaced with an env var will be highlighted in logs (use DEBUG log level) which helps you quickly find typos in your Env Vars
- Only supports environment variables to override the defaults
- Loading from config files
from sane_settings import (
EnvConfigBase,
Environments,
SecretStr,
env_field,
prefix_field,
)
@dataclass
class DatabaseSettings(EnvConfigBase):
"""Database connection settings."""
# Database connection parameters
host: str = env_field("HOST", default="localhost") # APP_DB_HOST
port: int = env_field("PORT", default=5432) # APP_DB_PORT
user: str = env_field("USER", default="postgres") # APP_DB_USER
password: SecretStr = env_field("PASSWORD", default="postgres") # APP_DB_PASSWORD, will NOT show in logs unless you use DatabaseSettings().password.get_secret_value()
name: str = env_field("NAME", default="queue_service") # APP_DB_NAME
@property
def uri(self) -> str:
"""Get the PostgreSQL connection URI."""
return f"postgresql://{self.user}:{self.password.get_secret_value()}@{self.host}:{self.port}/{self.name}?sslmode=allow"
@dataclass
class Settings(EnvConfigBase):
"""Main application settings."""
# Nested Settings objects
database: DatabaseSettings = prefix_field("DB") # Any nested attribute will use APP_DB_{atribute}
# Basic settings
TRACING_BACKEND: TracingBackends | None = env_field("TRACING_BACKEND")
ENVIRONMENT: Environments = env_field("ENVIRONMENT") # a default enum for your environments
# defaults at the end
SERVICE_NAME: str = "my-service"
settings = Settings.load_from_env(app_prefix="APP", pretty_check=True)| Document | Purpose |
|---|---|
| User Guide | Getting started, configuration patterns, type casting, best practices |
| API Reference | Complete reference for all public classes and functions |
| Architecture Guide | How the library works internally, type system, loading mechanism |
| Examples | Real-world usage patterns for common scenarios |
(https://github.com/alltuner/uv-version-bumper) just bump-patch|minor|major just push-all
uv run pytest -v