Tokio-inspired Python JSON-RPC peers with named params
Project description
tw2_jsonrpc
tw2_jsonrpc is the Python sibling of the Rust tw2-jsonrpc experiment: one
bidirectional JSON-RPC Peer, async-first transport code, named params only,
and a small decorator API for registering local methods.
This package is peer-first: connect over TCP, attach to stdio, connect over WebSocket, launch a child process that speaks over stdio, or accept TCP and WebSocket peers with a server registry.
Client Call
import tw2_jsonrpc
async with await tw2_jsonrpc.connect_tcp("127.0.0.1", 8080) as peer:
result = await peer.call("math.add", a=2, b=3)
Params are named only. Pass a dict or omit params:
await peer.call("math.add", {"a": 2, "b": 3})
await peer.call("math.add", a=2, b=3)
await peer.call("health")
Array params are rejected with InvalidParams.
Transports
All transports return a Peer. After that, calls and notifications feel the
same.
peer = await tw2_jsonrpc.connect_tcp("127.0.0.1", 8080)
peer = await tw2_jsonrpc.connect_stdio()
peer = await tw2_jsonrpc.connect_ws("ws://127.0.0.1:8080/rpc")
Subprocesses
Use start_subprocess when the library should launch and own a child process
that speaks newline-delimited JSON-RPC over stdio. This is stdio JSON-RPC with
child-process lifecycle management, not a separate transport protocol:
peer = await tw2_jsonrpc.start_subprocess(["worker", "--stdio"])
start_process remains available as an alias.
Server
A Server owns methods that should be installed on every peer it creates.
peer_cls is chosen at the transport boundary:
server = tw2_jsonrpc.Server()
@server.method("math.add")
async def add(a: int, b: int) -> int:
return a + b
handle = await server.serve_tcp("127.0.0.1", 8080, peer_cls=tw2_jsonrpc.TcpPeer)
await handle.serve_forever()
Peer subclasses can still carry per-peer state and methods:
class AppPeer(tw2_jsonrpc.TcpPeer):
@tw2_jsonrpc.method("session.ping")
async def ping(self) -> str:
return "pong"
handle = await server.serve_tcp("127.0.0.1", 8080, peer_cls=AppPeer)
Use a handler for per-connection setup:
async def on_peer(peer: AppPeer) -> None:
@peer.method("session.echo")
async def echo(text: str) -> str:
return text
handle = await server.serve_tcp(
"127.0.0.1",
8080,
peer_cls=AppPeer,
handler=on_peer,
)
WebSocket serving and subprocess peers use the same server registry:
handle = await server.serve_ws("127.0.0.1", 8080, peer_cls=tw2_jsonrpc.WebSocketPeer)
peer = await server.connect_subprocess(["worker", "--stdio"], peer_cls=tw2_jsonrpc.SubprocessPeer)
The free serve_tcp and serve_ws helpers remain available when you do not
need server-wide methods.
Peer Handlers
Runtime registration is available directly on a peer:
peer = await tw2_jsonrpc.connect_stdio()
@peer.method("math.add")
async def add(a: int, b: int) -> int:
return a + b
The method name can default to the function name:
@peer.method
async def health() -> dict:
return {"ok": True}
Peer subclasses can expose decorated methods:
class App(tw2_jsonrpc.StdioPeer):
@tw2_jsonrpc.method("tools.echo")
async def echo(self, text: str) -> str:
return text
Subclass handlers receive self as the current peer, so callbacks and
notifications can use the same object:
class App(tw2_jsonrpc.StdioPeer):
@tw2_jsonrpc.method("callback.demo")
async def callback_demo(self, name: str) -> dict:
await self.notify("callback.ready", {"name": name})
return {"ok": True}
You can also register or remove handlers explicitly:
peer.add_method(echo, "tools.echo")
peer.remove_method("tools.echo")
Notifications
Notifications are fire-and-forget. No result is returned.
await peer.notify("log.info", message="started")
Handlers live in the same method table. If the incoming JSON-RPC message omits
id, no response is sent, even if the handler returns a value.
For readability, notification is available as an alias for method
registration:
@peer.notification("log.info")
async def log_info(message: str) -> None:
print(message)
Errors
Remote JSON-RPC error responses become JsonRpcError subclasses:
try:
result = await peer.call("math.add", {"a": 1, "b": 2})
except tw2_jsonrpc.InvalidParams:
...
except tw2_jsonrpc.JsonRpcError:
...
Handlers can raise structured JSON-RPC errors:
raise tw2_jsonrpc.InvalidParams("field 'name' is required")
Local timeout and cancellation stay local:
result = await peer.call("slow.method", {"x": 1}, timeout=5.0)
If a call is cancelled or times out, the pending local waiter is removed. JSON-RPC 2.0 has no standard remote cancellation, so a later response for that id is ignored.
Mental Model
Peeris a bidirectional JSON-RPC connection.await peer.call(...)sends a request and returns the decoded result.await peer.notify(...)sends a fire-and-forget message.@peer.methodregisters a request handler.Serverregisters methods shared by every peer it accepts or starts.@peer.notificationis a readability alias for notification handlers.- Only named params are supported.
- The shape is intentionally close to
tw2-jsonrpc, but Python does not need a builder or proc macro.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tw2_jsonrpc-1.0.0.tar.gz.
File metadata
- Download URL: tw2_jsonrpc-1.0.0.tar.gz
- Upload date:
- Size: 8.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c56e4e01d1473e4f3a5f66e907bb670a6e809649d33ba18a7d235dbf98aced7
|
|
| MD5 |
8708c595308bd51e1b88b2a68737841b
|
|
| BLAKE2b-256 |
fc6fa1532e229bfa019af0aace9b9a2325a412fa488bb6840bc60264ab16eccc
|
File details
Details for the file tw2_jsonrpc-1.0.0-py3-none-any.whl.
File metadata
- Download URL: tw2_jsonrpc-1.0.0-py3-none-any.whl
- Upload date:
- Size: 12.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f26a373e2cd41fd3b1bf7fb2caf13725391d12fd665b70758ee4749c5155984
|
|
| MD5 |
1dd517246e7998d307ecaca9c4ce9e64
|
|
| BLAKE2b-256 |
02c699eb215756f1301df19fec2ce2429d162ad5689caa4504f0c601f5729c6a
|