[英]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.