Pipes#
Rendering lifecycle recovery#
Widget state can arrive out of order, and an overloaded Jupyter server can drop a state update. The browser therefore reports a stale pipe or viewer instead of waiting indefinitely; the kernel re-emits the relevant widget state with bounded exponential backoff. This is a recovery handshake, not a rendering timeout: it stops as soon as the browser has a renderable source.
sequenceDiagram
participant K as Kernel
participant B as Browser
K->>B: widget state / run request
alt inlet, value, or outlet missing
B->>K: stale (which state is missing)
K->>B: re-emit endpoint and pipe/viewer state
else state is renderable
B->>B: render layout and selection
end
- class ipyelk.pipes.Pipe(*args: t.Any, **kwargs: t.Any)#
A step in the processing pipeline for diagrams.
Attributes#
- inlet:
MarkElementWidget input elements to manipulate.
- outlet:
MarkElementWidget output elements that potentially have been manipulated.
- observed: tuple of
str which potential changes that would require this pipe to be rerun.
- reports: tuple of
str types of changes that get added to the output based of rerunning this pipe.
- on_progress:
callable Callable function that is executed when the pipe is running.
- status:
PipeStatus Captures the disposition of the pipe during the change lifecycle.
- status_view:
PipeStatusView Widget to show pipe status as it updates.
- enabled: bool
whether the processing step can be run
- cancel()#
Cancel the runner and drop its pending requests;
Trueif one was alive.The only thing that cancels a run. Use it when the pipe is detached, so an in-flight answer is not persisted into an index nobody views. A task on another loop is cancelled through that loop (see
util.settle).- Return type:
bool
- check_dirty(flow=None)#
Method to test is this pipe should be run given the set of changes.
- Parameters:
flow (
tuple[str,...] |None) – the changes to test against; defaults to the pendinginlet.flow- Returns:
dirty flag
- Return type:
bool
- close()#
Cancel and close this pipe and the widgets it created.
An inlet or outlet it was given, such as the source it was connected to, is left open.
- error()#
Method to raise any potential errors captured during the running of the pipe.
- Raises:
self.status.exception – captured exception during the processing.
- async run()#
Run method that takes the input performs checks/changes, and sets the output value.
Subclasses of the pipe will implement their own custom processing logic.
- schedule_run(change=None)#
Request a run and return the task that will serve it.
Requests coalesce on the trailing edge: one runner task serves every request made while it is alive, so ten calls in one tick cost one run and a call made mid-run costs one more run after it. Nothing is cancelled here (see
cancel); aPipelinegives way at its next stage boundary (Superseded).A runner on another event loop (a kernel subshell, see
util.settle) is handed over to a new runner on the caller’s loop, so the returned task is always awaitable by the caller. Pending requests survive the hand-over.Returns
None(and schedules nothing) when no event loop is running, e.g. when a diagram is built in a plain script or a test: there is no loop to run the task on, so raising would only crash widget construction (Diagram(source=...)refreshes from a trait observer). The request still counts and is served by the next runner.- Return type:
Task|None
- superseded()#
Whether a newer request arrived while the runner’s current run is in flight (a
Pipelinechecks this between stages).- Return type:
bool
- inlet:
- class ipyelk.pipes.Pipeline(*args: t.Any, **kwargs: t.Any)#
- cancel()#
Cancel, and re-record the in-flight run’s tags now: the cancellation unwinds a loop turn later, after a new runner may already have taken.
- Return type:
bool
- check()#
Checks inlets and outlets of the pipeline and raises error is not connected
- Raises:
BrokenPipe – Disconnected pipes
- Return type:
bool- Returns:
True if no errors in pipeline
- check_dirty(flow=None)#
Method to test is this pipe should be run given the set of changes.
- Parameters:
flow (
tuple[str,...] |None) – the changes to test against; defaults to the pendinginlet.flow- Returns:
dirty flag
- Return type:
bool
- close()#
Close this pipeline, its sub-pipes and the widgets it created.
- async run(flow=None)#
Run the dirty pipes for the pending flow.
The flow is taken from the inlet at the start (
MarkElementWidget.take) and re-recorded unless the run succeeds. A source should therefore feed one pipeline: a second would find the flow already taken.flowis passed by a parent pipeline to a nested one, since the parent already took it.
- class ipyelk.pipes.base.PipeStatus(disposition=PipeDisposition.done, elapsed=None, exception=None)#
An immutable snapshot of a pipe’s disposition.
A plain value, not a widget, so replacing
Pipe.statusopens no comm. Instances compare by identity: assigning a new one always notifiesstatusobservers.
- class ipyelk.pipes.base.PipeStatusView(*args: t.Any, **kwargs: t.Any)#
Widget to display the pipe status.
Attributes#
- include_exception: bool
exception captured
- html: ipywidgets.HTML
built status HTML to display
- close()#
Close method.
Closes the underlying comm. When the comm is closed, all of the widget views are automatically removed from the front-end.
- update(pipe)#
Method to update the status given changes in the pipe.
- class ipyelk.pipes.MarkElementWidget(*args: t.Any, **kwargs: t.Any)#
A synced element tree plus the shared index and the pending
flow.valueis written from both sides. A browser write is never sent back:echo_update=Falsestops the echo, and_should_send_propertystops the re-serialized copy. Kernel writes are sent exactly once.Known limitation: a second frontend attached to the same kernel used to learn browser-written values through the echo and no longer does.
- build_index(*, assign_ids=True)#
Index
value; by default pin the resulting ids onto the elements.The index’s
Registryassigns ids to elements that have none; writing them back is what lets every later serialization – which runs outside the Registry – emit the ids the index is keyed by, sopersistcan merge what comes back from the browser. Explicit ids are never changed.assign_ids=Falseleavesiduntouched so a caller can report or reject unassigned ids first (ValidationPipe.fix_null_id).- Return type:
MarkIndex
- gen#
generation of the
runrequest the browser last answered;0with an older extension build (seeutil.wait_for_answer)
- persist(rebuild_index=False)#
Fold
valueinto the shared index.The index – not
value– is the authority for the element hierarchy: hidden elements never survive serialization (seeNode.model_dump), so the index must be merged into, never rebuilt from, a value that has been through the browser.rebuild_indexis for the full Python-side hierarchy only (initially, or after adding elements); the merge path requires every element to already carry an id.
- record(*tags)#
Add
tagsto the pendingflow(order-preserving union).Writers must record rather than assign, which would overwrite what another writer left pending.
- Return type:
tuple[str,...]
- set_trait(name, value)#
Remember which
valuecame from the browser (set_stateholds_property_lock), for_should_send_property.
- take()#
Consume the pending
flow: return it and leave()behind.Pipeline.runtakes at the start, so tags recorded mid-run stay pending for the next run.- Return type:
tuple[str,...]
- class ipyelk.pipes.ElkJS(*args: t.Any, **kwargs: t.Any)#
JupyterLab widget for calling elkjs layout given a valid elkjson dictionary
- async run()#
Run method that takes the input performs checks/changes, and sets the output value.
Subclasses of the pipe will implement their own custom processing logic.
- timeout#
Seconds to wait for the browser to return a layout before giving up; 0 waits forever (the request is re-sent with backoff until a frontend answers)