Skip to main content

arrowbricks

Runs SQL against a Databricks SQL warehouse via the Statement Execution API and hands you the result as Arrow -- a Cursor shaped like databricks-sql-python's (execute, fetchone/fetchmany/fetchall, fetchall_arrow/fetchmany_arrow), or stream_query_json for streaming NDJSON. The hot path (statement submit/poll, bounded-concurrency chunk fetch, the reorder buffer, Arrow-IPC decode) is a PyO3/arrow-rs extension bundled in this same package -- no DuckDB, no pandas/pyarrow.

  • Single responsibility: Databricks to Arrow. No embedded query engine -- that's duckbricks, built on top of this.
  • Bring-your-own-auth -- a static token or your own token-refresh callable. No cloud-SDK dependency baked in.
  • Result-order preserved even though chunks can complete out of order over the network.
  • Chunks are fetched lazily as fetchone/fetchmany/fetchall actually need them, not all upfront.
  • Heartbeats between slow chunks (execute_streamed/stream_query_json), so a caller streaming this over e.g. SSE never goes silent during a cold warehouse start.
  • Rust core, real OS-thread concurrency: 1.6x-2.5x faster than a pure-Python/asyncio client at fetching/decoding a multi-chunk result, scaling further with chunk count and concurrency where asyncio+GIL plateaus.
  • Zero required runtime dependencies.

Install

pip install arrowbricks

Ships as platform wheels (Linux/macOS/Windows) with the Rust extension precompiled -- no Rust toolchain needed to install, and no required dependencies. fetchone/fetchmany/fetchall (row-tuple materialization) need pip install arrowbricks[arro3]; everything else (fetchall_arrow/fetchmany_arrow, execute_arrow, stream_query_json, upload_volume_file/delete_volume_file, Cursor.description) works with nothing installed.

Quickstart

import asyncio
from arrowbricks import connect


async def main():
    conn = connect(
        host="adb-1234567890.1.azuredatabricks.net",
        warehouse_id="abcd1234efgh5678",
        token="dapi...",  # or token_provider=... -- see Auth below
    )
    cursor = conn.cursor()

    await cursor.execute("SELECT * FROM my_catalog.my_schema.my_table LIMIT 100")
    async for row in cursor:
        print(row)

    await cursor.execute("SELECT * FROM my_catalog.my_schema.my_table LIMIT 100")
    table = await cursor.fetchall_arrow()  # an Arrow table (arro3/pyarrow/DuckDB-compatible)


asyncio.run(main())

For streaming NDJSON (e.g. a FastAPI SSE endpoint, first row out as soon as its chunk arrives):

from arrowbricks import HEARTBEAT, DatabricksClient, stream_query_json

client = DatabricksClient(host=..., warehouse_id=..., token=...)

async for item in stream_query_json(client, "SELECT * FROM my_catalog.my_schema.big_table"):
    if item is HEARTBEAT:
        continue  # forward as an SSE keep-alive comment, e.g.
    print(item)  # one ready-to-send JSON string per row

See examples/basic.py for a runnable version, examples/cursor_paging.py for paging a large result with fetchmany/fetchmany_arrow without buffering it all upfront, examples/fastapi_sse.py for streaming a query to a client as Server-Sent Events, examples/fastapi_sse_pivot.py for the same over a buffered Cursor.fetchall_streamed result with one combined heartbeat/timeout budget across both the wait and the download, or examples/azure_auth.py for a caching token_provider built on Azure AD (DefaultAzureCredential).

Rust core

rust/arrowbricks_core is a PyO3/arrow-rs crate implementing the actual hot path -- statement submit/poll, bounded-concurrency chunk fetch, the chunk_index reorder buffer, Arrow-IPC decode/write, and NDJSON encode -- built into this same arrowbricks wheel as a compiled submodule, not a separate PyPI package. Cursor, stream_query_json, DatabricksClient all delegate to it directly -- there's no separate Python-level HTTP client or Arrow library on the hot path at all. See its own README for the crate-level design, including standalone DuckDB and FastAPI SSE usage examples against the compiled extension directly.

Why not databricks-sql-connector?

The official driver is the right choice if you need full DB-API 2.0 compatibility over Databricks' Thrift/ODBC-style protocol. If you just want a query result as Arrow/JSON in your own async app, it drags in a lot for that: pandas, thrift, openpyxl, pybreaker, pyjwt, oauthlib, lz4, requests, urllib3 as hard dependencies. arrowbricks talks to the plain REST Statement Execution API instead, with a Rust core and zero required runtime dependencies of its own. The Cursor API is deliberately shaped like the official driver's so switching between them is mostly a constructor change, but arrowbricks is async throughout (execute, fetchone, etc. are all coroutines) -- there's no sync escape hatch.

Why not duckbricks?

