跳转到主要内容

JSON-RPC v2.0 无I/O

项目描述

JSON-RPC v2.0 无I/O

PyPI Python Versions MIT License Build Status codecov

该项目提供了一个Sans I/OJSON-RPC v 2.0实现。这意味着库处理了协议规范所需的所有编码、解码、封帧和逻辑,但它不实现任何I/O(输入或输出)。为了使用此库,您需要使用一个下游项目将该项目的I/O封装起来,或者编写自己的I/O封装器。

客户端示例

此示例说明了库的功能以及如何将其集成到您选择的I/O框架中。

from sansio_jsonrpc import JsonRpcPeer, JsonRpcParseError, JsonRpcException

client = JsonRpcPeer()
request_id, bytes_to_send = client.request(
    method='open_vault_door',
    args={'employee': 'Mark', 'pin': 1234},
)

首先,我们实例化客户端。我们调用request方法创建一个新的JSON-RPC请求,并将其转换为适合发送到服务器的bytes表示形式。该方法还返回一个request_id,它是一个可哈希的值,可以用来关联任何未来的响应,将其与启动它的请求相关联。

connection.send(bytes_to_send)
received_bytes = connection.recv()

请记住,这是一个Sans I/O库,所以它本身不执行任何网络操作。将数据传输到和从远程机器的责任在于调用者。此块显示了正在使用的假设I/O框架,用于发送挂起的请求并接收作为bytes的响应,但可以使用您选择的任何I/O框架来实现。

try:
    messages = client.parse(received_bytes)
except JsonRpcParseError as pe:
    print('Exception while parsing response', pe)

在此块中,我们将从远程机器接收到的数据输入客户端对象的parse()方法。此方法解析传入的数据,并返回一个包含服务器发送的消息的可迭代对象。(在当前实现中,可迭代对象始终包含恰好1条消息,但API设计成这样是为了允许未来的增强,例如能够返回0-n条消息的流式JSON解析器。)

for response in messages:
    assert isinstance(response, JsonRpcResponse)
    print('received a response:', response.id)
    if response.is_success:
        print('success:', response.result)
    else:
        print('error:', response.error)
        exc = JsonRpcException.exc_from_error(response.error)

最后,我们遍历消息。对于客户端,这些消息将始终是JsonRpcReponse对象,其中包含结果或错误信息。response.id字段应与之前获得的request_id匹配。

如果您希望错误响应引发异常,可以调用JsonRpcException.exc_from_error(...),它将为您构造一个异常。异常系统在下面的单独章节中描述。当然,您需要注意抛出异常的位置,因为读取多个响应时抛出异常将导致其余响应被忽略。在一个并发系统中,您可能希望将异常提升到调用request()的代码路径。

在并发系统中,您还希望在同一连接上多路复用请求和响应。这个库本身不实现多路复用,因为带有适当流控制(见下文“背压”)的多路复用将取决于您使用的传输和I/O框架。

服务器示例

此示例显示了如何接收JSON-RPC请求并分发响应。

from sansio_jsonrpc import (
    JsonRpcPeer,
    JsonRpcException,
    JsonRpcParseError,
    JsonRpcMethodNotFoundError,
)

server = JsonRpcPeer()

此示例开始与客户端相同:实例化一个JsonRpcPeer对象。请注意,客户端和服务器实现实际上在同一个类中!

这可能在开始时令人惊讶,但这是最灵活的JSON-RPC实现方式。例如,规范说明通知是一种特殊类型的请求,请求只能从客户端发送到服务器。但是,有些场景下服务器向客户端发送通知很有用,例如发布/订阅系统。规范说明

该规范的某项实现可以轻松地同时扮演这两个角色,甚至对不同的客户端或相同的客户端同时扮演这两个角色。

这就是为什么库在单个类中实现了这两个角色。将对象命名为server只是一个约定,以帮助我们记住正在发生的事情。

received_bytes = connection.recv()

此块再次显示了假设的I/O框架。作为服务器,我们只想等待客户端发送给我们一些内容。

try:
    messages = server.parse(received_bytes)
except JsonRpcParseError as pe:
    bytes_to_send = server.respond_with_error(request=None, error=pe.get_error())
    connection.send(bytes_to_send)
    return

接下来,我们想要解析传入的数据。如果在解析过程中引发解析错误,则应发送响应,并且不应遍历消息,因为messages实际上并未返回!因为我们无法解析请求,所以在响应中将request=None设置。

