[英]How to add a FileTransport cleanly to Asyncio?
我正在编写一个读取文本数据并对其进行操作的应用程序。 文本数据可以来自TCP端口,也可以来自文本文件(该文件包含先前从TCP端口读取并存档的数据)。 我正在用Python 3编写它,并且使用asyncio似乎是显而易见的工具。
使用Streams API open_connection()
打开TCP端口并从中读取数据很简单。 异步体系结构的概念是针对输入输出的下层和上层的传输和协议 。 因此,似乎我应该实现一个传输来从文件中读取文本,并将其传递给协议。 这将使我的应用程序其余部分与文本数据是来自TCP端口还是来自文件分离。
但是我很难弄清楚如何告诉asyncio使用我首选的Transport。
open_connection()
具有一个与TCP端口Transport有关的参数列表,无法指定其他Transport,而文件路径等参数要少得多。 open_connection()
转过身并调用loop.create_connection()
。 正如专门用于TCP端口传输一样。 现在仍然可以提供不同的运输方式。 loop.create_connection()
的实现从self._make_ssl_transport()
或self._make_socket_transport()
获取其Transport对象。 这些在asyncio.selector_events.BaseSelectorEventLoop
和asyncio.proactor_events.BaseProactorEventLoop
具有替代实现,因此我们显然已经超过了应该选择文件传输的地步。 我是否缺少某个地方让asyncio告诉我要使用哪种传输方式? 还是将asyncio真正地根深蒂固地编码为使用其自己的TCP端口和UDP数据报传输器,仅此而已?
如果我想允许将自己的Transport与asyncio一起使用 ,则看起来我必须扩展事件循环,或者编写更多灵活的替代create_connection()
,将其编码为特定的事件循环实现。 这似乎是一项艰巨的工作,并且容易受到实施更改的影响。
还是用传输处理文件输入是愚蠢的事情? 我是否应该改写我的代码以说:
if (using_tcp_port): await asyncio.open_connection(....) else: completely_different_file_implementation(....)
根据文档 API的create_connection()
它需要一个协议,并创建一个流式传输,这是一个TCP连接。 因此,它不应该是用于自定义传输的API。
但是,对于TCP传输或自定义文件传输重用相同协议的想法是有效的。 它不会是“完全不同的实现”,但至少不会使用create_connection()
。 假设它是read_file()
:
def my_protocol_factory():
return your_protocol
if using_tcp_port:
transport, protocol = await loop.create_connection(my_protocol_factory, host, port)
else:
transport, protocol = await read_file(loop, my_protocol_factory, path_to_file)
然后,您将获得以下内容:
from asyncio import transports
import aiofiles # https://github.com/Tinche/aiofiles
def read_file(loop, protocol_factory, path):
protocol = protocol_factory()
transport = FileTransport(path, loop)
transport.set_protocol(protocol)
return transport, protocol
class FileTransport(transports.ReadTransport):
def __init__(self, path, loop):
super().__init__()
self._path = path
self._loop = loop
self._closing = False
def is_closing(self):
return self._closing
def close(self):
self._closing = True
def set_protocol(self, protocol):
self._protocol = protocol
self._loop.create_task(self._do_read())
def get_protocol(self):
return self._protocol
async def _do_read(self):
try:
async with aiofiles.open(self._path) as f:
self._loop.call_soon(self._protocol.connection_made, self)
async for line in f:
self._loop.call_soon(self._protocol.data_received, line)
if self._closing:
break
self._loop.call_soon(self._protocol.eof_received)
except Exception as ex:
self._loop.call_soon(self._protocol.connection_lost, ex)
else:
self._loop.call_soon(self._protocol.connection_lost, None)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.