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.

  • Rust core. Statement submit/poll, bounded-concurrency chunk fetch, the reorder buffer, and Arrow-IPC decode all run in a PyO3/arrow-rs extension bundled in this same package -- 1.6x-2.5x faster than a pure-Python/asyncio client on a multi-chunk result, scaling further with chunk count and concurrency where asyncio+GIL plateaus.
  • Zero required dependencies. pip install arrowbricks and go.
  • 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.
  • Lazy fetching -- chunks are pulled only 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.

Install

pip install arrowbricks

Ships as precompiled platform wheels (Linux/macOS/Windows) -- no Rust toolchain needed, and nothing else to install for most of the API. Row-tuple fetches (fetchone/fetchmany/fetchall) need one optional extra: pip install arrowbricks[arro3] -- see Arrow vs. row-tuple fetches below.

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).

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], and iterating a Cursor directly -- row tuples; needs the arro3 extra.
  • 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).
  • 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.description -- DB-API-style [(name, type_name, None, None, None, None, None), ...] after execute().
  • 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).
  • 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, buf) -- writes any Arrow-C-Data-Interface-compatible object as an uncompressed Arrow-IPC stream (see below).
  • ReplayableArrowChunk(data: bytes, chunk_index, declared_row_count=None) -- wraps raw Arrow-IPC stream bytes (e.g. previously downloaded and stored) so they can be read more than once via __arrow_c_stream__ (a schema peek, then the actual scan -- DuckDB's registration path does this), and .to_table() for a one-shot parse. 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

Everything above works with zero dependencies installed except row-tuple fetches. fetchall_arrow/fetchmany_arrow return an Arrow table straight from the Rust core -- 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 needs arro3-core (pip install arrowbricks[arro3]):

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.

Rust core

rust/arrowbricks_core is the crate implementing the hot path above, built into this same arrowbricks wheel as a compiled submodule -- not a separate PyPI package. See its own README for the crate-level design, plus standalone DuckDB and FastAPI SSE 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 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.

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.1.tar.gz (68.7 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.1-cp311-abi3-win_amd64.whl (5.5 MB view details)

Uploaded CPython 3.11+Windows x86-64

arrowbricks-1.0.1-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.1-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.1-cp311-abi3-macosx_11_0_arm64.whl (4.8 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

arrowbricks-1.0.1-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.1.tar.gz.

File metadata

  • Download URL: arrowbricks-1.0.1.tar.gz
  • Upload date:
  • Size: 68.7 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.1.tar.gz
Algorithm Hash digest
SHA256 924af9a6dee68590b3ec121bc0bcbf78c041894a32fdfa9429975ffab3fb0602
MD5 5a4630bcd0a6f7a721014b92fec54f52
BLAKE2b-256 bd3385feaeb01abfd02c095c4d8ce18c84dcb60ec30661003cc65d678408ddee

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.1.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.1-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: arrowbricks-1.0.1-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.1-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 2e3b08bf25f2551886b773736d89f8523612618b3fcc9c07a24958614382c990
MD5 c3802d0aada7979329be2f03e6574d55
BLAKE2b-256 006c8f65badb234e2a89782ff4f520d4193de8854921a8bc7077fc335687d539

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.1-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.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for arrowbricks-1.0.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f8a1d29de6914da45419821294f14714098c3039469af964adbbb41ed733640c
MD5 eb41e85d2c549788a842cab572180256
BLAKE2b-256 efdaa36fcf904a29094b66c266d08960c5c8b0a3a1463c38a2e9e93aca1e115e

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.1-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.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for arrowbricks-1.0.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 774cc73798ab303f2cebc7a59d9293a0efc3fb2d6994d312a342f68a832ab4e6
MD5 e3eaebf2b0fce2a59dbd01cf35580f96
BLAKE2b-256 f452378eb84a8eaa5c1e34b6f9d8e02d7349950d58c2b2160f42c1a668e427dc

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.1-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.1-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for arrowbricks-1.0.1-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6666b42c851fc529441de5c8e30819f198e65ebc20f18d862ca2ef128065c19d
MD5 82986e001a5925b5e72e07cb424ae77e
BLAKE2b-256 603653b75fbd6a12888480dd38ad2a0ed51fcb1be940928b31cb5264ab8ad373

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.1-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.1-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for arrowbricks-1.0.1-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 575cc9508492b05803752692c04c42bd7a36fb1e428a5e198b59c4fa2272c102
MD5 440f5a5f63d5a93e71fe45b5b87741a8
BLAKE2b-256 7bd3ee8cca8ba9c930646d70a453bdecdfe23b81333a14c676924bd5d0bbe671

See more details on using hashes here.

Provenance

The following attestation bundles were made for arrowbricks-1.0.1-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

This release

1.0.1 This release

6 files

1.0.0

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