Advanced Features¶
This guide covers advanced Dracon features that go beyond basic configuration loading.
Advanced Merge Strategies¶
Depth-Limited Merging¶
Control how deep recursive merging goes:
# Merge only 2 levels deep
<<{+2}: !include file:base.yaml
# Different limits for different sources
base_config:
<<{+1}: !include file:shallow.yaml # Only merge top level
<<{+3}: !include file:deep.yaml # Merge 3 levels deep
Keypath Redirection¶
Redirect merge operations to specific paths:
# Merge into specific nested location
app_config:
database:
<<@/staging/database: !include file:staging.yaml
Independent Dict/List Control¶
Set different merge strategies for dictionaries and lists:
# Append lists, replace dictionaries
<<{~}[+]: !include file:source.yaml
# Replace lists, merge dictionaries recursively
<<{+}[~]: !include file:source.yaml
# Custom depth for each
<<{+2}[+1]: !include file:source.yaml
Advanced CLI Features¶
File Loading Arguments¶
Mark Pydantic fields to automatically load files:
from dracon import Arg
class Config(BaseModel):
# Automatically prefixes with '+' to load file
secrets: Annotated[dict, Arg(is_file=True, help="Secrets file")]
# Manual file loading syntax
override: Annotated[dict, Arg(help="Config override")]
# Usage: --override +config.yaml
Nested Argument Overrides¶
Override deeply nested configuration from CLI:
# Override nested fields
myapp --database.connection.pool_size 20
myapp --logging.handlers.file.level DEBUG
# Load entire nested section from file
myapp --database +db-prod.yaml
# Load specific key from file
myapp --database.host +config.yaml@database.prod_host
Context Variable Definition¶
Define variables for use in configuration interpolation:
# Define context variables
myapp --define.region us-west-2 --define.version 1.2.3
# Use in YAML files
region_config: ${region}
app_version: ${version}
Advanced Interpolation¶
Reference System¶
Use @
for keypath references and &
for anchor references:
environment: prod
# Keypath references
database:
host: "db.${@/environment}.local" # References /environment
backup: "backup.${@host}.local" # References database/host
# Anchor references
defaults: &defaults
timeout: 30
retries: 3
service:
timeout: ${&defaults.timeout * 2} # References anchor content
Key Interpolation¶
Generate dynamic keys using interpolation:
!define theme:
t1: primary
t2: secondary
# Generate keys dynamically
styles:
${theme.t1}_color: "#007bff"
${theme.t2}_color: "#6c757d"
Context-Aware Interpolation¶
Access file and runtime context:
# File context
config_backup: ${DIR}/backup/${FILE_STEM}.backup
load_time: ${FILE_LOAD_TIME}
# Runtime context (provided during loading)
deployment_url: https://${region}.example.com
version_tag: ${app_name}:${version}
Advanced Deferred Execution¶
Selective Deferral¶
Force specific paths to be deferred:
loader = DraconLoader(
deferred_paths=[
'app.output_path',
'services.*.endpoint' # Wildcards supported
]
)
Context Clearing¶
Clear specific context variables in deferred nodes:
# Clear old context variables
clean_config: !deferred::clear_ctx=old_var,temp_value
new_value: ${fresh_runtime_var}
Query Parameters in Deferred Tags¶
Pass parameters to deferred construction:
# With query-style parameters
computed_path: !deferred::base_path=/data&suffix=.log ${base_path}/app${suffix}
Advanced Include Patterns¶
Package Resources¶
Load from Python packages:
# Include from installed package
defaults: !include pkg:mypackage:config/defaults.yaml
# With keypath selection
db_config: !include pkg:mypackage:config/db.yaml@production
Environment and Variable Includes¶
# From environment variables
api_key: !include env:API_SECRET
# From defined variables
!define config_name: advanced
current_config: !include var:config_name
Instruction-Based Programming¶
Complex Loops¶
# Loop with complex content generation
!define services: [auth, api, worker]
!define environments: [dev, staging, prod]
!each(service) ${services}:
!each(env) ${environments}:
${service}_${env}:
image: myapp/${service}:${env}
replicas: ${1 if env == 'dev' else 3}
resources:
cpu: ${0.5 if service == 'worker' else 1.0}
memory: ${512 if env == 'dev' else 1024}MB
Variable Scoping¶
!define global_timeout: 30
services:
!define service_timeout: ${global_timeout * 2}
auth:
timeout: ${service_timeout} # Uses local definition
api:
!define service_timeout: ${global_timeout / 2}
timeout: ${service_timeout} # Uses redefined local value
Performance Optimizations¶
Caching Control¶
# Disable caching for dynamic content
loader = DraconLoader(use_cache=False)
# Custom cache settings (implementation dependent)
loader = DraconLoader(use_cache=True) # Uses LRU cache with 128 items
Custom Container Types¶
Use specialized data structures:
from collections import OrderedDict, deque
# Custom container types for performance
loader = DraconLoader(
base_dict_type=OrderedDict, # Maintain key order
base_list_type=deque # Efficient appends/prepends
)
Parallel Processing Support¶
Dracon configurations work with multiprocessing:
import multiprocessing as mp
from dracon import load
def worker(config_data):
# config_data is picklable and works across processes
return process_with_config(config_data)
if __name__ == '__main__':
config = load('config.yaml')
with mp.Pool() as pool:
results = pool.map(worker, [config] * 4)
Error Handling and Debugging¶
Rich Error Messages¶
Dracon provides detailed error context:
try:
config = load('config.yaml')
except Exception as e:
# Rich error formatting with context
print(e) # Shows file location, line numbers, validation details
Validation with Custom Messages¶
from pydantic import BaseModel, validator
class Config(BaseModel):
port: int
@validator('port')
def validate_port(cls, v):
if not (1024 <= v <= 65535):
raise ValueError(f"Port {v} must be between 1024 and 65535")
return v
Safe environment variable access¶
api_url: ${getenv('API_URL') or 'http://localhost:8080'}
## Integration Patterns
### Custom Loaders
Extend the loader system:
```python
from dracon.loaders import BaseLoader
class DatabaseLoader(BaseLoader):
def load(self, path, context):
# Load configuration from database
return fetch_config_from_db(path)
# Register custom loader
loader = DraconLoader()
loader.register_loader('db', DatabaseLoader())
# Use in YAML
config: !include db:config_table@prod_settings
Pre-commit Hook Integration¶
Validate configurations before commit:
#!/usr/bin/env python3
# pre-commit hook
import sys
from dracon import load
from myapp.models import AppConfig
try:
config = load(sys.argv[1], context={'AppConfig': AppConfig})
print(f"✓ Configuration {sys.argv[1]} is valid")
except Exception as e:
print(f"✗ Configuration error: {e}")
sys.exit(1)
CI/CD Integration¶
# .github/workflows/validate-config.yml
name: Validate Configuration
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Validate configs
run: |
python -c "
from dracon import load
from myapp.models import AppConfig
configs = ['config/base.yaml', 'config/prod.yaml']
for config_file in configs:
load(config_file, context={'AppConfig': AppConfig})
print(f'✓ {config_file} is valid')
"
Best Practices¶
Configuration Architecture¶
# Layered configuration with clear precedence
app_config:
# 1. Defaults (lowest priority)
<<{<+}: !include file:defaults.yaml
# 2. Environment-specific (medium priority)
<<{<+}: !include file:environments/${ENVIRONMENT}.yaml
# 3. Local overrides (highest priority)
<<{<+}: !include file:local.yaml