繁体   English   中英

python asyncio如何读取StdIn并写入StdOut?

[英]python asyncio how to read StdIn and write to StdOut?

我需要异步读取 StdIn 以获取消息(json 由 \\r\\n 终止),并在处理异步后将更新的消息写入 StdOut。

目前我正在同步进行,例如:

class SyncIOStdInOut():
    def write(self, payload: str):
        sys.stdout.write(payload)
        sys.stdout.write('\r\n')
        sys.stdout.flush()

    def read(self) -> str:
        payload=sys.stdin.readline()
        return  payload

如何执行相同但异步的操作?

这是使用异步流(对于 Unix)将stdout stdin回显到stdout的示例。

import asyncio
import sys


async def connect_stdin_stdout():
    loop = asyncio.get_event_loop()
    reader = asyncio.StreamReader()
    protocol = asyncio.StreamReaderProtocol(reader)
    await loop.connect_read_pipe(lambda: protocol, sys.stdin)
    w_transport, w_protocol = await loop.connect_write_pipe(asyncio.streams.FlowControlMixin, sys.stdout)
    writer = asyncio.StreamWriter(w_transport, w_protocol, reader, loop)
    return reader, writer


async def main():
    reader, writer = await connect_stdin_stdout()
    while True:
        res = await reader.read(100)
        if not res:
            break
        writer.write(res)


if __name__ == "__main__":
    asyncio.run(main())

作为即用型解决方案,您可以使用aioconsole库。 它实现了类似的方法,但也提供了额外有用的异步等价物inputprintexeccode.interact

from aioconsole import get_standard_streams

async def main():
    reader, writer = await get_standard_streams()

更新:

让我们试着弄清楚函数connect_stdin_stdout是如何工作的。

  1. 获取当前事件循环:
loop = asyncio.get_event_loop()
  1. 创建StreamReader实例。
reader = asyncio.StreamReader()

通常, StreamReader/StreamWriter类不打算直接实例化,只能作为open_connection()start_server()等函数的结果使用。 StreamReader为某些数据流提供缓冲的异步接口。 一些源代码(库代码)调用它的函数,例如feed_datafeed_eof ,数据被缓冲并且可以使用文档化的接口协程read()readline()read()

  1. 创建StreamReaderProtocol实例。
protocol = asyncio.StreamReaderProtocol(reader)

此类派生自asyncio.ProtocolFlowControlMixin ,有助于在ProtocolStreamReader之间进行调整。 它覆盖诸如data_receivedeof_received类的Protocol方法并调用StreamReader方法feed_data

  1. 在事件循环中注册标准输入流stdin
await loop.connect_read_pipe(lambda: protocol, sys.stdin)

connect_read_pipe函数将类似文件的对象作为pipe参数。 stdin是一个类似文件的对象。 从现在开始,所有从stdin读取的数据都将落入StreamReaderProtocol ,然后传入StreamReader

  1. 在事件循环中注册标准输出流stdout
w_transport, w_protocol = await loop.connect_write_pipe(FlowControlMixin, sys.stdout)

connect_write_pipe您需要传递一个协议工厂,该工厂创建实现StreamWriter.drain()流控制逻辑的协议实例。 该逻辑在FlowControlMixin类中实现。 StreamReaderProtocol继承自它。

  1. 创建StreamWriter实例。
writer = asyncio.StreamWriter(w_transport, w_protocol, reader, loop)

此类使用函数write()writelines()等将传递给它的数据转发到底层transport

protocol用于支持drain()函数等待底层传输已刷新其内部缓冲区并可以再次写入的那一刻。

reader是一个可选参数,可以是None ,它还用于支持drain()函数,在此函数开始时检查是否为 reader 设置了异常,例如,由于连接丢失(相关对于套接字和双向连接),那么drain()也会抛出异常。

您可以在这个很好的答案中阅读有关StreamWriterdrain()函数的更多信息。

更新 2:

要使用\\r\\n分隔符读取行,可以使用readuntil

这是您可以从 stdin 异步读取的另一种方式(一次读取一行)。

async def async_read_stdin()->str:
    loop = asyncio.get_event_loop()
    return await loop.run_in_executor(None, sys.stdin.readline)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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