18.5.6. Subprocess

18.5.6.1. Windows event loop

在Windows上,默认事件循环为SelectorEventLoop,不支持子流程。应使用ProactorEventLoop在Windows上使用它的示例:

import asyncio, sys

if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)

18.5.6.2. Create a subprocess: high-level API using Process

coroutine asyncio.create_subprocess_exec(*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)

创建子过程。

limit参数设置传递到StreamReader的缓冲区限制。有关其他参数,请参阅AbstractEventLoop.subprocess_exec()

返回Process实例。

此函数是coroutine

coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)

运行shell命令cmd

limit参数设置传递到StreamReader的缓冲区限制。有关其他参数,请参阅AbstractEventLoop.subprocess_shell()

返回Process实例。

应用程序有责任确保适当地引用所有空格和元字符以避免shell注入漏洞。shlex.quote()函数可用于正确地转义将用于构建shell命令的字符串中的空格和shell元字符。

此函数是coroutine

使用AbstractEventLoop.connect_read_pipe()AbstractEventLoop.connect_write_pipe()方法来连接管道。

18.5.6.3. Create a subprocess: low-level API using subprocess.Popen

使用subprocess模块异步运行子进程。

coroutine AbstractEventLoop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

从一个或多个字符串参数(编码到filesystem encoding的字符串或字节字符串)创建子过程,其中第一个字符串指定要执行的程序,其余字符串指定程序的参数。(因此,一起的字符串参数形成程序的sys.argv值,假设它是一个Python脚本。)这类似于使用shell = False调用的标准库subprocess.Popen类,以及作为第一个参数传递的字符串列表;然而,Popen采用单个参数,它是字符串列表,subprocess_exec()接受多个字符串参数。

protocol_factory必须实例化asyncio.SubprocessProtocol类的子类。

其他参数:

返回一对(transport, 协议),其中transportBaseSubprocessTransport

此方法是coroutine

请参阅subprocess.Popen类的构造函数。

coroutine AbstractEventLoop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

使用平台的“shell”语法从cmd创建子过程,该子过程是一个字符串或编码为filesystem encoding的字节字符串。这类似于使用shell=True调用的标准库subprocess.Popen类。

protocol_factory必须实例化asyncio.SubprocessProtocol类的子类。

有关其余参数的更多详细信息,请参见subprocess_exec()

返回一对(transport, 协议),其中transportBaseSubprocessTransport

应用程序有责任确保适当地引用所有空格和元字符以避免shell注入漏洞。shlex.quote()函数可用于正确地转义将用于构建shell命令的字符串中的空格和shell元字符。

此方法是coroutine

18.5.6.4. Constants

asyncio.subprocess.PIPE

Special value that can be used as the stdin, stdout or stderr argument to create_subprocess_shell() and create_subprocess_exec() and indicates that a pipe to the standard stream should be opened.

asyncio.subprocess.STDOUT

可用作create_subprocess_shell()create_subprocess_exec()stderr参数的特殊值,表示标准错误应该进入相同的句柄作为标准输出。

asyncio.subprocess.DEVNULL

Special value that can be used as the stdin, stdout or stderr argument to create_subprocess_shell() and create_subprocess_exec() and indicates that the special file os.devnull will be used.

18.5.6.5. Process

class asyncio.subprocess.Process

create_subprocess_exec()create_subprocess_shell()函数创建的子过程。

Process类的API设计为接近subprocess.Popen类的API,但有一些区别:

此类为not thread safe另请参见Subprocess and threads部分。

coroutine wait()

等待子进程终止。设置并返回returncode属性。

此方法是coroutine

注意

当使用stdout=PIPEstderr=PIPE时,这将会死锁,子进程会向管道生成足够的输出,从而阻止等待操作系统管道缓冲区接受更多数据。在使用管道时避免使用communicate()方法。

coroutine communicate(input=None)

与进程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结束。等待进程终止。可选的输入参数应为要发送到子进程的数据,或None,如果没有数据应发送到子进程。输入的类型必须为字节。

communicate()返回一个元组(stdout_data, stderr_data)

如果在将输入写入stdin时引发BrokenPipeErrorConnectionResetError异常,则会忽略异常。它发生在进程退出之前所有数据都写入stdin。

