Interpolation Syntax¶
Dracon provides powerful expression interpolation using ${...} syntax for dynamic values.
Basic Interpolation¶
Simple Expressions¶
# Environment variables
log_level: ${getenv('LOG_LEVEL', 'INFO')}
# Mathematical expressions
max_workers: ${int(getenv('NUM_CPUS', '4')) * 2}
# String operations
app_name: ${getenv('APP_NAME', 'myapp').lower()}
Evaluation Timing¶
All interpolation expressions — whether using ${...} or $(...) — are evaluated lazily at construction time. Both syntaxes behave identically.
Shorthand Variables¶
When enable_shorthand_vars=True (default):
# These are equivalent:
user_home: $HOME
user_home: ${HOME}
# Complex expressions still need full syntax
computed: ${$HOME + '/data'}
Reference System¶
Construction-time References (@)¶
Reference other keys in the same configuration:
environment: prod
database:
host: "db.${@/environment}.local" # References /environment
backup_host: "backup.${@host}" # References database/host
Reference Syntax¶
Note
KeyPaths use a dot-separated syntax to navigate the YAML structure. The slash (/) is a special character that means "root".
@/key: Absolute reference from root@key.subkey: Relative reference from current level@..key: Parent level reference@/nested.deep.key: Deep nested reference (starting at root)
Node (composition-time) Copies (&)¶
Will duplicate the referenced node:
defaults:
timeout: 30
retries: 3
service_config:
name: my-service
timeout: ${&/defaults.timeout * 2} # References anchor content
Built-in Functions¶
OS Functions¶
# Environment variables
api_url: ${getenv('API_URL', 'http://localhost:8080')}
# File system
data_dir: ${expanduser('~/data')}
current_dir: ${getcwd()}
config_files: ${listdir('/etc/myapp')}
# Path operations
log_file: ${join(expanduser('~/logs'), 'app.log')}
script_name: ${basename(FILE)} # FILE is set by the file loader
script_dir: ${dirname(FILE)}
Date/Time Functions¶
# Current datetime (default format: YYYY-MM-DD HH:MM:SS)
timestamp: ${now()}
# Custom format using strftime codes
date_only: ${now('%Y-%m-%d')}
time_only: ${now('%H:%M:%S')}
iso_format: ${now('%Y-%m-%dT%H:%M:%S')}
filename_safe: ${now('%Y%m%d_%H%M%S')}
NumPy (when installed)¶
When numpy is installed, it's available as np:
Dracon Functions¶
Context Variables¶
Automatic Context (in file loading)¶
# File information
config_backup: ${DIR}/backup/${FILE_STEM}.backup
load_timestamp: ${FILE_LOAD_TIME}
config_size: ${FILE_SIZE}
Custom Context¶
# Provided at loader construction time
loader = DraconLoader(context={
'version': '1.2.3',
'deployment': 'production',
'custom_func': lambda x: x.upper()
})
# Used in YAML
app_version: ${version}
deployment_type: ${deployment}
service_name: ${custom_func('myservice')}
Advanced Patterns¶
Conditional Expressions¶
# Simple conditionals
debug_mode: ${getenv('DEBUG', 'false').lower() == 'true'}
# Complex conditionals
log_level: ${
'DEBUG' if getenv('ENVIRONMENT') == 'dev'
else 'WARNING' if getenv('ENVIRONMENT') == 'prod'
else 'INFO'
}
List Comprehensions¶
# Generate lists
service_ports: ${[8000 + i for i in range(int(getenv('REPLICAS', '3')))]}
# Filter lists
enabled_services: ${[s for s in services if s.get('enabled', True)]}
Dictionary Operations¶
# Merge dictionaries
merged_config: ${dict(base_config, **override_config)}
# Filter dictionaries
prod_settings: ${
{k: v for k, v in all_settings.items()
if not k.startswith('dev_')}
}
Key Interpolation¶
Generate dynamic keys:
!define environments: ["dev", "staging", "prod"]
!each(env) ${environments}:
${env}_database_url: postgres://db.${env}.local/myapp
${env}_redis_url: redis://cache.${env}.local
Nested Interpolation¶
# Environment-based configuration selection
!define config_key: ${getenv('ENVIRONMENT', 'dev')}_settings
# Use the computed key
app_config: ${globals()[config_key]}
# Nested expressions
complex_value: ${getenv('PREFIX', 'app') + '_' + str(getenv('VERSION', '1'))}
Escaping¶
Escape interpolation when you need literal ${}:
# Escaped - will be literal "${version}"
escaped_template: \${version}
# Not escaped - will interpolate
interpolated: ${version}
# Mixed
docker_command: echo \${VERSION} > /tmp/version && echo ${actual_version}
Error Handling¶
Safe Navigation¶
# Handle missing keys gracefully
optional_value: ${config.get('optional_key', 'default')}
# Chain operations safely
nested_value: ${config.get('section', {}).get('key', 'fallback')}
Try-Catch Patterns¶
# Using Python's exception handling
database_url: ${
getenv('DATABASE_URL') if getenv('DATABASE_URL')
else f"postgresql://{getenv('DB_USER')}:{getenv('DB_PASS')}@{getenv('DB_HOST')}/myapp"
}
Permissive Mode¶
When permissive=True, undefined variables don't raise errors — they survive as literal ${...} strings. Known variables are resolved and constant sub-expressions are folded.
from dracon.interpolation import evaluate_expression
# partial resolution
evaluate_expression("${a} and ${b}", context={'a': 1}, permissive=True)
# → "1 and ${b}"
# single expression with partial folding
evaluate_expression("${x + 1 + y}", context={'x': 10}, permissive=True)
# → "${11 + y}"
# fully unknown — returned unchanged
evaluate_expression("${unknown}", context={}, permissive=True)
# → "${unknown}"
Available on: evaluate_expression, do_safe_eval, InterpolableNode.evaluate, LazyInterpolable, resolve_all_lazy, and Dracontainer.resolve_all_lazy.
See Permissive Evaluation for details.
Performance Notes¶
- Expressions are cached when possible
- References (
@and&) are resolved efficiently - Complex expressions may impact loading time
$()and${}behave identically — both are lazy
Common Use Cases¶
Environment-based Configuration¶
database:
host: db.${getenv('ENVIRONMENT', 'dev')}.local
pool_size: ${int(getenv('DB_POOL_SIZE', '10'))}
ssl_mode: ${
'require' if getenv('ENVIRONMENT') == 'prod'
else 'prefer'
}
Dynamic Service Discovery¶
services:
auth_service: http://${getenv('AUTH_HOST', 'localhost')}:${getenv('AUTH_PORT', '8001')}
data_service: http://${getenv('DATA_HOST', 'localhost')}:${getenv('DATA_PORT', '8002')}
service_mesh: ${
[f"http://{service}:{port}"
for service, port in zip(service_hosts, service_ports)]
}