app.py serves LadybugDB Cypher results as Flask REST + JSON
(Arrow -> to_pydict() -> json.dumps, Snowflake {"data": [[row, json], ...]} protocol).
This package serves the same queries as native Arrow columnar batches over gRPC,
consumed with the standard columnar.tech ADBC methods — no row-by-row JSON.
uv sync # creates .venv (ladybug comes from PyPI)
# recipe one-liner also works:
# uv pip install ladybug pyarrow adbc-driver-flightsql# 1. server (:memory: demo graph, same data as app.build_demo_graph)
uv run python ladybug_flight_server.py
# grpc://localhost:50051, try: --db /tmp/graph.lbdb --port 50051 --no-demo
# 2. client — ADBC, exactly the posted recipe but pointed at LadybugDB
uv run python ladybug_adbc_client.py
uv run python ladybug_adbc_client.py --demo # all 6 demo queries
uv run python ladybug_adbc_client.py --no-adbc --demo # plain FlightClient pathimport adbc_driver_flightsql.dbapi as flight_sql
with flight_sql.connect("grpc://localhost:50051") as conn:
with conn.cursor() as cur:
cur.execute("MATCH (u:User) RETURN u.name, u.age ORDER BY u.id")
table = cur.fetch_arrow_table() # pyarrow.Table — zero JSON overhead
print(table)adbc_driver_flightsql is typically tied to SQL (cursor.execute(sql),
CommandStatementQuery{query}), but query is just a string on the wire.
LadybugFlightServer treats it as Cypher (see extract_query()), so the
columnar.tech call pattern works unchanged for a graph DB. CALL show_tables()
keeps working for schema introspection.
REST (app.py) |
Arrow (ladybug_flight_server.py) |
|
|---|---|---|
| encoding | to_pydict → json.dumps → parse |
Arrow IPC over gRPC |
| types | lost (everything is JSON) | preserved (schema, fetch_arrow_table) |
| client | POST /query {"data": [[0, cypher]]} |
cursor.execute(cypher) |
| batching | manual row-index envelope | RecordBatchStream / fetch_arrow_table |
Prebuilt multi-arch images (amd64/arm64, Debian 3.14-slim base) are published
to GHCR on every GitHub Release — no PyPI publish for this repo by design.
docker pull ghcr.io/ladybugdb/ladybug-adbc-python:latest
# :memory: demo graph on localhost:50051
docker run --rm -p 50051:50051 ghcr.io/ladybugdb/ladybug-adbc-python:latest
# persistent graph via a volume (server runs as non-root, /data is writable)
docker run --rm -p 50051:50051 -v ladybug-data:/data \
-e LADYBUG_DB=/data/graph.lbdb \
ghcr.io/ladybugdb/ladybug-adbc-python:latest
# query it (same columnar.tech recipe, local or remote host)
uv run python ladybug_adbc_client.py --demoEnv knobs: FLIGHT_HOST (default 0.0.0.0 in the image), FLIGHT_PORT
(default 50051), LADYBUG_DB (default :memory:). CLI flags
(--host/--port/--db) override env. A HEALTHCHECK probes the server with a
trivial RETURN 1 Flight query.
Build locally:
docker build -t ladybug-adbc:dev .- Bump
versioninpyproject.toml, commit, push. - GitHub UI → Releases → Draft a new release, tag
vX.Y.Z→ Publish. .github/workflows/docker-publish.ymlruns tests, then pushesghcr.io/ladybugdb/ladybug-adbc-python:X.Y.Z,X.Y, andlatest(latestskipped for prereleases). No PyPI upload — PyPI later, maybe.
app.py— legacy Flask REST (unchanged, for comparison).ladybug_flight_server.py—LadybugFlightServer(FlightServerBase), demo graph, protobuf-tolerantextract_query.ladybug_adbc_client.py—query_cypher_adbc()/query_cypher_flight().tests/test_flight.py— spins up the server on an ephemeral port, asserts ADBC + Flight return identical Arrow tables.