请注意,如果要将数据发送到进程的stdin,则需要使用stdin=PIPE创建Process对象。类似地,为了在结果元组中获得除None之外的任何东西,您还需要给予stdout=PIPE和/或stderr=PIPE

此方法是coroutine

注意

读取的数据在内存中缓冲,因此如果数据大小较大或无限制,则不要使用此方法。

在版本3.4.2中更改:此方法现在忽略BrokenPipeErrorConnectionResetError

send_signal(signal)

将信号信号发送到子进程。

注意

在Windows上,SIGTERMterminate()的别名。CTRL_C_EVENTCTRL_BREAK_EVENT可发送到以creationflags参数(包括CREATE_NEW_PROCESS_GROUP)开始的进程。

terminate()

停止孩子。在Posix OS上,方法向子进程发送signal.SIGTERM在Windows上,调用Win32 API函数TerminateProcess()来停止子进程。

kill()

杀死孩子。在Posix OS上,函数向子进程发送SIGKILL在Windows上kill()terminate()的别名。

stdin

标准输入流(StreamWriter),None如果使用stdin=None创建过程。

stdout

标准输出流(StreamReader),None(如果使用stdout=None创建过程)。

stderr

如果使用stderr=None创建过程,则标准错误流(StreamReader),None

警告

使用communicate()方法,而不是.stdin.write.stdout.read.stderr.read以避免由于流暂停读取或写入和阻塞子进程而导致的死锁。

pid

进程的标识符。

请注意,对于由create_subprocess_shell()函数创建的进程,此属性是生成的shell的进程标识符。

returncode

退出时的进程的返回代码。None值表示进程尚未终止。

负值-N表示子节点由信号N(仅限Unix)终止。

18.5.6.6. 子进程和线程

asyncio支持从不同的线程中运行子进程,但有一些限制:

  • 事件循环必须在主线程中运行
  • 子监视器必须在主线程中实例化,然后从其他线程执行子进程。调用主线程中的get_child_watcher()函数来实例化子监视器。

asyncio.subprocess.Process类不是线程安全的。

18.5.6.7. 子过程示例

18.5.6.7.1. Subprocess using transport and protocol

子进程协议的示例,用于获取子进程的输出并等待子进程退出。子过程由AbstractEventLoop.subprocess_exec()方法创建:

import asyncio
import sys

class DateProtocol(asyncio.SubprocessProtocol):
    def __init__(self, exit_future):
        self.exit_future = exit_future
        self.output = bytearray()

    def pipe_data_received(self, fd, data):
        self.output.extend(data)

    def process_exited(self):
        self.exit_future.set_result(True)

@asyncio.coroutine
def get_date(loop):
    code = 'import datetime; print(datetime.datetime.now())'
    exit_future = asyncio.Future(loop=loop)

    # Create the subprocess controlled by the protocol DateProtocol,
    # redirect the standard output into a pipe
    create = loop.subprocess_exec(lambda: DateProtocol(exit_future),
                                  sys.executable, '-c', code,
                                  stdin=None, stderr=None)
    transport, protocol = yield from create

    # Wait for the subprocess exit using the process_exited() method
    # of the protocol
    yield from exit_future

    # Close the stdout pipe
    transport.close()

    # Read the output which was collected by the pipe_data_received()
    # method of the protocol
    data = bytes(protocol.output)
    return data.decode('ascii').rstrip()

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

date = loop.run_until_complete(get_date(loop))
print("Current date: %s" % date)
loop.close()

18.5.6.7.2. Subprocess using streams

使用Process类控制子进程和StreamReader类从标准输出读取的示例。子过程由create_subprocess_exec()函数创建:

import asyncio.subprocess
import sys

@asyncio.coroutine
def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

    # Create the subprocess, redirect the standard output into a pipe
    create = asyncio.create_subprocess_exec(sys.executable, '-c', code,
                                            stdout=asyncio.subprocess.PIPE)
    proc = yield from create

    # Read one line of output
    data = yield from proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Wait for the subprocess exit
    yield from proc.wait()
    return line

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

date = loop.run_until_complete(get_date())
print("Current date: %s" % date)
loop.close()