Live demo — data resets daily at 03:00 UTC. Nothing you enter is saved. Server UI →

product: maestro audience: test-developer authority: normative

Local Debugging with a Python Runner Script

The recommended pattern for local Python debugging is a run_local.py script at the package root that simulates the Maestro execution model — setup → steps → teardown in order, evaluates measurements, and respects terminate-on-fail and run_on_abort.

pytest IS NOT used because it does not model Maestro execution concepts (no terminate-on-fail semantics, no run_on_abort guarantee, no measurement evaluation).

Anatomy of run_local.py

Place it at the package root alongside package.json. It imports the test module directly via sys.path:

import sys
sys.path.insert(0, "python_modules")
import esh10000121_tests as tests

Local settings — values Maestro normally injects, declared as defaults overridable via CLI arguments:

_DEFAULTS = {
    "host":             "http://agent64.local:5000",  # cfg.AGENT_HOST
    "product_id":       "ESH10000121",                # exec.tag.ProductId
    "product_revision": "1",                          # exec.tag.ProductRevision
    "serial_number":    "DEV-001",                    # exec.serial_number
}

Step helper — models the key YAML execution control flags:

aborted = False

def step(name, fn, *, args=None, meas_specs=None,
         terminate_on_fail=False, run_on_abort=False,
         retries=0, retry_delay_s=0.0):
    global aborted
    if aborted and not run_on_abort:
        print(f"  - [SKIP   ] {name}  (aborted)")
        return
    # invoke fn, evaluate meas_specs, print result, set aborted on fail

Test sequence mirrors the YAML:

# SETUP
step("Connect to Agent",   tests.connect, args={"host": host},
     retries=2, retry_delay_s=1.0, terminate_on_fail=True)
step("Load Forwarder",     tests.load_forwarder, terminate_on_fail=True)

# STEPS
step("Scan I2C",           tests.scan_i2c,       meas_specs=_I2C_MEASUREMENTS,
     terminate_on_fail=True)
step("Test Interconnect",  tests.test_interconnect, meas_specs=_MPIO_MEASUREMENTS)

# TEARDOWN
step("Disconnect",         tests.disconnect, run_on_abort=True)

Measurement specs can be generated programmatically:

_I2C_MEASUREMENTS = [
    (f"I2C_ADDR_0x{a.upper()}", f"i2c_addr_0x{a.upper()}", "true")
    for a in ["10", "11", "20", "50", "51"]
]

CLI usage:

python run_local.py
python run_local.py --host http://agent72.local:5000 --serial SN-12345

Exit code 0 = PASS, 1 = FAIL.

What the script does NOT model

YAML feature Not modelled because
timeout_ms: per step Thread-safe per-call timeout adds complexity
force: pass/fail/skip Development override — not needed locally
precondition: expressions Write a separate branch in the step function
repeat: loops Write a loop in the step function
MES routing / exec.tag.* Simulated by CLI arguments
An unhandled error has occurred. Reload 🗙

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.