JSON-RPC v2.0 无I/O
项目描述
JSON-RPC v2.0 无I/O
该项目提供了一个Sans I/O的JSON-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)
接下来,我们遍历接收到的消息。对于服务器,每条消息应该是JsonRpcRequest
。handle_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 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 022159b658116cbd239a110e4841be8250fc052a0cbcd7f24fc9cec88add071f |
|
MD5 | 4010a62a8b12a648977d74a4d4e67ecc |
|
BLAKE2b-256 | 72f88a0bc25a087d97d0e4e774c0b4b2cec14bfba42fa5a1b4c507ce4ab6c536 |
sansio_jsonrpc-0.2.0-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 3924ed21650aef938d09a04d32b1771c0d37014c09685959dfce3b8b1db24763 |
|
MD5 | 5570c6aae5b3ffaaa17114935ece34cd |
|
BLAKE2b-256 | 1af3a7d39f4c8e655074eda7a07401d83b56b8f808b4dc44c00d6a90fd58cbf7 |