连接以 asyncio.subprocess.create_subprocess_exec() 开头的两个进程

Connect two processes started with asyncio.subprocess.create_subprocess_exec()

当使用旧学校 subprocess.Popen() API 启动两个进程时,我可以轻松地将一个进程的 标准输出 连接到 标准在另一个进程的 中,在使用 |:

连接命令时,以与 UNIX shell 相同的方式创建管道
from subprocess import Popen, PIPE

process_1 = Popen(['ls'], stdout = PIPE)
process_2 = Popen(['wc'], stdin = process_1.stdout)

process_1.wait()
process_2.wait()

在使用来自 asyncio.subprocess.create_subprocess_exec()(或类似)的异步 API 时,我如何才能完成相同的任务?这是我试过的:

from asyncio.events import get_event_loop
from asyncio.subprocess import PIPE, create_subprocess_exec

async def main():
    process_1 = await create_subprocess_exec('ls', stdout = PIPE)
    process_2 = await create_subprocess_exec('wc', stdin = process_1.stdout)

    await process_1.wait()
    await process_2.wait()

get_event_loop().run_until_complete(main())

但是对 create_subprocess_exec() 的第二次调用抱怨传递给 stdin 的参数没有 fileno(这是真的):

Traceback (most recent call last):
  File ".../test-async.py", line 11, in <module>
     get_event_loop().run_until_complete(main())
[...]
  File ".../test-async.py", line 6, in main
     process_2 = await create_subprocess_exec('wc', stdin = process_1.stdout)
[...]
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 1388, in _get_handles
     p2cread = stdin.fileno()
AttributeError: 'StreamReader' object has no attribute 'fileno'

如何获得与上述同步示例相同的结果?

在 asyncio 中,process.stdout is actually a StreamReader,不是文件对象。可以通过process._transport._proc.stdout访问文件对象。不幸的是,您将无法使用它,因为它已经在事件循环中注册以提供流接口 process.stdout.

处理该问题的一种方法是创建自己的管道并将文件描述符传递给子进程:

async def main():
    read, write = os.pipe()
    process_1 = await create_subprocess_exec('ls', stdout=write)
    os.close(write)
    process_2 = await create_subprocess_exec('wc', stdin=read, stdout=PIPE)
    os.close(read)
    return await process_2.stdout.read()

请注意,一旦第一个子进程启动,write 文件描述符就应该显式关闭(它不会自动关闭,除非您使用 subprocess.PIPE)。 read 文件描述符也需要关闭,如 here 所述。