Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rpc.service

import rpc

Everything here is re-exported by rpc, so import rpc is enough and the names are called as rpc.*. Importing rpc.service on its own works too and reaches the same definitions.

Service: what a program answers. A service holds the handler for each method the program serves, and answers every message it is given with them, whatever carried the message there.

A service on its own answers messages that each arrive in an exchange of their own, such as the body of an HTTP request, and whose answer goes back in that same exchange. An Endpoint is a service on a connection: it answers what arrives on the connection, and calls the other side of it as well.

Re-exported by rpc.

Constants

DEFAULT_BATCH_LIMIT

rpc.DEFAULT_BATCH_LIMIT = 1000

The most messages a batch may hold before it is refused whole, 1000, until Service.set_batch_limit() says otherwise.

Classes

Context

class rpc.Context

What a handler is told about the message it is handling: the request’s id, its method, the endpoint handling it, and the request it arrived in.

A request handler normally answers by returning. One on an Endpoint that cannot answer before it returns, because the answer comes from work still to be done, calls defer(), keeps the context, and answers through it with reply() or fail() once the answer is ready.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

rpc.Context(endpoint, id, method: string, is_request: bool, request, can_defer: bool)

Returns a new Context. A service makes one for each message it hands to a handler.

Parameters

  • endpoint (Service) — The {Service} or {Endpoint} handling the message.
  • id (string|number|nil) — The request’s id; nil for a notification.
  • method (string) — The method called.
  • is_request (bool) — Whether the message is a request, which is answered, rather than a notification, which is not.
  • request (any) — What the message arrived in, such as the http request whose body it was; nil for a message from a connection.
  • can_defer (bool) — Whether the request can be answered after its handler returns, which needs a connection to answer on.

Context.is_notification()

rpc.Context.is_notification() -> bool

True when the message is a notification, which nothing answers.

Returns bool

Context.defer()

rpc.Context.defer() -> Context

Takes over answering the request: whatever the handler returns is ignored, and the request is answered when reply() or fail() is called, from the isolate that owns the endpoint. Calling it again changes nothing.

Only a request that arrived on an Endpoint’s connection can be deferred. One answered in its own exchange, such as an HTTP request, is answered when its handler returns.

A request deferred from inside a batch is answered on its own, after the batch’s other answers.

Returns Context — itself.

Raises ValueError for a notification, which is never answered, or for a request with no connection to answer it on later.

Context.is_deferred()

rpc.Context.is_deferred() -> bool

True once defer() has been called.

Returns bool

Context.reply()

rpc.Context.reply(result)

Answers the deferred request with result.

Parameters

  • result (any)

Raises ValueError when the request was not deferred, or has already been answered.

Raises RpcClosedError when the endpoint has been closed.

Context.fail()

rpc.Context.fail(error)

Answers the deferred request with error.

Parameters

  • error (RpcError)

Raises ValueError when the request was not deferred, or has already been answered.

Raises RpcClosedError when the endpoint has been closed.

Context.is_answered()

rpc.Context.is_answered() -> bool

True once a deferred request has been answered.

Returns bool

Context.to_string()

rpc.Context.to_string() -> string

