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.
Metadata
Release files for tw2-jsonrpc 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| tw2_jsonrpc-1.0.1.tar.gz | 8.1 kB | Without provenance | Details for tw2_jsonrpc-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| tw2_jsonrpc-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for tw2_jsonrpc-1.0.1-py3-none-any.whl |
Total release size: 20.2 kB
Release files / tw2_jsonrpc-1.0.1.tar.gz
| Download URL | tw2_jsonrpc-1.0.1.tar.gz |
|---|---|
| Size | 8.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f57ed9c105bc8653b16b54cb75f8c677cbcacc009fe0544958b0e170045daa29
|
|
BLAKE2b-256 checksum How to use checksums |
f482587146dc9b1805cbd562b4463ee4088761c95b29d0f56a7d604e01ae18e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / tw2_jsonrpc-1.0.1-py3-none-any.whl
| Download URL | tw2_jsonrpc-1.0.1-py3-none-any.whl |
|---|---|
| Size | 12.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
be36ddb9c294db5ca5c04d674c8f169b33f08f8d2ff8eca45e2c29b3121eb895
|
|
BLAKE2b-256 checksum How to use checksums |
7058f0092ba04e7c561941f482f3b62b2bb7c27efbdae47cc058aea4f970e4c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Security for tw2-jsonrpc 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.