Skip to content

Variable Scoping in Nested Generates

Moving a <variable> from an outer <generate> into a nested <generate> changes which record owns the value. Keep that ownership visible in every script= expression so a reviewer can tell whether the current or parent record is intended.

Review Rules

  • Use this.<name> for a value declared in the current nested generate.
  • Use this.parent.<name> when the direct parent owns the value.
  • Use an explicit named path only when the model intentionally addresses a previously built nested branch.
  • Use root.<name> for a value owned by the setup scope.
  • Read memstore data through the public Scripting API. Do not imitate a memstore record with a locally constructed dict just to move it between scopes.

Quick Decision Guide

You want to read... Preferred form
A value in the current nested <generate> this.varName
A value in the direct parent <generate> this.parent.varName
A setup value root.varName
An intentional named branch child.grand_child.varName

An unqualified name can still resolve to an outer value. That is concise at the top level, but ambiguous when the same name exists in more than one nested generate.

Example 1: Memstore Values Through the Scripting API

The helper keeps memstore communication behind the supported read-only API:

script/scope_memstore.scr.py
1
2
3
4
5
from datamimic_ee.scripting import load_memstore


def first_memstore_value(product_name: str):
    return load_memstore(product_name)[0]["value"]

A parent-owned value

scope-outer.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<setup>
    <execute uri="script/scope_memstore.scr.py"/>

    <generate name="outer_values" count="1" target="mem">
        <key name="value" constant="outer"/>
    </generate>

    <generate name="batch" count="1">
        <variable name="selected" script="first_memstore_value('outer_values')"/>

        <generate name="datFileCreation" count="1">
            <key name="selected" script="this.parent.selected"/>
        </generate>
    </generate>
</setup>

selected belongs to batch, so the nested generate reads it explicitly from this.parent.

Current and parent values with the same name

scope-shadowing.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<setup>
    <execute uri="script/scope_memstore.scr.py"/>

    <generate name="outer_values" count="1" target="mem">
        <key name="value" constant="outer"/>
    </generate>
    <generate name="inner_values" count="1" target="mem">
        <key name="value" constant="inner"/>
    </generate>

    <generate name="batch" count="1">
        <variable name="selected" script="first_memstore_value('outer_values')"/>

        <generate name="datFileCreation" count="1">
            <variable name="selected" script="first_memstore_value('inner_values')"/>

            <key name="selected_current" script="this.selected"/>
            <key name="selected_parent" script="this.parent.selected"/>
            <key name="selected_named" script="datFileCreation.selected"/>
        </generate>
    </generate>
</setup>

The three expressions make their ownership reviewable: this.selected and datFileCreation.selected read inner; this.parent.selected reads outer.

Example 2: Cascade Generate and this.id

scope-cascade.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<setup>
    <generate name="cascade_generate_test" count="1">
        <variable name="id" generator="IncrementGenerator"/>
        <key name="outer_id" script="id"/>

        <generate name="first_inner_generate" count="2">
            <variable name="id" generator="IncrementGenerator"/>
            <key name="first_inner_id" script="this.id"/>

            <generate name="second_inner_generate" count="2">
                <variable name="id" generator="IncrementGenerator"/>
                <key name="second_inner_id" script="this.id"/>

                <generate name="third_inner_generate" count="2">
                    <variable name="id" generator="IncrementGenerator"/>
                    <key name="third_inner_id" script="this.id"/>
                </generate>
            </generate>
        </generate>
    </generate>
</setup>

The outer id is unqualified because it is at the top level. Every nested id uses this.id, so reusing the same name cannot hide which value is read.

Example 3: Named Path Access

scope-named-path.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<setup>
    <generate name="parent" count="2">
        <variable name="count_global" script="2"/>

        <generate name="child" count="{count_global}">
            <variable name="idx" generator="IncrementGenerator"/>
            <key name="child_id" script="child.idx"/>

            <generate name="grand_child" count="{child.idx}">
                <variable name="index" generator="IncrementGenerator"/>
                <key name="grand_child_id" script="child.grand_child.index"/>
                <key name="parent_idx" script="child.idx"/>
            </generate>
        </generate>
    </generate>
</setup>

Use a named path when the path itself matters to the model. For a merely local value, this.index remains easier to review.

Example 4: Explicit Paths in nestedKey

scope-nested-key.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<setup>
    <generate name="customer" count="1">
        <generate name="data" count="1">
            <nestedKey name="send_info" type="list" count="2">
                <variable name="email" constant="[email protected]"/>
                <variable name="sms" constant="+49123456789"/>

                <nestedKey name="contact" type="list" count="3">
                    <key name="email" script="data.send_info.email"/>
                    <key name="sms" script="data.send_info.sms"/>
                </nestedKey>
            </nestedKey>
        </generate>
    </generate>
</setup>

The path shows that contact deliberately reuses values owned by send_info.

Example 5: Shared Outside, Local Inside

scope-shared-local.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<setup>
    <generate name="batch" count="1">
        <variable name="customer_id" constant="C-1000"/>

        <generate name="invoice" count="1">
            <variable name="lineIndex" generator="IncrementGenerator"/>
            <key name="customer_id" script="this.parent.customer_id"/>
            <key name="invoice_line" script="this.lineIndex"/>
        </generate>

        <generate name="deliveryNote" count="1">
            <variable name="lineIndex" generator="IncrementGenerator"/>
            <key name="customer_id" script="this.parent.customer_id"/>
            <key name="delivery_line" script="this.lineIndex"/>
        </generate>
    </generate>
</setup>

The shared customer ID stays in the parent. Each child keeps its own local line counter and qualifies both reads.

Troubleshooting

Symptom Likely cause Fix
variable not found The value is owned by another scope Use this.<name>, this.parent.<name>, or the intentional named path
Wrong value without an error An unqualified name resolved to an outer value Qualify current and parent ownership explicitly
A path breaks after nesting changes The named branch changed Prefer this.* for local values; reserve named paths for intentional branch access
Memstore logic exposes runtime objects The model bypasses the public boundary Move the read to datamimic_ee.scripting.load_memstore() or load_memstore_page()