Skip to content

Python client

kinedb-client is the direct client of kinedb for Python. It sends SQL to a running kinedb server over HTTP and over a WebSocket, with the same API as the JavaScript client.

Install

The packages live on the forge, under the kinedb organisation: https://git.kinedb.com/kinedb/-/packages. There is no pypi.org copy.

pip:

pip install --index-url https://git.kinedb.com/api/packages/kinedb/pypi/simple kinedb-client

uv, in the pyproject.toml of your application:

toml
[[tool.uv.index]]
name = "kinedb"
url = "https://git.kinedb.com/api/packages/kinedb/pypi/simple"
explicit = true

[tool.uv.sources]
kinedb-client = { index = "kinedb" }

Use the forge as an EXPLICIT index, never --extra-index-url alone. All four kinedb names are free on pypi.org, so a second index may answer first and hand your application a stranger's package.

The index needs a Gitea token when the organisation is private. pip takes it in the URL (https://<user>:<token>@git.kinedb.com/...); uv takes UV_INDEX_KINEDB_USERNAME and UV_INDEX_KINEDB_PASSWORD.

Use

python
import asyncio
from kinedb.client import KineDB, rows_as_objects

async def main():
    db = KineDB("http://localhost:4820")

    print(await db.health())
    print(await db.sql("SHOW DATABASES"))    # one statement over HTTP

    sock = await db.connect()                # a persistent WebSocket
    await sock.authenticate("root", "hunter2")
    rows = await sock.sql("SELECT * FROM users")
    print(rows_as_objects(rows))

    sub = await sock.watch("users", lambda notify: print(notify))
    await sub.cancel()
    sock.close()

asyncio.run(main())

base_url is required. Unlike the browser client, this one has no page to derive a URL from. ws_url is derived from base_url when it is not given: http becomes ws, https becomes wss, and /ws is appended to the path.

A result is a plain dict, tagged the way the server tags its own JSON: resp["type"] is rows, created, inserted, backoff, health and so on. A typed result set also carries resp["types"] and resp["schema_id"].

On integers. The JavaScript client hands back a BigInt above 2^53, because a JavaScript number cannot hold an integer exactly past that point. A Python int is unbounded, so that distinction has no Python half: an Int column decodes to a plain int and keeps every bit, always.

Credentials

The client holds no session. An application installs two hooks once, and every KineDB(...) in that application picks them up:

python
from kinedb.client import set_auth_hooks

set_auth_hooks(
    get_token=lambda: store.token,           # read on EVERY request, never cached
    on_unauthorized=lambda: store.logout(),  # runs on a 401, BEFORE the raise
)

Either hook may be a coroutine function; the client awaits the result.

One client can override them, which is what a program that talks to a second server with a different credential needs:

python
KineDB(url, get_token=lambda: other_token)

With neither, the client sends no bearer and bounces nobody. That is the right default for a consumer that never logs in. GET /health stays bare in every case, because it is open by design and a liveness probe has no credentials.

set_auth_hooks returns the hooks it replaced, so a test or a one-off task can put them back:

python
previous = set_auth_hooks(get_token=borrowed)
...
set_auth_hooks(**previous)

Retrying

Off by default: a server-flagged transient rejection raises at once, with err.retryable set so a caller can build its own loop.

python
db = KineDB(url, retry=True)
db = KineDB(url, retry={"max_retries": 3, "base_delay_ms": 20, "max_delay_ms": 500})

With it on, the client absorbs those rejections behind a random full-jitter wait, and honours the server's own retry_ms pacing hint on a backoff response. The CLIENT retries; the server never does.

No runtime dependencies

The package installs nothing else. The HTTP transport is urllib.request on a worker thread, and the WebSocket client is our own RFC 6455 codec over asyncio streams (kinedb.client.ws). An SDK that pulled websockets or httpx would force a version range on every application that installs it.

Pass http_request= or ws_connect= to KineDB(...) to drive it from a test with no server.