YAML Configuration

PyRogue supports configuration and state import/export through YAML APIs on Root and through subtree export on Node.

These APIs are the standard way to capture a known-good configuration, restore it later, or inspect the current tree as structured YAML rather than as interactive Variables.

Typical YAML Workflows

  • Configuration baselines for known-good startup values

  • Captured runtime state for debug and issue reproduction

  • Status snapshots for validation and operator handoff

Main Entry Points

The main YAML entry points are:

  • Node.getYaml(...) to export one subtree as YAML text.

  • Root.saveYaml(...) to write YAML to a file, with optional auto-naming.

  • Root.loadYaml(...) to read YAML from one or more files or directories.

  • Root.setYaml(...) to apply YAML text directly.

These APIs also back the built-in hidden Root commands:

  • SaveConfig / LoadConfig

  • SaveState

  • SetYamlConfig / GetYamlConfig / GetYamlState

The command wrappers mainly pre-select useful defaults for modes and group filters.

Configuration Vs State Filters

Typical defaults:

  • Config operations use modes ['RW', 'WO'] and exclude NoConfig

  • State operations use modes ['RW', 'RO', 'WO'] and exclude NoState

That means:

  • Read-only Variables are normally ignored for config load

  • Commands are ignored by YAML set/load (BaseCommand._setDict is a no-op)

This is why configuration YAML usually reads like “things you can set”, while state YAML reads like “everything worth observing”.

Saving YAML

Node.getYaml(...) and Root.saveYaml(...) both serialize current tree values using the same underlying tree-dictionary path.

Important export options:

  • readFirst=True performs a Root-wide read before export.

  • modes controls which Variable access modes are included.

  • incGroups and excGroups apply the same group-filtering model used elsewhere in the tree.

  • recurse on getYaml(...) controls whether child Devices are included.

saveYaml(...) can either write to a named file or auto-generate a timestamped filename. If the target name ends in .zip, it writes the YAML payload into a compressed zip member.

Loading And Applying YAML

Root.loadYaml(...) accepts more than a single file path. In the current implementation, the input can be:

  • A single .yml or .yaml file.

  • A directory, in which case all .yml and .yaml files in that directory are loaded in sorted order.

  • A zip-file subdirectory path.

  • A Python list of those entries.

  • A comma-separated string of entries.

Root.setYaml(...) applies YAML text directly without going through the filesystem.

For loadYaml(..., writeEach=False) and setYaml(..., writeEach=False), the application workflow is:

  1. Parse YAML input to ordered dictionaries.

  2. Traverse the tree and assign Variable display values with setDisp(..., write=False).

  3. After the full YAML payload is staged in shadow state, run the normal Root configuration-commit path through Root._write().

Implications:

  • If a Variable is assigned more than once while loading, the last assignment wins in shadow memory before commit

  • Hardware is not touched until the bulk commit step

If writeEach=True, each setDisp write is issued immediately while YAML is being traversed (no final consolidated Root._write() pass).

The load path is wrapped in both Root.pollBlock() and Root.updateGroup() so polling does not race with the configuration operation and listeners see a coalesced update batch.

The staged values are then committed using the normal tree transaction path. For the bulk write, verify, read, and check model behind that commit step, see Device Block Operations.

Practical Guidance

  • Separate baseline configuration from transient runtime state.

  • Track YAML files in source control when they represent release artifacts.

  • Document version compatibility when tree names or meaning change.

  • Prefer writeEach=False for coordinated configuration loads unless you specifically need immediate per-entry writes.

Array Matching And Slicing In YAML Keys

YAML load supports array-style Node matching:

  • AmcCard[0]:

  • AmcCard[1:3]:

  • AmcCard[:]:

  • AmcCard[*]:

Examples:

Root:
  AmcCard[:]:
    DacEnable[0]: True

  AmcCard[1:3]:
    DacEnable: True

The matching rules follow the same array-aware nodeMatch() behavior used by the tree:

  • [0] selects one element.

  • [:] and [*] select all elements.

  • [1:3] uses Python-style slice semantics, so it selects elements 1 and 2.

Array Variables can also be targeted at the Variable level, for example with DacEnable[0].

Related settings on Root:

  • ForceWrite: force writes of non-stale Blocks in bulk config paths

  • InitAfterConfig: call initialize() after config apply

What To Explore Next