Control flow: subflows, Map, Parallel, recursion, start/end¶
States you don’t put in mock_mapping are handled by the built-in StandardFlowStrategy, which
understands the composite states and recurses into them. This page covers how that works and how
to control it.
Subflows (nested state machines)¶
A Task with resource arn:aws:states:::states:startExecution.sync:2 starts another state
machine. The toolkit runs that nested machine in the same local loop and injects its output
back into the parent step — so a multi-machine pipeline runs end-to-end locally.
Register each nested machine in asl_registry keyed by the name of the state that starts it:
runner = WorkflowRunner(
role_arn=role_arn,
asl_registry={
"main": parent_definition,
# the parent has a startExecution task state named "child_flow":
"child_flow": child_definition,
},
mock_mapping=mock_mapping,
)
If a startExecution step’s name isn’t found in the registry, you get a clear error listing the
available keys.
Map states¶
For a Map state, StandardFlowStrategy runs the ItemProcessor once per item. By default the
items are the state’s input when it’s a list, otherwise input["items"].
When the items live somewhere else in the input, subclass AbstractMockMapResponseStrategy and
implement get_items:
from aws_stepfunctions_toolkit import AbstractMockMapResponseStrategy
class SamplesMap(AbstractMockMapResponseStrategy):
def get_items(self, input_data):
return input_data["samples"]["Payload"]["body"]
mock_mapping = {"Map - Samples": SamplesMap(), ...}
The strategy uses test_state to apply the Map’s ItemSelector to each item, then runs each
through the ItemProcessor.
Parallel states¶
For a Parallel state, each branch is run through the local loop and the results are collected
into a list, mirroring Step Functions’ output shape.
Recursion and depth¶
Subflows, Maps and Parallels can nest to any depth — each composite state recurses back into the
same run_sub_machine loop. As it descends, the runner tracks a parent path so steps deep
inside the tree can be addressed precisely.
Hierarchical keys¶
Strategy lookup tries the most specific key first, then the bare name:
"<parent_path>/<state_name>"— e.g."child_flow/example_batch_2""<state_name>"
So a plain "example_batch_2" key applies to that state wherever it appears, while
"child_flow/example_batch_2" targets only the occurrence inside the child_flow subflow. The
parent path segments are the names of the enclosing subflow / Map / Parallel states.
Running a sub-range¶
runner.start runs the whole "main" machine by default, but you can run just part of it:
runner.start(initial_input, start="Parallel") # begin at a specific state
runner.start(initial_input, start="A", end="C") # stop after state "C"
start— the state to begin at (instead of the definition’sStartAt).end— the state after which to stop (its output is returned).
This is handy for reproducing a failure from the middle of a long pipeline without re-running the earlier steps — pair it with mocked inputs captured from a real run (see CLI & history).