for request in messages:
    assert isinstance(request, JsonRpcRequest)

    try:
        # Handle request here and send a response.
        result = handle_request(request)
        bytes_to_send = server.respond_with_result(request=request, result=result)
    except JsonRpcException as jre:
        bytes_to_send = server.respond_with_error(request=request, error=pe.get_error())

    connection.send(bytes_to_send)

接下来,我们遍历接收到的消息。对于服务器,每条消息应该是JsonRpcRequesthandle_request(...)的实现取决于您!您应该通过返回结果来处理每个请求。respond_with_result(...)方法生成适当的JSON-RPC响应,然后您应该将其发送给客户端。

如果在处理程序中发生错误,您应该引发内置异常或JsonRpcApplicationError的自定义子类。例如,如果您没有特定方法的处理程序,应引发JsonRpcMethodNotFoundError。异常在下面的内容中更详细地描述。

异常

本库中的异常系统旨在尽可能使错误处理符合Python风格,尽可能抽象JSON-RPC协议的细节。本模块中的所有异常都继承自JsonRpcException,因此它是一个很好的通配符异常处理器。所有异常都有一个错误码和一条消息。异常还可以可选地包含一个data字段,该字段可以设置为任何有效的JSON数据类型。

此外,库中还有一些专门错误与规范中的特定JSON-RPC错误码相对应。例如,如果你收到无法解码为有效JSON-RPC请求的数据,规范要求返回-32700错误,而此库则抛出JsonRpcParseError,以便更容易捕获此特定情况。如果服务器发送

规范还允许你定义自己的错误码。有

本库中有两种使用异常的方式。首先,库本身可以抛出异常。例如,在recv()中,它可以抛出上面描述的JsonRpcParseError。第二种用法是,远程对等方可能会发送包含错误对象的响应。在这种情况下,你可能希望将该错误转换为异常,然后抛出它。

JsonRpcException.exc_from_error(...)静态方法将错误转换为异常。此方法自动选择JsonRpcException的适当子类。例如,如果远程对等方发送错误码-32700,则此方法将其转换为JsonRpcParseError。一些错误码是为实现定义自己的错误码而保留的,例如,-32099在规范中保留但未赋予任何含义。如果远程对等方发送此错误码,则此方法将抛出JsonRpcReservedError

规范还有一些未保留的错误码,可用于定义应用程序特定的错误。你可以在自己的代码中以两种不同的方式使用这些错误码。第一种方法是捕获/抛出JsonRpcApplicationError并设置/检查错误码为你的应用程序定义的值。第二种方法是创建自己的JsonRpcApplicationError子类。以下是一个示例

from sansio_jsonrpc import JsonRpcApplicationError


class MyApplicationError1(JsonRpcApplicationError):
    ERROR_CODE = 1
    ERROR_MESSAGE = "My application error type 1"


class MyApplicationError2(JsonRpcApplicationError):
    ERROR_CODE = 2
    ERROR_MESSAGE = "My application error type 2"

JsonRpcException.exc_from_error(...)方法将自动选择适当的子类。例如,如果服务器发送错误码1,则该方法将返回一个MyApplicationError1实例,即使该类定义在你的代码中!库使用一些元类黑魔法来使这成为可能。

反压

作为SANS I/O库,此包不实现任何类型的流控制。如果对等方发送数据的速度比你处理数据的速度快,而你持续读取,那么你的进程的内存使用将不断增长,直到耗尽内存或内核终止它。反压意味着向对等方发出信号,让它暂时停止发送数据,直到你的进程能够跟上。反压的具体实现取决于你使用的传输协议和I/O框架。例如,TCP具有流控制功能,大多数实现会在你停止从套接字读取时自动应用反压。因此,如果你使用此库与TCP一起使用,你应该小心只在准备好处理另一条消息时从套接字读取。如果你急切地从套接字读取到无界的用户空间缓冲区(例如队列),那么你的代码将不会从TCP的流控制中受益,因为内核会看到空缓冲区,并继续填充它。

开发

该项目使用MyPy进行类型检查和Black进行代码格式化。Poetry用于管理依赖关系和构建版本。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源代码分发

sansio-jsonrpc-0.2.0.tar.gz (14.1 kB 查看哈希值)

上传时间 源代码

构建分发

sansio_jsonrpc-0.2.0-py3-none-any.whl (10.7 kB 查看哈希值)

上传时间 Python 3

由以下机构支持