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: a CLI command
Examples

Example: a CLI command

Turn a typed function into an `lsd csvkit stats` CLI command with @tool.command — required args become positionals, defaults become options.

TL;DRDecorate a (ctx, ...) function with @tool.command and its signature becomes a subcommand — zero argparse. A parameter with no default is a positional argument, one with a default is an --option, and a bool is a --flag. It runs as `lsd <tool> <name>`.

Every package can add CLI subcommands straight from a typed function — no argparse. Decorate a (ctx, ...) function with @tool.command and it runs as lsd <tool> <name>.

The code#

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

tool = Tool("csvkit", label="CSV Kit")


@tool.command("stats", help="print row/column counts and a null report for a CSV")
def stats(ctx: Context, src: str, sort_by: str = "") -> int:
    """`lsd csvkit stats data.csv [--sort-by col]` — quick stats without the desktop."""
    t = Table.read_csv(src)
    if sort_by:
        t = t.sort(sort_by)
        print(f"{len(t)} rows x {len(t.columns)} columns: {t.columns}")
        t.null_report().print()
        return 0

What each line does#

  • @tool.command("stats", help=...) — registers a subcommand named stats. The help string shows in lsd csvkit --help.
  • ctx: Context — the injected Context; it does not become a CLI argument. A command may read host services through it (project, ui, engine).
  • src: str — no default, so it becomes a required positional argument.
  • sort_by: str = "" — a default, so it becomes an --sort-by option. (A bool parameter would become a --flag.) The type annotation drives parsing.
  • print(...) — CLI command handlers are one of the few places print is the right call (not logging). Return an int exit code.

Run it#

From a shell, once the package is installed or added to your project:

bash
lsd csvkit stats people.csv
# → 3 rows x 2 columns: ['name', 'age']
# column nulls pct
# name 0 0
# age 0 0

lsd csvkit stats people.csv --sort-by age
# → same stats, rows sorted by age first

@tool.command returns the function unchanged, so you can also call it directly in a test with a stub context:

Python
from lsdtools import Context

stats(Context.testing(), "people.csv")
# → prints the stats; returns 0

Try changing it#

  • Add limit: int = 20 (an --limit option) and print t.head(limit).
  • Add a to_parquet: bool = False flag that writes src out as Parquet when set.
  • Add a second command (@tool.command("head", ...)) — each typed function is its own subcommand.

LearnCommands & actions

APITool · Context · CLI commands

ExamplesA dockable panel · A complete package

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