chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
# python-configuration — detailed worked examples
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Pattern 5: Type Coercion
|
||||
|
||||
Pydantic handles common conversions automatically.
|
||||
|
||||
```python
|
||||
from pydantic_settings import BaseSettings
|
||||
from pydantic import Field, field_validator
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# Automatically converts "true", "1", "yes" to True
|
||||
debug: bool = False
|
||||
|
||||
# Automatically converts string to int
|
||||
max_connections: int = 100
|
||||
|
||||
# Parse comma-separated string to list
|
||||
allowed_hosts: list[str] = Field(default_factory=list)
|
||||
|
||||
@field_validator("allowed_hosts", mode="before")
|
||||
@classmethod
|
||||
def parse_allowed_hosts(cls, v: str | list[str]) -> list[str]:
|
||||
if isinstance(v, str):
|
||||
return [host.strip() for host in v.split(",") if host.strip()]
|
||||
return v
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
ALLOWED_HOSTS=example.com,api.example.com,localhost
|
||||
MAX_CONNECTIONS=50
|
||||
DEBUG=true
|
||||
```
|
||||
|
||||
### Pattern 6: Environment-Specific Configuration
|
||||
|
||||
Use an environment enum to switch behavior.
|
||||
|
||||
```python
|
||||
from enum import Enum
|
||||
from pydantic_settings import BaseSettings
|
||||
from pydantic import Field, computed_field
|
||||
|
||||
class Environment(str, Enum):
|
||||
LOCAL = "local"
|
||||
STAGING = "staging"
|
||||
PRODUCTION = "production"
|
||||
|
||||
class Settings(BaseSettings):
|
||||
environment: Environment = Field(
|
||||
default=Environment.LOCAL,
|
||||
alias="ENVIRONMENT",
|
||||
)
|
||||
|
||||
# Settings that vary by environment
|
||||
log_level: str = Field(default="DEBUG", alias="LOG_LEVEL")
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def is_production(self) -> bool:
|
||||
return self.environment == Environment.PRODUCTION
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def is_local(self) -> bool:
|
||||
return self.environment == Environment.LOCAL
|
||||
|
||||
# Usage
|
||||
if settings.is_production:
|
||||
configure_production_logging()
|
||||
else:
|
||||
configure_debug_logging()
|
||||
```
|
||||
|
||||
### Pattern 7: Nested Configuration Groups
|
||||
|
||||
Organize related settings into nested models.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
class DatabaseSettings(BaseModel):
|
||||
host: str = "localhost"
|
||||
port: int = 5432
|
||||
name: str
|
||||
user: str
|
||||
password: str
|
||||
|
||||
class RedisSettings(BaseModel):
|
||||
url: str = "redis://localhost:6379"
|
||||
max_connections: int = 10
|
||||
|
||||
class Settings(BaseSettings):
|
||||
database: DatabaseSettings
|
||||
redis: RedisSettings
|
||||
debug: bool = False
|
||||
|
||||
model_config = {
|
||||
"env_nested_delimiter": "__",
|
||||
"env_file": ".env",
|
||||
}
|
||||
```
|
||||
|
||||
Environment variables use double underscore for nesting:
|
||||
|
||||
```bash
|
||||
DATABASE__HOST=db.example.com
|
||||
DATABASE__PORT=5432
|
||||
DATABASE__NAME=myapp
|
||||
DATABASE__USER=admin
|
||||
DATABASE__PASSWORD=secret
|
||||
REDIS__URL=redis://redis.example.com:6379
|
||||
```
|
||||
|
||||
### Pattern 8: Secrets from Files
|
||||
|
||||
For container environments, read secrets from mounted files.
|
||||
|
||||
```python
|
||||
from pydantic_settings import BaseSettings
|
||||
from pydantic import Field
|
||||
from pathlib import Path
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# Read from environment variable or file
|
||||
db_password: str = Field(alias="DB_PASSWORD")
|
||||
|
||||
model_config = {
|
||||
"secrets_dir": "/run/secrets", # Docker secrets location
|
||||
}
|
||||
```
|
||||
|
||||
Pydantic will look for `/run/secrets/db_password` if the env var isn't set.
|
||||
|
||||
### Pattern 9: Configuration Validation
|
||||
|
||||
Add custom validation for complex requirements.
|
||||
|
||||
```python
|
||||
from pydantic_settings import BaseSettings
|
||||
from pydantic import Field, model_validator
|
||||
|
||||
class Settings(BaseSettings):
|
||||
db_host: str = Field(alias="DB_HOST")
|
||||
db_port: int = Field(alias="DB_PORT")
|
||||
read_replica_host: str | None = Field(default=None, alias="READ_REPLICA_HOST")
|
||||
read_replica_port: int = Field(default=5432, alias="READ_REPLICA_PORT")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_replica_settings(self):
|
||||
if self.read_replica_host and self.read_replica_port == self.db_port:
|
||||
if self.read_replica_host == self.db_host:
|
||||
raise ValueError(
|
||||
"Read replica cannot be the same as primary database"
|
||||
)
|
||||
return self
|
||||
```
|
||||
Reference in New Issue
Block a user