Disabled Nodes¶
Hosts often need to temporarily skip part of a graph — to debug a
failing step in isolation, to render a “what would the rest of the
pipeline look like without this node” preview, or to A/B test two
arrangements without re-typing them. BioImageFlow exposes this through
a per-node enabled flag.
Toggling enabled¶
Three equivalent surfaces:
node.disable() # node-level
node.enable()
wf.disable(node_or_name, ...) # workflow-level, varargs
wf.enable(node_or_name, ...)
sess.set_enabled("node", False) # session-level (wire-format edit)
sess.set_enabled("node", True)
The session form mirrors the wire-format clean-form rule (see
Wire Format): re-enabling strips the enabled key entirely.
Skip propagation¶
A disabled node — and every node transitively downstream of it — is
skipped during execution. Workflow.plan() reflects this with
NodePlanStatus.SKIPPED (see Planning and Cache State); the
logical_signature of skipped nodes is the empty string.
Skipping does not cascade to parents. A disabled leaf node leaves its ancestors untouched.
The enabled flag is not part of result identity¶
Re-enabling a previously disabled node hits the existing cache — no recompute. The flag affects scheduling, not the per-node identity. This is an intentional design choice: toggling visibility on a node should not invalidate its cached output.
compute_steps¶
For hosts that need fine-grained control over execution
(stepping through nodes manually, gating compute on UI events),
Workflow.compute_steps
yields a NodeStep for every node in
topological order — including skipped ones:
for step in wf.compute_steps(targets):
print(step.node_name, step.skipped)
if not step.skipped:
step.execute()
Each step exposes:
step.node_name— scoped name (matchesNodePlan.node_name).step.skipped— bool;Truewhen disabled or downstream of a disabled node.step.execute()— runs the node and returns the resulting DataFrame; raisesDisabledNodeErrorwhen called on a skipped step. The host typically branches onstep.skippedrather than catching the exception.
Calling step.execute() is mandatory before advancing to the next
step (the engine checks); skipping it leaves the workflow in an
inconsistent state.
How compute() handles skipped targets¶
Workflow.compute() raises
DisabledNodeError only when all
requested targets are skipped. Multi-target calls silently omit
skipped targets from the result and return DataFrames for the
executable ones:
wf.compute(a, b) # a is skipped, b is not — returns just b's result
wf.compute(a) # a is skipped — DisabledNodeError
This matches the host’s expectation that toggling one node off should not break a multi-target compute.
Wire format¶
The enabled flag round-trips through the wire format as
"enabled": false on disabled nodes; the key is omitted for
enabled nodes. See Wire Format.
Worked example: a “skip this node” toggle¶
A GUI exposes a per-node “skip” checkbox. The handler updates the session, re-validates, and re-renders the cache plan:
def on_toggle_skip(name: str, skip: bool):
sess.set_enabled(name, not skip)
errs = sess.validate()
plan = sess.plan() # SKIPPED entries propagate
render(sess, errs, plan)
Because set_enabled is non-structural (see WorkflowSession),
the toggle does not re-resolve any tool class — even on a workflow with
hundreds of nodes.