繁体   English   中英

如何将FileTransport干净地添加到Asyncio?

[英]How to add a FileTransport cleanly to Asyncio?

我正在编写一个读取文本数据并对其进行操作的应用程序。 文本数据可以来自TCP端口,也可以来自文本文件(该文件包含先前从TCP端口读取并存档的数据)。 我正在用Python 3编写它,并且使用asyncio似乎是显而易见的工具。

使用Streams API open_connection()打开TCP端口并从中读取数据很简单。 异步体系结构的概念是针对输入输出的下层和上层的传输协议 因此,似乎我应该实现一个传输来从文件中读取文本,并将其传递给协议。 这将使我的应用程序其余部分与文本数据是来自TCP端口还是来自文件分离。

但是我很难弄清楚如何告诉asyncio使用我首选的Transport。

  • Streams API 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.BaseSelectorEventLoopasyncio.proactor_events.BaseProactorEventLoop具有替代实现,因此我们显然已经超过了应该选择文件传输的地步。

我是否缺少某个地方让asyncio告诉我要使用哪种传输方式? 还是将asyncio真正地根深蒂固地编码为使用其自己的TCP端口和UDP数据报传输器,仅此而已?

如果我想允许将自己的Transport与asyncio一起使用 ,则看起来我必须扩展事件循环,或者编写更多灵活的替代create_connection() ,将其编码为特定的事件循环实现。 这似乎是一项艰巨的工作,并且容易受到实施更改的影响。

还是用传输处理文件输入是愚蠢的事情? 我是否应该改写我的代码以说:

if (using_tcp_port): await asyncio.open_connection(....) else: completely_different_file_implementation(....)

根据文档 APIcreate_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM