Priority ExampleΒΆ

Demonstrates priority ordering.

  1"""
  2Example demonstrating priority ordering.
  3
  4Shows three ways to customize priority:
  51. Reorder sources (recommended - simplest)
  62. Use PriorityPolicy for per-key rules
  73. Multiple sources of the same type with custom IDs
  8
  9Run with:
 10    python priority_example.py
 11    python priority_example.py --host 0.0.0.0 --port 9999
 12"""
 13
 14import os
 15import tempfile
 16from dataclasses import dataclass, field
 17
 18from varlord import Config, PriorityPolicy, sources
 19
 20# Set environment variables for testing
 21os.environ["HOST"] = "env-host"
 22os.environ["PORT"] = "8888"
 23os.environ["API_KEY"] = "env-api-key"
 24
 25
 26@dataclass(frozen=True)
 27class DBConfig:
 28    """Database configuration (nested dataclass - best practice)."""
 29
 30    host: str = field(default="localhost", metadata={"description": "Database host"})
 31    port: int = field(default=5432, metadata={"description": "Database port"})
 32
 33
 34@dataclass(frozen=True)
 35class AppConfig:
 36    """Application configuration model."""
 37
 38    host: str = field(default="127.0.0.1", metadata={"description": "Server host address"})
 39    port: int = field(default=8000, metadata={"description": "Server port number"})
 40    debug: bool = field(default=False, metadata={"description": "Enable debug mode"})
 41    api_key: str = field(default="default-key", metadata={"description": "API key"})
 42    # Use nested dataclass for nested structure (best practice)
 43    db: DBConfig = field(
 44        default_factory=DBConfig, metadata={"description": "Database configuration"}
 45    )
 46
 47
 48def example_1_reorder_sources():
 49    """Method 1: Reorder sources (recommended - simplest)."""
 50    print("=== Example 1: Reorder Sources ===")
 51    print("Priority: defaults < env < cli (later sources override earlier ones)\n")
 52
 53    # Priority is determined by sources order: later sources override earlier ones
 54    # Model defaults are automatically applied first (lowest priority)
 55    # Model is auto-injected to all sources by Config
 56    cfg = Config(
 57        model=AppConfig,
 58        sources=[
 59            sources.Env(),  # Overrides defaults - model auto-injected
 60            sources.CLI(),  # Highest priority (last) - model auto-injected
 61        ],
 62    )
 63
 64    app = cfg.load()
 65    print("βœ… Config loaded:")
 66    print(f"   host: {app.host} (from {'CLI' if '--host' in str(cfg._sources) else 'env'})")
 67    print(f"   port: {app.port}")
 68    print(f"   api_key: {app.api_key[:10]}...")
 69
 70
 71def example_2_priority_policy():
 72    """Method 2: Use PriorityPolicy (advanced: per-key rules)."""
 73    print("\n=== Example 2: PriorityPolicy ===")
 74    print("Custom priority: api_key and db.* only from defaults and env (CLI ignored)\n")
 75
 76    # Use when you need different priority rules for different keys
 77    # Model is auto-injected to all sources by Config
 78    cfg = Config(
 79        model=AppConfig,
 80        sources=[
 81            sources.Env(),  # Model defaults applied automatically, model auto-injected
 82            sources.CLI(),  # Model auto-injected
 83        ],
 84        policy=PriorityPolicy(
 85            default=["defaults", "env", "cli"],  # Default: all sources in order
 86            overrides={
 87                "api_key": ["defaults", "env"],  # API key: env can override, but not CLI
 88                "db.host": ["defaults", "env"],  # DB host: env can override, but not CLI
 89                "db.port": ["defaults", "env"],  # DB port: env can override, but not CLI
 90            },
 91        ),
 92    )
 93
 94    app = cfg.load()
 95    print("βœ… Config loaded:")
 96    print(f"   host: {app.host} (CLI can override)")
 97    print(f"   api_key: {app.api_key[:10]}... (CLI cannot override)")
 98    print(f"   db.host: {app.db.host} (CLI cannot override)")
 99
100
101def example_3_multiple_sources_same_type():
102    """Method 3: Multiple sources of the same type."""
103    print("\n=== Example 3: Multiple Sources of Same Type ===")
104    print("System config (lower priority) + User config (higher priority)\n")
105
106    # Create two YAML files
107    yaml1_content = """
108host: system-host
109port: 9000
110api_key: system-key
111"""
112    yaml2_content = """
113host: user-host
114port: 8080
115debug: true
116"""
117
118    with tempfile.NamedTemporaryFile(mode="w", suffix="_system.yaml", delete=False) as f:
119        f.write(yaml1_content)
120        yaml1_path = f.name
121
122    with tempfile.NamedTemporaryFile(mode="w", suffix="_user.yaml", delete=False) as f:
123        f.write(yaml2_content)
124        yaml2_path = f.name
125
126    try:
127        # Create two YAML sources with different IDs
128        yaml1 = sources.YAML(yaml1_path, model=AppConfig, source_id="system-config")
129        yaml2 = sources.YAML(yaml2_path, model=AppConfig, source_id="user-config")
130
131        cfg = Config(
132            model=AppConfig,
133            sources=[
134                yaml1,  # System config (lower priority)
135                yaml2,  # User config (higher priority - overrides system)
136            ],
137        )
138
139        app = cfg.load()
140        print("βœ… Config loaded:")
141        print(f"   host: {app.host} (from user-config, overrides system-config)")
142        print(f"   port: {app.port} (from user-config)")
143        print(f"   debug: {app.debug} (from user-config)")
144        print(
145            f"   api_key: {app.api_key[:10]}... (from system-config, user-config doesn't have it)"
146        )
147    finally:
148        import os
149
150        os.unlink(yaml1_path)
151        os.unlink(yaml2_path)
152
153
154def main():
155    """Main function."""
156    example_1_reorder_sources()
157    example_2_priority_policy()
158    example_3_multiple_sources_same_type()
159
160
161if __name__ == "__main__":
162    main()