Quick Start

Installation

pip install varlord

# With optional dependencies
pip install varlord[etcd]

Basic Usage

Step 1: Define your configuration model

from dataclasses import dataclass, field
from varlord import Config, sources

@dataclass(frozen=True)
class AppConfig:
    host: str = field(default="127.0.0.1")
    port: int = field(default=8000)
    debug: bool = field(default=False)

Step 2: Create and load configuration

cfg = Config(
    model=AppConfig,
    sources=[
        sources.Env(),  # Model auto-injected, defaults applied automatically
        sources.CLI(),  # Model auto-injected
    ],
)

app = cfg.load()
print(app.host)  # Can be overridden by env var or CLI arg

Or use the convenience method:

cfg = Config.from_model(
    AppConfig,
    cli=True,  # env_prefix removed - all env vars filtered by model
)

app = cfg.load()

Priority Ordering

Priority is determined by sources order (later sources override earlier ones):

cfg = Config(
    model=AppConfig,
    sources=[
        sources.Env(),  # Model defaults applied first, then env
        sources.CLI(),  # Highest priority (last)
    ],
)

For per-key priority rules, use PriorityPolicy:

from varlord import PriorityPolicy

cfg = Config(
    model=AppConfig,
    sources=[...],
    policy=PriorityPolicy(
        default=["defaults", "env", "cli"],
        overrides={
            "secrets.*": ["defaults", "etcd"],  # Secrets: skip env
        },
    ),
)

Validation

Add validation in your model’s __post_init__:

from varlord.validators import validate_range, validate_regex

@dataclass(frozen=True)
class AppConfig:
    port: int = 8000
    host: str = "127.0.0.1"

    def __post_init__(self):
        validate_range(self.port, min=1, max=65535)
        validate_regex(self.host, r'^\d+\.\d+\.\d+\.\d+$')

Logging

Enable debug logging to track configuration loading:

import logging
from varlord import set_log_level

set_log_level(logging.DEBUG)
cfg = Config(...)
app = cfg.load()  # Will log source loads, merges, conversions

Dynamic Updates

Use ConfigStore for dynamic configuration updates:

def on_change(new_config, diff):
    print("Config updated:", diff)

# Enable watch in the source
cfg = Config(
    model=AppConfig,
    sources=[
        sources.Etcd(..., watch=True),  # Enable watch here
    ],
)

# load_store() automatically detects and enables watch
store = cfg.load_store()  # No watch parameter needed
store.subscribe(on_change)
current = store.get()  # Thread-safe access

Note

load_store() automatically detects if any source supports watching. You only need to enable watch in the source itself (e.g., Etcd(watch=True)).