duckbricks does the same Databricks-to-Arrow work, then goes further: it uses a real embedded DuckDB engine to materialize results into your own DuckDB connection/table (feed_select_to_duckdb_table), or push a DuckDB query's result up to Databricks (feed_duckdb_table_to_databricks). If you need that -- a real local SQL engine sitting on top, not just "run this query, get Arrow/JSON back" -- use duckbricks; it depends on arrowbricks for the Databricks/Arrow half. If you don't need DuckDB at all, arrowbricks alone is the smaller, single-responsibility half.

Auth

connect/DatabricksClient take either:

  • token: str -- a static personal access token or pre-issued OAuth token, or
  • token_provider -- a callable (sync or async) returning a token string, called on every request.

arrowbricks has no opinion on how you get a token and no cloud-SDK dependency of its own. If your provider is expensive to call, cache/refresh inside it -- arrowbricks does no caching on your behalf.

conn = connect(host=..., warehouse_id=..., token_provider=my_token_provider)

API

  • connect(host, warehouse_id, *, token=None, token_provider=None, ...) -> Connection
  • Connection.cursor() -> Cursor
  • Connection.client -> DatabricksClient -- the same client cursor() uses, for lower-level access (e.g. stream_query_json, upload_volume_file).
  • Cursor.execute(sql, parameters=None, *, row_limit=None, offset=None, catalog=None, schema=None, total_timeout_s=None) -> Cursor -- submits and waits for the statement, like a real DB-API cursor. parameters, if given, is Databricks' own named-parameter format -- [{"name": ..., "value": ..., "type": ...}] bound against :name markers in sql.
  • Cursor.execute_streamed(...) -- same args, but an async generator yielding HEARTBEAT while waiting on a slow cold start, then the ready Cursor -- for bridging e.g. an SSE connection. Its timeout/heartbeats stop the moment the statement is ready, before any chunk has been downloaded -- see fetchall_streamed below for the download phase itself.
  • Cursor.fetchone() -> tuple | None, Cursor.fetchmany(size) -> list[tuple], Cursor.fetchall() -> list[tuple] -- needs arrowbricks[arro3] (see "Arrow vs. row-tuple fetches" below).
  • Cursor.fetchmany_arrow(size) -> Table, Cursor.fetchall_arrow() -> Table -- an Arrow table (implements __arrow_c_stream__, so arro3/pyarrow/DuckDB can all consume it directly, zero-copy). No extra dependency needed.
  • Cursor.fetchall_streamed(*, total_timeout_s=None) / Cursor.fetchall_arrow_streamed(*, total_timeout_s=None) -- like fetchall()/fetchall_arrow(), but yield HEARTBEAT while pulling chunks instead of blocking silently, then the final rows/Table -- for a caller downloading a large result over SSE who needs heartbeats (and a timeout) through the download, not just the initial wait. Compose with execute_streamed and a shared deadline if you want one combined budget across both phases (see examples/fastapi_sse_pivot.py).
  • Cursor is an async iterator, yielding one row (tuple) at a time -- needs arrowbricks[arro3], same as fetchone/fetchmany/fetchall.
  • Cursor.description -- DB-API-style [(name, type_name, None, None, None, None, None), ...] after execute(). No extra dependency needed.
  • stream_query_json(client, sql, **kwargs) -- yields HEARTBEAT, then each row as a JSON string, as soon as its chunk arrives. Timestamps come out as full ISO-8601, every column key is always present ("col":null for a null value, never an omitted key). No extra dependency needed.
  • DatabricksClient(host, warehouse_id, *, token=None, token_provider=None, ...) -- the lower-level client Connection wraps. client.upload_volume_file(volume_path, data)/client.delete_volume_file(volume_path) for the Files API.
  • write_ipc_stream(table_or_chunk, buf) -- writes any Arrow-C-Data-Interface-compatible object as an uncompressed Arrow-IPC stream (see below). No extra dependency needed.

Cursor.execute/execute_streamed/stream_query_json all accept catalog, schema, row_limit, offset, and total_timeout_s.

Arrow vs. row-tuple fetches -- when you need arrowbricks[arro3]

fetchall_arrow/fetchmany_arrow return an Arrow table backed entirely by the Rust core -- no extra install needed, and it's the faster path if your code can consume Arrow directly (DuckDB, pyarrow, polars, a Parquet writer, ...):

import duckdb

table = await cursor.fetchall_arrow()
duckdb.sql("SELECT count(*) FROM table").show()  # DuckDB reads it zero-copy

fetchone/fetchmany/fetchall (and iterating a Cursor directly) materialize actual Python tuples instead -- ("id", "label")-style rows you can index into, print, or pass to code that doesn't know about Arrow at all. That conversion is handled by arro3-core (pip install arrowbricks[arro3]), not this package itself:

