How-To: Use Interpolation for Dynamic Values¶
Dracon allows embedding Python expressions in your YAML strings for dynamic configuration.
Basic Lazy Interpolation (${...}
)¶
Values enclosed in ${...}
are evaluated lazily – only when the configuration value is first accessed in your code.
!define base_port: 8000
!define instance_num: ${getenv('INSTANCE_NUM', 0)} # Evaluated lazily
server:
# Simple math using context variables
port: ${base_port + instance_num}
# String formatting
host: "server-${instance_num}.example.com"
# Conditional logic
log_level: ${'DEBUG' if getenv('ENV') == 'dev' else 'INFO'}
In Python:
import dracon as dr
import os
os.environ['INSTANCE_NUM'] = '3'
os.environ['ENV'] = 'dev'
config = dr.loads(yaml_content_above) # Assuming yaml_content_above holds the YAML
print(config.server.port) # Output: 8003 (evaluates 8000 + 3)
print(config.server.host) # Output: server-3.example.com
print(config.server.log_level) # Output: DEBUG
Referencing Other Config Values (@
)¶
Inside a ${...}
expression, use @
followed by a KeyPath to reference the final value of another configuration key after all loading, merging, and construction.
- Absolute Path:
@/path/from/root
- Relative Path:
@.sibling_key
,@../parent_key
app:
name: "MyService"
port: 9000
logging:
# Absolute path reference
filename: "/var/log/${@/app.name}.log" # -> /var/log/MyService.log
# Relative path reference
level_info: "Log level for ${@.filename}" # -> Log level for /var/log/MyService.log
Important: @
references point to the final, constructed value, but the expression itself is still evaluated lazily.
Immediate Interpolation ($(...)
)¶
Values enclosed in $(...)
are evaluated immediately during the YAML parsing phase.
Use Cases:
- Dynamically generating YAML tags.
- Calculating simple scalar values needed instantly.
Limitations:
- Cannot use
@
references (target values don't exist yet). - Can only reliably access context variables defined before the
$(...)
expression.
!define type_name: "str"
!define scale: 10
config:
# Tag determined immediately
value: !$(type_name) 123.45 # Node gets tag !str
# Value calculated immediately
scaled_value: $(scale * 5.5) # Node gets value 55.0
Using Context Variables¶
Both ${...}
and $(...)
can access:
- Variables and functions passed via
context=...
. - Variables defined using
!define
or!set_default
in the current or parent scope. - Built-in functions like
getenv
,getcwd
,max
,str
, etc. - Automatic variables like
$DIR
,$FILE
within included files (see Includes Guide).
# main.py
import dracon as dr
import os
def my_helper(a, b):
return a * b + 1
context = {
'ENV': os.getenv('ENVIRONMENT', 'dev'),
'my_helper': my_helper
}
yaml_string = """
!define factor: 2
calculated: ${my_helper(@/input_value, factor)}
env_based: ${factor if ENV == 'prod' else factor * 2}
input_value: 10
"""
config = dr.loads(yaml_string, context=context)
print(config.calculated) # Output: 21 (my_helper(10, 2))
print(config.env_based) # Output: 4 (factor * 2 because ENV='dev')
See Interpolation Concepts for more on the evaluation engine and safety considerations.