All docs
Parameters vs ports
The one classification rule behind every LSD node — how a step's parameters split into input ports, injected context, and form fields, all checked at import.
Every @tool.* step is a plain function. LSD reads its signature and sorts each parameter into
one of four kinds — by its annotation, not its name — to decide what becomes a wire, what gets
injected, and what becomes a form field.
Usage#
from lsdtools import Tool, Table, StepContext
tool = Tool("demo")
@tool.shape
def clip(t: Table, lo: float, hi: float = 1.0, ctx: StepContext = None) -> Table:
ctx.progress(0.5, "clipping")
return t.filter((t["v"] >= lo) & (t["v"] <= hi))
# → t: input PORT · lo: required FIELD · hi: optional FIELD · ctx: INJECTED
The classification rule#
| Annotation | Becomes | Notes |
|---|---|---|
Table / pa.Table |
an input port | wired from an upstream node; a load step may have none |
Source |
an injected reader | @tool.load only; bound to the file/query the user picks |
StepContext |
injected capabilities | progress, cancel, scratch, emit; excluded from the cache key |
anything else (int, str, bool, float, …) |
a typed parameter | a form field; no default ⇒ required, default ⇒ optional |
The annotation is the whole signal. Rename t to left and it is still a port; drop the Table
annotation off it and it becomes a plain field.
Ports: Table and pa.Table#
A parameter annotated Table (or a raw pyarrow.Table) is an input port —
an edge you wire from an upstream node on the canvas, or pass positionally in Python:
@tool.shape
def merge(a: Table, b: Table) -> Table: # two ports
return a.join(b, on="id")
merge(load_a(), load_b()) # wire both ports
A load step is the source of data, so it may have no ports — see Load steps. Shape and deliver steps have one or more.
Injected: StepContext (and why not Context)#
A StepContext parameter is filled in by the engine at run time — never by the
user, and never part of the cache key. Use it for ctx.progress(...), ctx.cancelled,
ctx.set_output(...), ctx.emit(...), and ctx.output_dir / ctx.scratch_dir.
A Context (the richer object exposing live project state) is rejected on a step at import:
@tool.shape
def bad(t: Table, ctx: Context) -> Table: # TypeError at import!
...
# → "a step must not read Context … use StepContext for progress/cancel/scratch"
A step must stay pure so the cache can trust it; Context is for
commands, actions and views, not steps.
Everything else is a parameter#
Any other annotation is a typed parameter that renders as a form field on the node. The type
drives the widget (int / float → number, bool → checkbox, str → text). A parameter with
no default is required; a parameter with a default is optional:
@tool.load
def rows(n: int, seed: int = 0) -> Table: # n required, seed optional
...
To go beyond a plain auto-generated field — a form with its own buttons, or a custom editor — a tool
contributes a view: a View of param() fields and @action
buttons the desktop docks beside the node. A domain view can also ride on a single parameter via
Annotated[...] — see the file loader.
Checked at import time#
All of this is validated when the module is imported (when the package loads), so a mistake never waits until someone clicks the node. The three guards:
- a
Tableport on@tool.load→ "a load step takes no Table input ports"; - a
Sourceon@tool.shape/@tool.deliver→ "a Source parameter is only valid on @tool.load"; - a
Contexton any step → "a step must not read Context … use StepContext".
Fix the signature and the whole package loads clean.
Related#
Learn — The Tool object · Tables & data flow · Load steps
Examples — Your first tool · Live node metrics
Frequently asked questions
Why is my Source parameter rejected on a shape step?
A Source produces data, so it is only valid on @tool.load. On a shape or deliver step it raises a TypeError at import time. Wire a Table input port from an upstream load node instead.
Why can't I annotate a step parameter Context?
A step must stay pure so the cache can trust it. A Context exposes live project state the cache can't see, so it is rejected at import. Use StepContext for progress, cancel, scratch and emit; use Context only in commands, actions and views.