Skip to content

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: ${os.cpu_count() * 2}

# String operations
app_name: ${getenv('APP_NAME', 'myapp').lower()}

Immediate vs Lazy Evaluation

# Lazy evaluation (default) - resolved at construction time
computed_at_runtime: ${time.time()}

# Immediate evaluation - resolved during loading
computed_at_load: $(time.time())

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

KeyPath 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

  • @/key: Absolute reference from root
  • @key: Relative reference from current level
  • @../key: Parent level reference
  • @/nested/deep/key: Deep nested reference

Anchor References (&)

Reference YAML anchors and process them:

defaults: &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__)}
script_dir: ${dirname(__file__)}

Dracon Functions

# Construct deferred nodes
output_path: ${construct(deferred_node, {'runtime_var': 'value'})}

# Access current file context
config_dir: ${__DRACON__CURRENT_PATH}
parent_dir: ${__DRACON__PARENT_PATH}

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 when loading
loader.load('config.yaml', 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"
}

Performance Notes

  • Expressions are cached when possible
  • References (@ and &) are resolved efficiently
  • Complex expressions may impact loading time
  • Use immediate evaluation $() for values that don't change

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)]
}

Configuration Validation

# Validate required environment variables
database_url: ${
  getenv('DATABASE_URL') or 
  (_ for _ in ()).throw(ValueError('DATABASE_URL is required'))
}