Using Rugo Standalone
Rugo is the file engine inside Opteryx — the part that turns Parquet, CSV, and JSONL files into columns the engine computes over. It is also published on its own, as a plain reader/writer library with no SQL layer on top.
pip install rugoThis gets you Rugo and Draken (the columnar library it emits into) — and nothing else.
Rugo vs. Opteryx
These are two different things worth keeping straight:
- Rugo reads and writes Parquet, CSV, and JSONL directly — you call its functions, get Draken morsels back, and work with them in Python. There is no SQL here.
- Opteryx is the SQL engine built on top of Rugo. Querying a dataset with SQL —
SELECT ... FROM data.table— currently only works against Parquet data.
If you have CSV or JSONL you want to query with SQL, use Rugo to convert it to Parquet first (below), then query the Parquet with Opteryx.
Reading Parquet Directly
from rugo import parquet
with parquet.read_parquet(
"space_missions.parquet",
columns=["Company", "Location", "Price"],
predicates=[("Company", "=", "SpaceX")],
) as reader:
for morsel in reader:
for row in morsel:
print(row.Company, row.Location, row.Price)columns and predicates are applied by the reader itself — only the columns and row groups that can match are decoded.
A single column comes back as a Draken vector, which converts to a plain Python list or an Arrow array:
with parquet.read_parquet("space_missions.parquet") as reader:
morsel = next(iter(reader))
prices = morsel.column("Price")
print(prices.to_pylist()[:10])
print(prices.to_arrow()[:10])Writing Parquet
Rugo also writes Parquet — pass it a Draken morsel and get bytes back:
from rugo import parquet
data = parquet.write_parquet(morsel, compression="zstd")
with open("out.parquet", "wb") as f:
f.write(data)For large datasets, write_parquet holds the whole file in memory before returning it. open_parquet_writer writes one row group per write_row_group() call instead, pushing each chunk of bytes to a sink as it's produced — memory stays roughly constant (~one row group) no matter how many batches you write:
with open("out.parquet", "wb") as f:
with parquet.open_parquet_writer(f.write) as writer:
for batch in batches:
writer.write_row_group(batch)sink is any callable that takes bytes — a file's .write, or an adapter around a cloud upload API. Every batch passed to the same writer must share the same column schema.
Writing CSV and JSONL
Rugo writes JSONL and CSV directly from a Draken morsel, without going through Python's own json or csv modules:
from rugo import parquet
from rugo.jsonl import write_jsonl
with open("space_missions.jsonl", "wb") as out:
with parquet.read_parquet("space_missions.parquet") as reader:
for morsel in reader:
out.write(write_jsonl(morsel))write_csv(morsel) works the same way for CSV output.
From the Command Line
Installing Rugo also puts a rugo command on your PATH — the same reader and writer, driven from the shell. It is the fastest way to look at a file, convert between formats, or drop Parquet/CSV/JSONL into a pipeline without writing any Python.
rugo info space_missions.parquet # rows, columns, size, format
rugo schema space_missions.parquet # column names, types, nullability
rugo preview -n 5 space_missions.parquet # first rows as a table
rugo convert space_missions.parquet space_missions.jsonl # format inferred from extensionsThe most useful verbs:
| Verb | What it does |
|---|---|
info |
High-level metadata — rows, columns, size, format |
schema / columns |
Column names and types |
count |
Row count |
preview / head |
First N rows (-n, and -c to project columns) |
describe / stats |
Per-column null counts, min/max, distinct (Parquet only) |
inspect |
Low-level footer / row-group / encoding dump (Parquet only) |
diff |
Compare two files' schemas — columns added, removed, retyped |
convert |
Convert between Parquet, CSV, and JSONL |
merge |
Concatenate schema-identical files into one |
split |
Split a file into row-count-bounded chunks (--rows) |
Every verb takes --json for machine-readable output, so the CLI composes with tools like jq:
rugo count --json events.parquet | jq .num_rows
rugo describe --json events.parquet | jq '.columns[] | select(.null_count > 0)'describe, stats, and inspect read statistics from the Parquet footer that CSV and JSONL don't carry, so they apply to Parquet only; merge requires identical schemas across its inputs and fails loud on a mismatch rather than coercing.
Converting to Arrow
If your existing toolchain expects Arrow tables, a morsel converts directly:
with parquet.read_parquet("space_missions.parquet") as reader:
for morsel in reader:
table = morsel.to_arrow()Why It Exists
If you want fast Parquet I/O but do not need SQL, PyArrow is the usual default — and it is large. Rugo is the opposite trade: a thin, native, dependency-free engine for the three formats Opteryx uses, built from the same source Opteryx ships.
| Rugo | PyArrow | |
|---|---|---|
| Installed size | ~17 MB | ~124 MB |
| Cold import | ~5 ms | ~29 ms |
| Dependencies | none (codecs vendored) | several |
Every Parquet file Rugo writes is readable by PyArrow, Spark, DuckDB, or anything else that reads Parquet — interoperability is the entire reason the format exists.
The One Difference From Opteryx
The standalone rugo wheel is local-filesystem only. Opteryx's copy of Rugo can also read gs:// and http(s):// paths because Opteryx needs remote I/O; the standalone wheel deliberately leaves that out to stay thin. A remote path fails immediately and loudly rather than returning nothing.