Module Lifecycle¶
A node, group, or graph (a module) goes through the following steps in its lifetime:
Definition: The module type is defined in Python code which the Python interpreter reads into memory.
Construction: We construct an instance of the module. If the module is a graph, then the user explicitly constructs a graph like
graph = MyGraph(). Otherwise, a module is implicitly constructed by its containing graph when that graph is constructed.Setup: As a LabGraph graph is starting up,
setup()is called on every module. If a group contains some other modules, the group’ssetup()will always be called before those modules’setup()s are called. During thesetup()call, the module can prepare itself for execution. If the module is a group, then it can also configure the modules it contains - see Configuration.Execution: When the graph is ready, LabGraph will begin execution of all modules simultaneously.
Cleanup: When all modules have terminated, LabGraph will run
cleanup()on every module.cleanup()will be called in the same order thatsetup()was called.
Configuration¶
LabGraph provides a way to concisely specify configuration that is given to a graph and forwarded to its descendant nodes. We start by defining a configuration type:
class MyNodeConfig(df.Config):
num_trials: int
participant_name: str
Config classes actually just Message classes, except that they also provide special utilities for configuring graphs, as you’ll see.
We specify the configuration for a Node, Group, or Graph by giving it a config type annotation:
class MyNode(df.Node):
config: MyNodeConfig
...
Then we can configure it by calling configure() on it. We configure a node or group in the setup() method of its containing group:
class MyGroupConfig(df.Config):
...
class MyGroup(df.Config):
config: MyGroupConfig
MY_NODE: MyNode
def setup(self) -> None:
# Cascade config to MY_NODE based on the
# group's config
my_node_config = MyNodeConfig(...)
self.MY_NODE.configure(my_node_config)
The exception to this is graphs, which we can construct and configure directly:
my_graph = MyGraph()
my_graph.configure(MyGraphConfig(...))
Rather than hard-coding the top-level configuration like this, though, we can automatically build the configuration from from command-line arguments like so:
my_config = MyGraphConfig.fromargs()
The df.run function actually gets configuration using fromargs(), so we can also just run a graph using command-line arguments like so:
df.run(MyGraph)