This context as Context(method #id).

Returns string

Service

class rpc.Service

The methods a program serves, and the answering of every message sent to them.

Handlers say what it answers. A request handler is called with the request’s params and a Context, and what it returns is the result sent back; an RpcError it raises is sent back as that error, and any other error as an INTERNAL_ERROR carrying its message. A notification handler is called the same way and nothing is sent back. A message for a method with no handler goes to the on_unhandled() fallback when one is set; without one, a request is answered with METHOD_NOT_FOUND and a notification is dropped. A message that is not valid UTF-8, or not valid JSON, is answered with PARSE_ERROR.

import rpc

var calculator = rpc.Service()
  .on_request('add', @(params) => params[0] + params[1])

echo calculator.answer('{"jsonrpc": "2.0", "id": 1, "method": "add", "params": [2, 3]}')
# {"jsonrpc":"2.0","id":1,"result":5}

answer() is the whole of a service’s work: one message or batch in, its answer out. rpc.http_handler() puts a service behind an http route, and an Endpoint puts one on a connection.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

rpc.Service()

Returns a new Service with no handlers and a batch limit of DEFAULT_BATCH_LIMIT.

Service.on_request()

rpc.Service.on_request(method: string, handler: function) -> Service

Answers requests for method with handler, in place of any handler set for it before.

Parameters

  • method (string) — The method’s name. Names starting with rpc. are reserved by JSON-RPC.
  • handler (function(2)) — Called with the request’s params and a {Context}; what it returns is the result sent back.

Returns Service — itself.

Raises ValueError for a name starting with rpc..

Service.on_notification()

rpc.Service.on_notification(method: string, handler: function) -> Service

Handles notifications for method with handler, in place of any handler set for it before.

Parameters

  • method (string) — The method’s name. Names starting with rpc. are reserved by JSON-RPC.
  • handler (function(2)) — Called with the notification’s params and a {Context}; what it returns is ignored.

Returns Service — itself.

Raises ValueError for a name starting with rpc..

Service.on_unhandled()

rpc.Service.on_unhandled(handler: function) -> Service

Handles every request and notification whose method has no handler of its own, in place of any fallback set before. It is called as the other handlers are, with context.method naming the method. For a request, what it returns is the result sent back, and raising an RpcError with METHOD_NOT_FOUND refuses the method as a service without a fallback would.

A method whose name starts with rpc. never reaches it: JSON-RPC reserves those names, so such a request is answered with METHOD_NOT_FOUND and such a notification is dropped.

Parameters

  • handler (function(2)) — Called with the message’s params and a {Context}.

Returns Service — itself.

Service.on_error()

rpc.Service.on_error(handler: function) -> Service

Called with every problem the other side is not told about: an error a notification handler raised, an error other than an RpcError a request handler raised, a response nothing was waiting for, and, on an Endpoint, an error a send_request() callback raised and a connection that failed.

Parameters

  • handler (function(2)) — Called with the error and the message it concerns, as a dictionary, or nil when there is no one message.

Returns Service — itself.

Service.on_trace()

rpc.Service.on_trace(handler: function) -> Service

Called with the text of every message, as it arrives and as it is sent, for logging a conversation.

Parameters

  • handler (function(2)) — Called with 'in' or 'out' and the message’s JSON text.

Returns Service — itself.

Service.set_batch_limit()

rpc.Service.set_batch_limit(limit: ?int) -> Service

Sets the most messages a batch may hold. A larger batch is refused whole with one INVALID_REQUEST error and an id of nil, before any of it is handled, so one message cannot make a service do an unbounded amount of work.

Parameters

  • limit (?int) — Default DEFAULT_BATCH_LIMIT, 1000. nil takes a batch of any size.

Returns Service — itself.

Raises ValueError for a limit below 1.

Service.batch_limit()

rpc.Service.batch_limit() -> ?int

The most messages a batch may hold, or nil for no limit.

Returns ?int

Service.answer()

rpc.Service.answer(message, request) -> ?string

Answers one message, or a batch of them, and returns the JSON text of the answer, or nil when there is nothing to answer: a notification, or a batch of nothing but notifications.

import rpc

var greeter = rpc.Service()
  .on_request('greet', @(params) => 'Hello, ${params.name}!')

var call = '{"jsonrpc": "2.0", "id": 7, "method": "greet", "params": {"name": "Ada"}}'
var note = '{"jsonrpc": "2.0", "method": "greet", "params": {"name": "Ada"}}'

echo greeter.answer(call)  # {"jsonrpc":"2.0","id":7,"result":"Hello, Ada!"}
echo greeter.answer(note)  # nil

Parameters

  • message (string|bytes) — The message’s JSON text, or the bytes of it, which must be UTF-8.
  • request (any) — What the message arrived in, handed to every handler as context.request, such as the http request whose body it was. Default nil.

Returns ?string

Service.to_string()

rpc.Service.to_string() -> string

This service as Service(n methods).

Returns string


2026, Richard Ore and Zuri contributors