Container Generators - cprima-forks/uipath-ai-skills GitHub Wiki

Container Generators

Generators fall into two categories: leaf generators that emit a single self-contained XAML element, and container generators that wrap a body of child activities.

Leaf Generators

Leaf generators receive args, produce a single XAML string, and return. They are auto-dispatched via _auto_dispatch. Examples: ntypeinto, nclick, log_message, assign, read_range.

Container Generators

Container generators receive the full spec dict (not just args), because they need to recursively generate child activity XAML. They are marked container=True in _REGISTRY and their fn is called with (spec, args, scope_id, counter, indent) directly — bypassing _auto_dispatch.

There are two patterns for implementing containers:

Factory Pattern: _make_simple_container_handler

For containers where the child structure is straightforward (one children list, wrapped in a Sequence), a factory generates the handler:

_handle_foreach_row = _make_simple_container_handler(gen_foreach_row, "ForEachRow")
_handle_retryscope  = _make_simple_container_handler(gen_retryscope,  "RetryScope")

The factory:

  1. Recursively generates child XAML from spec.get("children", [])
  2. Calls _auto_dispatch on the generator function, injecting the child body as body_content and body_sequence_idref
  3. Optionally creates a new ScopeGuid when new_scope=True

new_scope=True is used for the NApplicationCard family — each card gets its own UUID that all its child activities reference via ScopeIdentifier.

Manual Handlers

Complex containers require manual handlers because their child structure is non-trivial:

Generator Complexity
try_catch Separate try/catch/finally branches; each catch has its own exception type and nested ActivityAction structure
if Two named branches: then_children, else_children
if_else_if Unpacks conditions[] array from args, each with its own children
switch Unpacks cases[] from args; each case gets x:Key attribute
ncheckstate Two optional branches: if_exists_children, if_not_exists_children

_ALL_CHILD_KEYS

A single module-level tuple is the authoritative list of all JSON keys that can contain child activity lists:

_ALL_CHILD_KEYS = (
    "children", "try_children", "then_children", "else_children",
    "finally_children", "default_children",
    "if_exists_children", "if_not_exists_children"
)

This tuple is used in three places:

  • _walk_all_activities — pre-generation tree walk for namespace detection and variable type inference
  • _generate_activity — recursive descent during generation
  • _validate_activities in _wf_validation.py — spec validation

switch cases, if_else_if conditions, and try_catch catches are handled separately (they live inside args, not as top-level spec keys) and are unpacked explicitly in those locations.