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:
- Recursively generates child XAML from
spec.get("children", []) - Calls
_auto_dispatchon the generator function, injecting the child body asbody_contentandbody_sequence_idref - Optionally creates a new
ScopeGuidwhennew_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_activitiesin_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.