Larkspur tabular transforms, kept small

Core concepts

Larkspur has four ideas. Once they click, the rest of the API is obvious.

Frame

A Frame is a lazy sequence of rows, where each row is a plain dict. Frames are created from a source (read_csv, read_ndjson, or from_rows) and are not consumed until you iterate or call a writer.

Stream

Internally a Frame wraps a Stream — a pull-based iterator. Because rows are pulled on demand, a pipeline over a ten-gigabyte file uses the same memory as one over ten rows.

Transform

A Transform is any callable that receives one row and yields zero or more rows. The built-ins (listed here) are just conveniences around that contract.

def drop_empty(row):
    if any(v == "" for v in row.values()):
        return            # yields nothing -> row is skipped
    yield row

Sink

A sink is the end of a pipeline: write_csv, write_ndjson, or collect() to get a list. Sinks are what actually drive iteration.

Rule of thumb: sources and sinks touch the outside world; transforms are pure. Keeping transforms pure is what makes them trivial to unit-test.