Trio实现asyncio.Protocol
项目描述
# trio-protocol
这实现了`asyncio.Transport`接口,以及在`trio`之上构建的基本asyncio类,如`asyncio.Task`,以帮助移植`asyncio`库。想法是允许`trio`运行`asyncio.Protocol`,使得单个代码库能够在两个框架上运行。
`trio-protocol`的**目标**不是让你在不做任何修改的情况下在`trio`上运行`asyncio`代码。如果需要这个功能,请查看[`asyncio-trio`](https://github.com/python-trio/trio-asyncio)。
#### 当前支持和不支持的内容
这是一个早期版本。你可以用它来支持一些基本的`asyncio`服务器。然而,它缺少以下内容:
- 客户端支持(仅测试了服务器)
- 测试套件。
- 在生产环境中运行时的稳健体验。
- 可能,asyncio代码在野外使用的有用/必要方法的实现,应添加。
## 使用方法
假设你想要在`trio`上运行来自python3-samples存储库的`asyncio-len-prefixed-string-protocol.py`。
如果你点击链接,你会看到该模块首先实现了`asyncio.Protocol`的子类,然后进一步子类化。最终,该协议的实现从客户端读取以长度为前缀的字符串,并回送一个`"ok"`消息。
在文件底部,你会找到以下代码来启动服务器
```python
loop = asyncio.get_event_loop()
coro = loop.create_server(MyStringReceiver, '127.0.0.1', 5566)
server = loop.run_until_complete(coro)
print('serving on {}'.format(server.sockets[0].getsockname()))
try
loop.run_forever()
except KeyboardInterrupt
print("exit")
finally
server.close()
loop.close()
```
这将在端口`5566`上创建一个`asyncio`服务器。连接到该端口的每个连接都将由`MyStringReceiver`协议提供服务。具体来说,`loop.create_server()`将设置服务器的套接字(由于它是一个`async`函数,它将在等待(在这种情况下,我们通过`loop.run_until_complete`执行)之前不做任何事情)。每当有人连接到服务器时,asyncio将调度一个任务来处理该连接。我们运行`loop.run_forever()`以使循环处于活动状态并处理这些任务。
现在让我们在`trio`上运行这个。将此代码段替换为
```python
import trio
from trio_protocol import run_server
trio.run(run_server, MyStringReceiver, '127.0.0.1', 5566)
```
这将有效!代码比原始代码短一些,部分原因是因为设置`trio`更简洁,部分原因是我们做的更少:我们没有很好地处理`KeyboardInterrupt`,并且在我们准备好接受连接时不打印消息。相反,`trio_protocol.run_server`是一个快捷方式,它可以为我们做所有事情:它打开一个nursery,启动一个服务器,并在该服务器上运行`asyncio.Protocol`。
如果我们想更精确地复制原始代码,我可以这样做
```python
import trio
from trio_protocol import create_server
async def run_server()
async with trio.open_nursery() as nursery
server = await create_server(nursery, MyStringReceiver, '127.0.0.1', 5566)
print('serving on {}'.format(server.sockets[0].getsockname()))
try
trio.run(run_server)
except KeyboardInterrupt
print("exit")
```
```trio_protocol.create_server```将开始监听套接字。它将返回一个`trio_protocol.Server`对象,该对象旨在与`asyncio.Server`相似。然后您可以自由运行自己的代码,服务器在后台运行,类似于`asyncio`版本。
>>> 注意:要测试此服务器,可以使用
>>> `python -c "import struct; print((b'%shello world' % struct.pack('<L', 12)).decode('ascii'))" | nc localhost 5566`
### 如果协议需要访问循环怎么办
如果协议使用`asyncio`循环,例如启动后台任务,可以使用`trio_protocol.Loop`,这是一个支持一些常用方法(如`call_later`或`create_task`)的类似循环的类。大多数协议都是编写为接受`loop`参数的,因此您会这样做
```python
import functools
import trio
from trio_protocol import Loop, run_server
async def run_server()
async with trio.open_nursery() as nursery
loop = Loop(nursery)
protocol_factory = functools.partial(MyProtocol, loop=loop)
await create_server(nursery, protocol_factory, '127.0.0.1', 5566)
trio.run(run_server)
```
现在,协议想要在`trio_protocol.Loop`给出的nursery中运行的背景任务将运行。
注意:这是一个严格的要求,即您想要运行的协议类使用显式循环。大多数`asyncio`接口都是这样编写的,当没有传递显式循环时,将自动使用当前全局循环。这不会工作,因为您最终会使用`asyncio`循环。
这实现了`asyncio.Transport`接口,以及在`trio`之上构建的基本asyncio类,如`asyncio.Task`,以帮助移植`asyncio`库。想法是允许`trio`运行`asyncio.Protocol`,使得单个代码库能够在两个框架上运行。
`trio-protocol`的**目标**不是让你在不做任何修改的情况下在`trio`上运行`asyncio`代码。如果需要这个功能,请查看[`asyncio-trio`](https://github.com/python-trio/trio-asyncio)。
#### 当前支持和不支持的内容
这是一个早期版本。你可以用它来支持一些基本的`asyncio`服务器。然而,它缺少以下内容:
- 客户端支持(仅测试了服务器)
- 测试套件。
- 在生产环境中运行时的稳健体验。
- 可能,asyncio代码在野外使用的有用/必要方法的实现,应添加。
## 使用方法
假设你想要在`trio`上运行来自python3-samples存储库的`asyncio-len-prefixed-string-protocol.py`。
如果你点击链接,你会看到该模块首先实现了`asyncio.Protocol`的子类,然后进一步子类化。最终,该协议的实现从客户端读取以长度为前缀的字符串,并回送一个`"ok"`消息。
在文件底部,你会找到以下代码来启动服务器
```python
loop = asyncio.get_event_loop()
coro = loop.create_server(MyStringReceiver, '127.0.0.1', 5566)
server = loop.run_until_complete(coro)
print('serving on {}'.format(server.sockets[0].getsockname()))
try
loop.run_forever()
except KeyboardInterrupt
print("exit")
finally
server.close()
loop.close()
```
这将在端口`5566`上创建一个`asyncio`服务器。连接到该端口的每个连接都将由`MyStringReceiver`协议提供服务。具体来说,`loop.create_server()`将设置服务器的套接字(由于它是一个`async`函数,它将在等待(在这种情况下,我们通过`loop.run_until_complete`执行)之前不做任何事情)。每当有人连接到服务器时,asyncio将调度一个任务来处理该连接。我们运行`loop.run_forever()`以使循环处于活动状态并处理这些任务。
现在让我们在`trio`上运行这个。将此代码段替换为
```python
import trio
from trio_protocol import run_server
trio.run(run_server, MyStringReceiver, '127.0.0.1', 5566)
```
这将有效!代码比原始代码短一些,部分原因是因为设置`trio`更简洁,部分原因是我们做的更少:我们没有很好地处理`KeyboardInterrupt`,并且在我们准备好接受连接时不打印消息。相反,`trio_protocol.run_server`是一个快捷方式,它可以为我们做所有事情:它打开一个nursery,启动一个服务器,并在该服务器上运行`asyncio.Protocol`。
如果我们想更精确地复制原始代码,我可以这样做
```python
import trio
from trio_protocol import create_server
async def run_server()
async with trio.open_nursery() as nursery
server = await create_server(nursery, MyStringReceiver, '127.0.0.1', 5566)
print('serving on {}'.format(server.sockets[0].getsockname()))
try
trio.run(run_server)
except KeyboardInterrupt
print("exit")
```
```trio_protocol.create_server```将开始监听套接字。它将返回一个`trio_protocol.Server`对象,该对象旨在与`asyncio.Server`相似。然后您可以自由运行自己的代码,服务器在后台运行,类似于`asyncio`版本。
>>> 注意:要测试此服务器,可以使用
>>> `python -c "import struct; print((b'%shello world' % struct.pack('<L', 12)).decode('ascii'))" | nc localhost 5566`
### 如果协议需要访问循环怎么办
如果协议使用`asyncio`循环,例如启动后台任务,可以使用`trio_protocol.Loop`,这是一个支持一些常用方法(如`call_later`或`create_task`)的类似循环的类。大多数协议都是编写为接受`loop`参数的,因此您会这样做
```python
import functools
import trio
from trio_protocol import Loop, run_server
async def run_server()
async with trio.open_nursery() as nursery
loop = Loop(nursery)
protocol_factory = functools.partial(MyProtocol, loop=loop)
await create_server(nursery, protocol_factory, '127.0.0.1', 5566)
trio.run(run_server)
```
现在,协议想要在`trio_protocol.Loop`给出的nursery中运行的背景任务将运行。
注意:这是一个严格的要求,即您想要运行的协议类使用显式循环。大多数`asyncio`接口都是这样编写的,当没有传递显式循环时,将自动使用当前全局循环。这不会工作,因为您最终会使用`asyncio`循环。