This is the ONQL Python client package.
pip install onql-clientfrom onqlclient import ... # your usage hereThis package provides a small asynchronous Python client for talking to an ONQL TCP server. It implements a simple framed protocol and a friendly async API so you can send requests and receive matching responses concurrently.
Install from PyPI:
pip install onql-clientBasic usage — create a client, send a request, then close the connection:
import asyncio
from onqlclient import ONQLClient
async def main():
client = await ONQLClient.create(host="localhost", port=5656)
try:
resp = await client.send_request("get", "some-key", timeout=5)
print("Response:", resp)
finally:
await client.close()
asyncio.run(main())If you prefer an interactive example:
# In an interactive session
import asyncio
from onqlclient import ONQLClient
client = asyncio.run(ONQLClient.create())
resp = asyncio.run(client.send_request("get", "my-key"))
print(resp)
asyncio.run(client.close())The client supports a simple subscription flow where a request id (rid) is used as the subscription handle. The server may send multiple frames with that same rid — the client delivers them to the callback you provide.
async def on_event(rid, keyword, payload):
print(f"SUBSCRIPTION {rid} -> {keyword}: {payload}")
client = await ONQLClient.create()
rid = await client.subscribe(onquery="", query="SELECT ...", callback=on_event)
# later:
await client.unsubscribe(rid)
await client.close()- ONQLClient.create(host: str = "localhost", port: int = 5656) -> ONQLClient
- Connects and returns a ready-to-use client instance.
- send_request(keyword: str, payload: str, timeout: int = 10) -> dict
- Sends a framed request and awaits the matching response.
- subscribe(onquery: str, query: str, callback: Callable) -> str
- Opens a streaming subscription and returns the subscription rid.
- unsubscribe(rid: str)
- Stops a subscription locally and optionally notifies the server.
- close()
- Close the connection and cleanup pending requests.
See the module docstrings in onqlclient/__init__.py for more details.
The client uses a very small custom framing protocol to multiplex requests and responses on a single TCP connection. Important details:
- Socket type: TCP (asyncio stream) — the client connects with
asyncio.open_connection(host, port). - End-of-message marker (EOM): ASCII 0x04 (EOT) — represented as the byte
value
\x04. Every frame ends with this byte so the reader can detect message boundaries. - Field delimiter: ASCII 0x1E (record separator) — represented as the
character
\x1E. Each frame is composed of three fields separated by this delimiter:request_id,keyword, andpayload.
Frame layout (UTF-8 encoded, then appended with EOM byte):
<request_id><DELIM><keyword><DELIM><payload><EOM>
Example bytes for a request asking keyword get for payload my-key:
b"abcd1234\x1Eget\x1Emy-key" + b"\x04"
Responses use the same framing; the client matches responses to the
original request by the request_id (rid). This design lets you make many
concurrent requests over a single long-lived TCP connection.
- If
create()fails, ensure the server is reachable on the host/port and your firewall allows outbound TCP to that port. - If
send_requestraises aTimeoutError, try increasing thetimeoutparameter or inspect the server logs to verify it is producing replies.
- Do not commit your virtual environment or any tokens to the repository.
The project includes a
.gitignorethat excludes.venv/,dist/,build/, and*.egg-info. - For publishing, use an ephemeral PyPI token (not your account password) and keep tokens outside the repository (CI secrets or environment variables are best).