YAML Parsing#

AccelForge inputs can be evaluated from YAML files. YAML parsing occurs once when YAML files are loaded into Python.

We use an extended version of the standard YAML syntax, including the << and <<< operators. <<, when used as a dictionary key, will merge the contents of its value with the current dictionary. <<< will merge the contents of its value and will merge nested dictionaries. The !nomerge tag will block merging from occuring.

The following is a YAML parsing cheat sheet:

# YAML Nodes
listNode:
- element1
- element2

dict_node:
  key1: value1
  key2: value2

# Styles
list_block_style:
- element1
- element2
list_flow_style: {element1, element2}

dict_block_style:
  key1: value1
  key2: value2
dict_flow_style: {key1: value1, key2: value2}

# Anchors, Aliases, and Merge Keys

# Anchors
anchored_list_flow_style: &my_anchored_list
- element1
- element2
anchored_list_block_style: &my_anchored_list [1, 2, 3, 4, 5]

anchored_dict_flow_style: &my_anchored_dict
  key1: value1
  key2: value2
anchored_dict_block_style: &my_anchored_dict {key1: value1, key2: value2}

# Aliases
my_list_alias: *my_anchored_list
result_of_my_list_alias: [1, 2, 3, 4, 5]

my_dict_alias: *my_anchored_dict
result_of_my_dict_alias: {key1: value1, key2: value2}

# Merge Keys
anchored_dict_1: &my_anchored_dict
  key1: value1_dict1
  key2: value2_dict1

anchored_dict_2: &my_anchored_dict2
  key2: value2_dict2
  key3: value3_dict2

merged_dict:
  <<: [*my_anchored_dict, *my_anchored_dict2] # My_anchored_dict takes precedence

result_of_merged_dict:
  key1: value1_dict1
  key2: value2_dict1 # Earlier anchors take precedence
  key3: value3_dict2

merged_dict2:
  <<: *my_anchored_dict
  value2: override_value2 # Override value2

result_of_merged_dict2:
  key1: value1_dict1
  key2: override_value2

# Hierarchical Merge Keys
anchored_dict_hierarchical_1: &my_anchored_dict
  key1: value1_dict1
  key2: {subkey1: subvalue1, subkey2: subvalue2}
  mylist: [d, e, f]
  mylist_nomerge: [4, 5, 6]

merged_dict_hierarchical:
  <<<: *my_anchored_dict
  key2: {subkey1: override1} # subkey2: subvalue2 will come from the merge
  mylist: [a, b, c]
  mylist_nomerge: !nomerge [1, 2, 3]

result_of_merged_dict_hierarchical:
  key1: value1_dict1
  key2: {subkey1: override1, subkey2: subvalue2}
  mylist: [a, b, c, d, e, f]
  mylist_nomerge: [1, 2, 3]

merged_dict_non_hierarchical:
  <<: *my_anchored_dict
  key2: {subkey1: override1} # This will override all of key2
  mylist: [a, b, c]
  mylist_nomerge: !nomerge [1, 2, 3]

result_of_merged_dict_non_hierarchical:
  key1: value1_dict1
  key2: {subkey1: override1}
  mylist: [a, b, c]
  mylist_nomerge: [1, 2, 3]

Jinja2 Templating#

We also support Jinja2 templating. To substitute Jinja2 variables, the jinja_parse_data argument can be passed to the from_yaml() function. Additional Jinja2 functions are also supported, including:

  • add_to_path(path): Add a path to the search path for the include function.

  • cwd(): Return the current working directory.

  • find_path(path): Find a file in the search path and return the path to the file.

  • include(path, key): Include a file and return the value of the key. For example, include(path/x.yaml, a) will open the file path/x.yaml, look for a top-level dictionary, and return the a key from that dictionary. Multiple levels of indexing can be used, such as include(path/x.yaml, a.b.c).

  • include_all(path, key): Include all files in a directory and return the value of the key. For example, include_all(path/dir, a) will open all files in the directory path/dir, look for a top-level dictionary, and return the a key from that dictionary.

  • include_text(path): Include a file and return the text of the file.

  • path_exists(path): Check if a file exists in the search path.

The following is a Jinja2 template cheat sheet:

# Add files to be included in the environment
{{add_to_path('path/to/some/dir')}}
{{add_to_path('path/to/some/other/dir')}}

variables:
  var1: 5
  var3: "{{cwd()}}/some_file.yaml" # {{cwd()}} is the directory of this file
  var4: "{{find_path('some_file.yaml')}}" # find_path searches all paths added by add_to_path
  var5: {{set_by_jinja}} # Sets the value to a "set_by_jinja" variable that must be defined

  {% if path_exists('some_file.yaml') %} # Check if a file exists
  var6: "some_file.yaml exists" # Include this line if the file exists
  {% else %}

arch:
  # Include a subset of the file. Index into the structure with
  # dot-separated keys.
  nodes: {{include('other.arch.yaml', 'arch.nodes')}}

# Include the entire file
{{include_text('grab_text_from_file.yaml')}}

compound_components:
  # Include the subsets of multiple files. They will be merged into one list.
  classes: {{include_all('compound_components/*.yaml', 'compound_components.classes')}}


{% if enable_text_flag|default(False) %}
text_included_if_enable_text_flag_is_true: |
  This text will be included if enable_text_flag is true. The |default(False) sets
  the default value of enable_text_flag to False if it is not set.
{% endif %}