Custom Tags

Tags are used to denote the type of a YAML node when converting to a Python object.

UW supports the use of standard YAML tags, denoted by !! as defined in the YAML specification. Additionally, UW defines the following tags to support use cases not covered by standard tags, denoted by ! and described in detail below. Where standard YAML tags are applied to their values immediately, application of UW YAML tags is delayed until after Jinja2 expressions in tagged values are dereferenced.

Tags may be implicit:

boolean: true
integer: 3
float: 3.14

Or explicit:

boolean: !!bool "true"
integer: !!int "3"
float: !!float "3.14"

NB Values tagged with UW YAML tags must be strings. Use quotes as necessary to ensure that they are.

!bool

Converts the tagged node to a Python bool object. For example, given input.yaml:

flag1: True
flag2: !bool "{{ flag1 }}"
flag3: !bool "0"
$ uw config realize -i input.yaml --output-format yaml
flag1: True
flag2: True
flag3: False

!datetime

Converts the tagged node to a Python datetime object. For example, given input.yaml:

date1: 2024-09-01
date2: !datetime "{{ date1 }}"
$ uw config realize -i input.yaml --output-format yaml
date1: 2024-09-01
date2: 2024-09-01T00:00:00

The value provided to the tag must be in ISO 8601 format to be interpreted correctly by the !datetime tag.

!dict

Converts the tagged node to a Python dict value. For example, given input.yaml:

d1: {'k0': 0, 'k1': 1, 'k2': 2}
d2: !dict "{ k0: 0, k1: 1, k2: 2 }"
d3: !dict "{{ '{' }}{% for n in range(3) %} k{{ n }}:{{ n }},{% endfor %}{{ '}' }}"
d4: !dict "[{% for n in range(3) %}[k{{ n }},{{ n }}],{% endfor %}]"
$ uw config realize -i input.yaml --output-format yaml
d1: {'k0': 0, 'k1': 1, 'k2': 2}
d2: {'k0': 0, 'k1': 1, 'k2': 2}
d3: {'k0': 0, 'k1': 1, 'k2': 2}
d4: {'k0': 0, 'k1': 1, 'k2': 2}

!extend

Extends an existing list in a base config with additional items. For example, given input.yaml:

values:
- 1
- 2

and update.yaml:

values: !extend
- 3
- 4
$ uw config realize -i input.yaml --update-file update.yaml
values:
- 1
- 2
- 3
- 4

Note

There are use cases where !extend is the ideal tool, but it also has significant limitations:

  • It must be applied to a literal YAML sequence.

  • It must be applied via a second, updating config, either via uw config realize with --update-file specified, as shown above, or via uw config compose.

  • The value-to-extend in the base config must exist at the same key path as the value tagged !extend in the updating config.

  • The value-to-extend in the base config must be a literal YAML sequence; in particular, it cannot be a tagged Jinaj2 expression like !list '{{ x }}' that will only eventually be a extendable sequence.

Tip

In light of these limitations, note that Jinja2 expressions combining and slicing Python list objects may address a larger set of use cases. They can be used within a single config, can reference values at arbitrary key paths, can prepend as well as append elements, can insert items at abitrary positions, and can even remove items. For example:

Listing 11 a.yaml
lo: [1, 2]
hi: [7, 8]
numbers: [3, 5, 6, 42]
final: !list '{{ lo + numbers[:1] + [4] + numbers[1:3] + hi }}'
$ uw config realize -i a.yaml --key-path final
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8

!float

Converts the tagged node to a Python float value. For example, given input.yaml:

f2: !float "{{ 3.141 + 2.718 }}"
$ uw config realize -i input.yaml --output-format yaml
f2: 5.859

!glob

Only for use in File Blocks. See Glob Support for more information.

!include

Load and parse the files specified in the tagged sequence value and insert their contents here. For example, given numbers.yaml:

values: !include [constants.yaml]

and constants.yaml:

e: 2.718
pi: 3.141
$ uw config realize -i numbers.yaml --output-format yaml
values:
  e: 2.718
  pi: 3.141

Values from files later in the sequence overwrite their predecessors, and full-value replacement, not structural merging, is performed. For example, given numbers.yaml:

values: !include [e.yaml, pi.yaml]

e.yaml:

constants:
  e: 2.718

and pi.yaml:

constants:
  pi: 3.141
$ uw config realize -i numbers.yaml --output-format yaml
values:
  constants:
    pi: 3.141

!int

Converts the tagged node to a Python int value. For example, given input.yaml:

f1: 3
f2: 11
f3: !int "{{ (f1 + f2) * 10 }}"
f4: !int '{{ leadtime.total_seconds() / 3600 }}'
$ uw config realize -i input.yaml --leadtime 6
f1: 3
f2: 11
f3: 140
f4: 6

!list

Converts the tagged node to a Python list value. For example, given input.yaml:

l1: [1, 2, 3]
l2: !list "[{% for n in range(3) %} a{{ n }},{% endfor %} ]"
l3: !list "[ a0, a1, a2, ]"
$ uw config realize -i input.yaml --output-format yaml
l1: [1, 2, 3]
l2: ['a0', 'a1', 'a2']
l3: ['a0', 'a1', 'a2']

!remove

Removes the tagged YAML key-value pair. For example, given input.yaml:

e: 2.718
pi: 3.141

and update.yaml:

e: !remove
$ uw config realize -i input.yaml --update-file update.yaml --output-format yaml
pi: 3.141

!timedelta

Converts the tagged node to a Python timedelta object. For example:

base: !datetime 2025-07-31T12:00:00
offset: !timedelta 6
cycle: !datetime "{{ base + offset }}"
$ uw config realize -i input.yaml --output-format yaml
base: 2025-07-31T12:00:00
offset: !timedelta '6:00:00'
cycle: 2025-07-31T18:00:00

The value provided to the tag may be an integer number of hours, or a string of the form hours[:minutes[:seconds]], where the hours, minutes, and seconds components are (possibly zero-padded) integers.