Skip to main content

jsonrpc-zmq

jsonrpc-zmq is a lightweight duplex JSON-RPC 2.0 peer implementation built on top of ZeroMQ.

Unlike traditional client/server RPC frameworks, this project implements a peer-to-peer JSON-RPC transport, meaning both sides can send requests and notifications.

The library provides two runtimes:

  • Async Runtime – built on asyncio
  • Qt Runtime – built on Qt event loop

This allows JSON-RPC communication in:

  • asyncio applications
  • Qt GUI applications
  • distributed systems
  • simulation and testing frameworks
  • IPC tools

Features

  • JSON-RPC 2.0 compliant
  • peer-to-peer RPC
  • ZeroMQ transport
  • request / response
  • notification support
  • handler registration
  • request timeout support
  • backpressure protection
  • bounded send queue
  • graceful shutdown
  • detailed logging

Two runtime implementations share the same protocol semantics.


Architecture

Application
      │
      │ request / notify
      ▼
JsonRpcPeer / QJsonRpcPeer
      │
      │
      ▼
ZeroMQ Socket
      │
      ▼
Remote Peer

Each peer can:

  • send requests
  • receive requests
  • send notifications
  • receive notifications

Transport Model

Both implementations share the same conceptual transport model.

Application
   │
   │ request()
   │ notify()
   ▼
Send Queue
   │
   ▼
Transport Loop
   │
   ▼
ZeroMQ Socket
   │
   ▼
Receive Loop
   │
   ▼
dispatch / resolve pending

Installation

Install the package (dependencies are pulled in automatically):

pip install .

Dependencies:

library purpose
pyzmq transport
jsonrpcclient request generation
jsonrpcserver request dispatch
qtpy Qt abstraction

Runtime Implementations

Async Runtime

Module:

JsonRpcPeer

Built on:

asyncio
zmq.asyncio

Async architecture

Application coroutine
        │
        │ await request()
        ▼
send_queue (asyncio.Queue)
        │
        ▼
send_loop task
        │
        ▼
ZeroMQ socket
        │
        ▼
recv_loop task
        │
        ▼
resolve Future / dispatch handler

Example

Server:

import asyncio
import logging

from jsonrpc_zmq import AsyncJsonRpcPeer, Success

logging.basicConfig(level=logging.INFO)


async def pong(ss=None):
    """ping handler: echoes back the optional 'ss' parameter"""
    return Success("pong" if ss is None else f"pong: {ss}")


async def echo(msg=None):
    print("received notification:", msg)
    return Success(msg)


async def server_main():
    server = AsyncJsonRpcPeer("tcp://127.0.0.1:5556", bind=True)
    server.add_handler("ping", pong)
    server.add_handler("echo", echo)
    server.start()
    try:
        await asyncio.Event().wait()
    finally:
        await server.stop()


try:
    asyncio.run(server_main())
except KeyboardInterrupt:
    print("server stopped")

Client:

import asyncio
import logging

from jsonrpc_zmq import AsyncJsonRpcPeer, JsonRpcError, RequestTimeoutError

logging.basicConfig(level=logging.DEBUG)


async def client_main():
    client = AsyncJsonRpcPeer("tcp://127.0.0.1:5556", bind=False)
    client.start()
    try:
        # request with params; the server's ping handler echoes the value back
        try:
            res = await client.request("ping", {"ss": 1}, timeout=3)
            print("got:", res)
        except JsonRpcError as e:
            print("Remote error:", e)
        except RequestTimeoutError as e:
            print("Request timeout:", e)

        # plain request
        try:
            res = await client.request("ping", timeout=3)
            print("got:", res)
        except JsonRpcError as e:
            print("Remote error:", e)
        except RequestTimeoutError as e:
            print("Request timeout:", e)

        # notification (fire-and-forget; the server prints what it receives)
        client.notify("echo", {"msg": "hello"})
        print("sleeping 10 seconds...")
        await asyncio.sleep(10)
    finally:
        await client.stop()


try:
    asyncio.run(client_main())
except KeyboardInterrupt:
    pass

Output:

got: pong: 1
got: pong
sleeping 10 seconds...

Qt Runtime

Module:

qt.QJsonRpcPeer

Built on:

Qt event loop
QThread
QSocketNotifier

Qt architecture

Main Thread
   │
   │ request()
   │ notify()
   ▼
QJsonRpcPeer
   │
   └── IO Thread
        │
        ├── SendWorker
        │
        └── RecvWorker

