Python client for the L database: a pure-Rust IPC core (TCP framing, wire serialization, LZ4 decompression) exposed through PyO3.
Install a wheel from the
v0.1.0 release
(pip install <wheel>), or build from source with uv run maturin develop.
import l
conn = l.connect("localhost", 5001)
conn.query("t:([]sym:`A`B`A;price:1.5 2.5 3.5)")
print(conn.query("select from t").to_python())
# {'sym': ['A', 'B', 'A'], 'price': [1.5, 2.5, 3.5]}
conn.close()Connection is also a context manager; connect(host, port, "user:pass")
authenticates. Queries return K values — call .to_python() to convert.
Pass arguments built with the K constructors:
conn.query_with_args("{x+y}", l.K.int(10), l.K.int(32)).
| L value | tag (atom/vector) | Python |
|---|---|---|
| bool | -1 / 1 | bool / list[bool] |
| byte | -4 / 4 | int / bytes |
| short / int / long | -5..-7 / 5..7 | int / list[int] |
| real / float | -8, -9 / 8, 9 | float / list[float] |
| char / string | -10 / 10 | str (both) |
| symbol | -11 / 11 | str / list[str] |
| temporals | -12..-19 | raw count since 2000.01.01 |
| mixed list | 0 | list |
| table / keyed table | 98 | dict of column name → list |
| dict | 99 | dict |
null :: |
101 | None |
Null numerics arrive as sentinels (0N = -2**31, 0Nj = -2**63, float
nulls = nan). Server errors raise RuntimeError; the connection stays
usable afterwards. Blocking I/O releases the GIL; the contract is one
connection per thread — a second thread using the same connection gets a
clean "Already borrowed" error, never a corrupted stream.
l -p 5001 &
L_TEST_PORT=5001 L_BIN=path/to/l uv run pytest # Rust: cargo test -p l-rs~200 tests against a live server: the full atom/vector type matrix in
both directions, structures (nested lists, dicts, keyed/wide/empty
tables, enum columns), null sentinels and infinities of every type,
unicode/NUL/10M-element boundaries, the server error taxonomy, LZ4 wire
compression, and kill-the-server robustness. L_BIN enables the
sacrificial-server tests; L_STRESS=1 adds 100MB-scale runs.