Sandbox — LSD is in active testing. All systems operational·payments are test-mode (no real charges).
Overview Use Cases News Docs Tool Store Support Community Get LSD
All docs
Docs/ Examples/ Example: drag-drop import
Examples

Example: drag-drop import

A drag-drop import template — dropping a .csv builds a pre-wired entity via @tool.template and an @tool.entity builder function.

TL;DRA @tool.template registers a file type (extensions=[".csv"]); its build function (ctx, files, name=None) returns a ready-to-run Entity by calling an @tool.entity builder's .build(...). Drop a file on the desktop and a whole pipeline appears, already wired.

The nicest way to start a pipeline is to drop a file on the canvas and have a whole, wired entity appear. A @tool.template maps a file extension to a build function that returns a pre-wired entity.

The code#

Python
# tool.py
from lsdtools import Tool, Context, Source, Entity, Table

tool = Tool("cloud", label="Point Cloud")


@tool.load(source_kinds=("file",))
def read_points(src: Source) -> Table:
    """Read a points file (needs x, y, z columns)."""
    return src.read()


@tool.shape
def near_origin(t: Table, radius: float = 100.0) -> Table:
    """Keep points within `radius` of the origin on the XY plane."""
    r2 = t["x"] * t["x"] + t["y"] * t["y"]
    return t.filter(r2 <= radius * radius)


@tool.entity(label="Point cloud", icon="lsd-folder-symbolic")
def point_cloud(path: str = "", radius: float = 100.0) -> Table:
    """An entity that reads a file, then trims it to points near the origin."""
    src = read_points(Source.file(path))
    return near_origin(src, radius=radius)


@tool.template("point-csv", label="Point cloud (CSV)", extensions=[".csv"])
def import_points(ctx: Context, files, name=None) -> Entity:
    """Dropped .csv → a pre-wired Point cloud entity."""
    return point_cloud.build(name=name or files.main.stem, path=str(files.main))

What each line does#

  • @tool.entity point_cloud — a builder function: its signature (path, radius) becomes the entity's declarative parameters, and its body runs once at creation to wire the child steps. It returns the terminal step; LSD collects that step and everything upstream of it.
  • @tool.template("point-csv", ..., extensions=[".csv"]) — registers the drop target. The build function is (ctx, files, name=None) -> Entity.
  • files.main — the dropped file as a Path (files["role"] addresses a specific role for multi-file templates). files.main.stem gives a sensible default entity name.
  • point_cloud.build(name=..., path=...) — creates the entity of that type, passing the template's values as its parameters. Because the builder wires read_points → near_origin, the returned entity is ready to run.

Run it in Python#

The graph the template builds — run the same wiring directly to see the result:

Python
from lsdtools import Engine, Source, Table

Table({"x": [0.0, 200.0], "y": [0.0, 0.0], "z": [1.0, 2.0]}).write_csv("cloud.csv")

pts = near_origin(read_points(Source.file("cloud.csv")), radius=100.0)
Engine().run(pts).output.print()
# → x y z
# 0 0 1 (the point 200 m out is dropped)

And the template/entity path itself — what a drop triggers:

Python
entity = point_cloud.build(name="cloud", path="cloud.csv") # what import_points returns
Engine().add(entity).run("cloud") # runs the pre-wired pipeline

Try changing it#

  • Add roles=[FileRole("collar"), FileRole("survey")] (from lsdtools.advanced) for a multi-file drop, and read them with files["collar"] / files["survey"].
  • Give the template a sniff= function so it only claims CSVs whose header has x, y, z.
  • Set priority= so your template wins over another that also handles .csv.

LearnEntity types · Commands & actions · Load steps

APITool · Context · Source

ExamplesFile loader · A complete package

By LSD Team · Last updated Jul 17, 2026 Ask on Discord View as Markdown