IO is handled in a dedicated Qt thread.

Example

Server:

import sys
from qtpy.QtWidgets import QApplication
from jsonrpc_zmq import QJsonRpcPeer, Success

app = None
peer = None

def ping(ss=None):
    return Success("pong" if ss is None else f"pong: {ss}")

def main():
    global peer
    peer = QJsonRpcPeer("tcp://127.0.0.1:5556", bind=True)
    peer.add_handler("ping", ping)
    peer.start()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    main()
    try:
        rc = app.exec_()
    except KeyboardInterrupt:
        rc = 0
    peer.stop()
    sys.exit(rc)

Client:

import sys
from qtpy.QtCore import QTimer
from qtpy.QtWidgets import QApplication
from jsonrpc_zmq import JsonRpcError, QJsonRpcPeer, RequestTimeoutError

app = None
peer = None

def main():
    global peer
    peer = QJsonRpcPeer("tcp://127.0.0.1:5556", bind=False)
    peer.start()


def request_and_quit():
    global peer, app
    try:
        result = peer.request("ping", timeout=3)
        print("got:", result)
    except JsonRpcError as e:
        print("Remote error:", e)
    except RequestTimeoutError as e:
        print("Request timeout:", e)
    finally:
        peer.stop()
        app.quit()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main()
    QTimer.singleShot(1000, request_and_quit)
    try:
        app.exec_()
    except KeyboardInterrupt:
        pass

Requests

Send request:

peer.request("method", params)

Async version:

await peer.request("method", params)

Notifications

Send notification:

peer.notify("event", {"data": 1})

Notifications do not expect responses.


Handler Registration

Register RPC method:

peer.add_handler("multiply", handler)

Remove handler:

peer.remove_handler("multiply")

Timeout

Requests support timeout.

Async:

await peer.request("slow", timeout=2)

Qt:

peer.request("slow", timeout=2)

If exceeded:

RequestTimeoutError

is raised.


Backpressure

Outgoing messages are buffered in a bounded queue.

Async version:

asyncio.Queue(maxsize=N)

Qt version:

deque(maxlen=N)

If the queue is full:

BackPressureError
TransportError

may be raised.


Error Model

Possible exceptions:

Exception Meaning
InvalidStateError peer not started or stopped
RequestTimeoutError request timed out
BackPressureError send queue full
TransportError transport failure
JsonRpcError remote RPC error

Example:

try:
    result = await peer.request("foo")
except JsonRpcError as e:
    print(e)

Shutdown

Async version:

await peer.stop()

Qt version:

peer.stop()

Shutdown will:

  • stop transport loops
  • close socket
  • fail all pending requests

Logging

The library uses Python logging.

Enable debug logs:

import logging

logging.basicConfig(level=logging.DEBUG)

Example output:

INFO JsonRpcPeer send message
INFO JsonRpcPeer received response
ERROR JsonRpcPeer transport crashed

Design Goals

The project focuses on:

  • simplicity
  • predictable failure model
  • runtime portability (async / Qt)
  • safe backpressure handling
  • minimal dependencies

This project is intentionally not a full RPC framework.


Limitations

Current limitations:

  • single ZMQ socket
  • no reconnect logic
  • no batching
  • no authentication
  • recommended socket type: PAIR

Example Use Cases

  • distributed testing frameworks
  • simulation systems
  • Qt desktop tools
  • CLI automation tools
  • IPC between processes

License

Copyright (C) 2018-2026 Connet Information Technology Company, Shanghai.

Download files

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

Source Distribution

jsonrpc_zmq-2026.8.17.tar.gz (18.2 kB view details)

Uploaded Source

File details

Details for the file jsonrpc_zmq-2026.8.17.tar.gz.

File metadata

  • Download URL: jsonrpc_zmq-2026.8.17.tar.gz
  • Upload date:
  • Size: 18.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.4

File hashes

Hashes for jsonrpc_zmq-2026.8.17.tar.gz
Algorithm Hash digest
SHA256 98a99897c2d472ab3c3ae76bec5bf08e03eb0a84fb359b7f28c7dec7a0135378
MD5 71c78c39d039e9cf327b5342687d83ca
BLAKE2b-256 83a0a58184c269dc5668e7c102ba9b99c3c89475bb56ab98f8f904c08a3afdac

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2026.8.17 This release

1 file

2026.8.16

1 file

2026.3.11

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