File Sources Example

Example demonstrating YAML, JSON, and TOML file sources.

  1"""
  2Example demonstrating file-based sources (YAML, JSON, TOML).
  3
  4This example shows:
  5- Loading configuration from YAML, JSON, and TOML files
  6- Multiple file sources with priority
  7- Nested configuration structures
  8- Missing file handling (graceful degradation)
  9
 10Run with:
 11    python file_sources_example.py
 12    python file_sources_example.py -cv  # Check variables
 13"""
 14
 15import os
 16import tempfile
 17from dataclasses import dataclass, field
 18
 19from varlord import Config, sources
 20
 21
 22@dataclass(frozen=True)
 23class DBConfig:
 24    """Database configuration (nested dataclass - best practice)."""
 25
 26    host: str = field(default="localhost", metadata={"description": "Database host"})
 27    port: int = field(default=5432, metadata={"description": "Database port"})
 28
 29
 30@dataclass(frozen=True)
 31class AppConfig:
 32    """Application configuration model."""
 33
 34    host: str = field(default="127.0.0.1", metadata={"description": "Server host address"})
 35    port: int = field(default=8000, metadata={"description": "Server port number"})
 36    debug: bool = field(default=False, metadata={"description": "Enable debug mode"})
 37    api_key: str = field(default="default-key", metadata={"description": "API key"})
 38    # Use nested dataclass for nested structure (best practice)
 39    db: DBConfig = field(
 40        default_factory=DBConfig, metadata={"description": "Database configuration"}
 41    )
 42
 43
 44def example_yaml_source():
 45    """Example: YAML source."""
 46    print("=== Example 1: YAML Source ===\n")
 47
 48    yaml_content = """
 49host: 0.0.0.0
 50port: 8080
 51debug: true
 52api_key: yaml-api-key
 53db:
 54  host: db.example.com
 55  port: 3306
 56"""
 57
 58    with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
 59        f.write(yaml_content)
 60        yaml_path = f.name
 61
 62    try:
 63        cfg = Config(
 64            model=AppConfig,
 65            sources=[
 66                sources.YAML(yaml_path, model=AppConfig),
 67            ],
 68        )
 69
 70        app = cfg.load()
 71        print("✅ Config loaded from YAML:")
 72        print(f"   host: {app.host}")
 73        print(f"   port: {app.port}")
 74        print(f"   debug: {app.debug}")
 75        print(f"   db.host: {app.db.host}")
 76        print(f"   db.port: {app.db.port}")
 77    finally:
 78        os.unlink(yaml_path)
 79
 80
 81def example_json_source():
 82    """Example: JSON source."""
 83    print("\n=== Example 2: JSON Source ===\n")
 84
 85    json_content = """{
 86    "host": "0.0.0.0",
 87    "port": 8080,
 88    "debug": true,
 89    "api_key": "json-api-key",
 90    "db": {
 91        "host": "db.example.com",
 92        "port": 3306
 93    }
 94}
 95"""
 96
 97    with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
 98        f.write(json_content)
 99        json_path = f.name
100
101    try:
102        cfg = Config(
103            model=AppConfig,
104            sources=[
105                sources.JSON(json_path, model=AppConfig),
106            ],
107        )
108
109        app = cfg.load()
110        print("✅ Config loaded from JSON:")
111        print(f"   host: {app.host}")
112        print(f"   port: {app.port}")
113        print(f"   debug: {app.debug}")
114        print(f"   db.host: {app.db.host}")
115        print(f"   db.port: {app.db.port}")
116    finally:
117        os.unlink(json_path)
118
119
120def example_toml_source():
121    """Example: TOML source."""
122    print("\n=== Example 3: TOML Source ===\n")
123
124    toml_content = """
125host = "0.0.0.0"
126port = 8080
127debug = true
128api_key = "toml-api-key"
129
130[db]
131host = "db.example.com"
132port = 3306
133"""
134
135    with tempfile.NamedTemporaryFile(mode="w", suffix=".toml", delete=False) as f:
136        f.write(toml_content)
137        toml_path = f.name
138
139    try:
140        cfg = Config(
141            model=AppConfig,
142            sources=[
143                sources.TOML(toml_path, model=AppConfig),
144            ],
145        )
146
147        app = cfg.load()
148        print("✅ Config loaded from TOML:")
149        print(f"   host: {app.host}")
150        print(f"   port: {app.port}")
151        print(f"   debug: {app.debug}")
152        print(f"   db.host: {app.db.host}")
153        print(f"   db.port: {app.db.port}")
154    finally:
155        os.unlink(toml_path)
156
157
158def example_multiple_file_sources():
159    """Example: Multiple file sources with priority."""
160    print("\n=== Example 4: Multiple File Sources (Priority) ===\n")
161
162    # System config (lower priority)
163    yaml1_content = """
164host: system-host
165port: 9000
166api_key: system-key
167"""
168    # User config (higher priority)
169    yaml2_content = """
170host: user-host
171port: 8080
172debug: true
173"""
174
175    with tempfile.NamedTemporaryFile(mode="w", suffix="_system.yaml", delete=False) as f:
176        f.write(yaml1_content)
177        yaml1_path = f.name
178
179    with tempfile.NamedTemporaryFile(mode="w", suffix="_user.yaml", delete=False) as f:
180        f.write(yaml2_content)
181        yaml2_path = f.name
182
183    try:
184        cfg = Config(
185            model=AppConfig,
186            sources=[
187                sources.YAML(yaml1_path, model=AppConfig, source_id="system-config"),
188                sources.YAML(yaml2_path, model=AppConfig, source_id="user-config"),
189            ],
190        )
191
192        app = cfg.load()
193        print("✅ Config loaded from multiple YAML files:")
194        print(f"   host: {app.host} (from user-config, overrides system-config)")
195        print(f"   port: {app.port} (from user-config)")
196        print(f"   debug: {app.debug} (from user-config)")
197        print(f"   api_key: {app.api_key[:10]}... (from system-config)")
198    finally:
199        os.unlink(yaml1_path)
200        os.unlink(yaml2_path)
201
202
203def example_missing_file():
204    """Example: Missing file handling (graceful degradation)."""
205    print("\n=== Example 5: Missing File (Graceful Degradation) ===\n")
206
207    # Try to load from a non-existent file
208    non_existent_yaml = "/tmp/non_existent_config.yaml"
209
210    cfg = Config(
211        model=AppConfig,
212        sources=[
213            sources.YAML(non_existent_yaml, model=AppConfig, required=False),
214            sources.Env(),  # Fallback to env vars
215        ],
216    )
217
218    # Set some env vars as fallback
219    os.environ["HOST"] = "env-host"
220    os.environ["PORT"] = "7777"
221
222    app = cfg.load()
223    print("✅ Config loaded (missing file handled gracefully):")
224    print(f"   host: {app.host} (from env, file not found)")
225    print(f"   port: {app.port} (from env)")
226    print("   Note: Missing file shows as 'Not Available' in -cv output")
227
228
229def main():
230    """Main function."""
231    example_yaml_source()
232    example_json_source()
233    example_toml_source()
234    example_multiple_file_sources()
235    example_missing_file()
236
237    print("\n💡 Tip: Run with -cv to see detailed source information and status")
238    print("   Example: python file_sources_example.py -cv")
239
240
241if __name__ == "__main__":
242    main()