簡體   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