await cursor.execute("SELECT id, label FROM my_catalog.my_schema.my_table")
async for row in cursor:  # or: rows = await cursor.fetchall()
    print(row[0], row[1])

Calling a row-tuple method without arro3-core installed raises a ModuleNotFoundError naming the exact install command, rather than failing silently or with a confusing traceback.

A note on Arrow IPC compression

write_ipc_stream (and everything in this package that serializes Arrow-IPC bytes) always writes uncompressed bodies. A compressed body (arro3's own default is compression="LZ4") is transparently decompressed by some Arrow readers (e.g. DuckDB's) but not necessarily by every other Arrow IPC reader -- notably, duckdb-wasm's browser-side decoder silently fails to parse LZ4-compressed bodies. Since arrowbricks' bytes might end up read by anything, plain uncompressed is the safe default.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

arrowbricks-1.0.0.tar.gz (67.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

arrowbricks-1.0.0-cp311-abi3-win_amd64.whl (5.5 MB view details)

Uploaded CPython 3.11+Windows x86-64

arrowbricks-1.0.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.4 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

arrowbricks-1.0.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.0 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

arrowbricks-1.0.0-cp311-abi3-macosx_11_0_arm64.whl (4.8 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

arrowbricks-1.0.0-cp311-abi3-macosx_10_12_x86_64.whl (5.3 MB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file arrowbricks-1.0.0.tar.gz.

File metadata

  • Download URL: arrowbricks-1.0.0.tar.gz
  • Upload date:
  • Size: 67.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for arrowbricks-1.0.0.tar.gz
Algorithm Hash digest
SHA256 45ea051dfb276e2d79185b7e27663d85ee95430c13d8649cac4de31848a361fc
MD5 70122c4ea6f947faa661568b34111d38
BLAKE2b-256 689e58b2d9fa7effba802d48ebf39b0264729060a41f0133b2abd2c9e7130170

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.0.tar.gz:

Publisher: release.yml on bmsuisse/arrowbricks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file arrowbricks-1.0.0-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: arrowbricks-1.0.0-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 5.5 MB
  • Tags: CPython 3.11+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for arrowbricks-1.0.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 53f75fceec0ea935e5115223ceedbb2c8f690031288e639ac728a8ef5882645b
MD5 bfb64de4ac5d5b67c6d3cc385194d177
BLAKE2b-256 ac59bd3ef6d284dfc57ee08f91fc42dc428aca5472acb31fbebed32e2cbbb822

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.0-cp311-abi3-win_amd64.whl:

Publisher: release.yml on bmsuisse/arrowbricks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file arrowbricks-1.0.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for arrowbricks-1.0.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 02f98441186b6ca3f3ce811405ad152081e84ce857c5b4ef0c41fa302f545279
MD5 0bf67c5cf97eab839762aa259cca3286
BLAKE2b-256 cccb296617dc83baa0f87eae2d2a3942f5ac5fca07bbae7bbc69f08b5378a463

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on bmsuisse/arrowbricks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file arrowbricks-1.0.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for arrowbricks-1.0.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 bf03a97c7d47638d978af0952129296523f86a4623a75842266321407b815c2a
MD5 403f72300fb558363c7e0f5798d36dc6
BLAKE2b-256 02da5aab06fe4ff35cf809ee1389affdbfc3c91141c7068abb011e8b2eef8a65

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on bmsuisse/arrowbricks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file arrowbricks-1.0.0-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for arrowbricks-1.0.0-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 858fff8949a047b25a63514bffd02ed154d1dc7464e689cab6f3616016018bad
MD5 78bae1fcb138f250f6cc51bff946c2d6
BLAKE2b-256 d58b698556d0381077b5d2e225b6228daa66f074512afca60523ca92c7b72317

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.0-cp311-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on bmsuisse/arrowbricks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file arrowbricks-1.0.0-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for arrowbricks-1.0.0-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 870345f25aab27d7e6fb999245b24703e93f7dacab6c339ecb529c2b1cf0a9fa
MD5 e879ba7ed6655c7198f7ebdf5a5bb3c0
BLAKE2b-256 f948ca06e45b9dba892f1e4023fecba5239319299b7818d921b4ec2a67043f37

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.0-cp311-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on bmsuisse/arrowbricks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

3.1.2

6 files

3.1.1

6 files

3.1.0

6 files

3.0.4

6 files

3.0.3

6 files

3.0.2

6 files

3.0.1

6 files

3.0.0

6 files

2.0.0

6 files

1.5.0

6 files

1.4.1

6 files

1.4.0

6 files

1.3.3

6 files

1.3.2

6 files

1.3.1

6 files

1.3.0

6 files

1.2.0

6 files

1.1.1

6 files

1.1.0

6 files

1.0.1

6 files

This release

1.0.0 This release

6 files

0.3.0

2 files

0.2.0

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page