mozprocess — 启动和管理进程

Mozprocess 是一个进程处理模块,它提供了一些超出 Python 的 subprocess 模块的功能

  • 更好地处理子进程,尤其是在 Windows 上

  • 能够在某个绝对时间段后或在没有任何数据写入 stdout/stderr 的某个时间段后使进程超时

  • 能够指定输出处理程序,这些处理程序将为进程生成的每一行输出调用

  • 能够指定将在进程超时和正常进程终止时调用的处理程序

买方自负!

大多数 mozprocess 开发发生在 Python 3 之前;mozprocess 的一些关键特性旨在解决 Python subprocess 中的 bug 和不足,而这些 bug 和不足在今天已不再是问题。如今我们知道 mozprocess 本身存在许多 bug,并且该模块难以找到现代的维护者:此模块维护不佳。

同时,mozprocess 拥有许多客户端,并为一些复杂的进程处理需求提供了便捷的解决方案。我们最好的建议

  • 对于常规的进程处理需求,请使用 Python subprocess - 不要使用 mozprocess

  • 对于带有可选的、简单的输出超时和/或输出处理的便捷进程处理,请使用 mozprocess.run_and_wait();这是最简单、最隔离和最现代的 mozprocess 接口

  • 对于更复杂的需求,请使用 mozprocess ProcessHandlerMixin/ProcessHandler,但请谨慎操作!

运行进程

mozprocess 包含两个类:ProcessHandler 继承自 ProcessHandlerMixin。

让我们看看如何运行一个进程。首先,应该至少使用一个参数实例化该类,该参数是一个命令(或由命令及其参数组成的列表)。然后可以使用 *run()* 方法启动进程。最后,*wait()* 方法将等待执行结束。

from mozprocess import processhandler

# under Windows replace by command = ['dir', '/a']
command = ['ls', '-l']
p = processhandler.ProcessHandler(command)
print("execute command: %s" % p.commandline)
p.run()
p.wait()

请注意,使用 *ProcessHandler* 而不是 *ProcessHandlerMixin* 将打印执行命令的输出。属性 *commandline* 提供了启动的命令。

收集进程输出

现在让我们考虑一个基本的 shell 脚本,它将打印从 1 到 5 的数字,并在每次打印之间等待 1 秒。此脚本将用作在后续示例中启动的命令。

proc_sleep_echo.sh:

#!/bin/sh

for i in 1 2 3 4 5
do
    echo $i
    sleep 1
done

如果您在 Windows 下运行,则无法使用之前的脚本(除非使用 Cygwin)。因此,您将使用以下脚本

proc_sleep_echo.bat:

@echo off
FOR %%A IN (1 2 3 4 5) DO (
    ECHO %%A
    REM if you have TIMEOUT then use it instead of PING
    REM TIMEOUT /T 1 /NOBREAK
    PING -n 2 127.0.0.1 > NUL
)

Mozprocess 允许指定自定义输出处理程序,以便在运行时收集进程输出。ProcessHandler 默认情况下会将所有输出写入 stdout。您还可以提供(给 ProcessHandler 或 ProcessHandlerMixin)一个函数或函数列表,这些函数将用作进程生成的每一行输出上的回调。

在以下示例中,命令的输出将存储在文件 *output.log* 中并在 stdout 中打印

import sys
from mozprocess import processhandler

fd = open('output.log', 'w')

def tostdout(line):
    sys.stdout.write("<%s>\n" % line)

def tofile(line):
    fd.write("<%s>\n" % line)

# under Windows you'll replace by 'proc_sleep_echo.bat'
command = './proc_sleep_echo.sh'
outputs = [tostdout, tofile]

p = processhandler.ProcessHandlerMixin(command, processOutputLine=outputs)
p.run()
p.wait()

fd.close()

可以保存进程输出(*obj = ProcessHandler(…, storeOutput=True)*),以便可以随时请求它(*obj.output*)。请注意,*stroreOutput* 的默认值为 *True*,因此无需在参数中提供它。

import time
import sys
from mozprocess import processhandler

command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat'

p = processhandler.ProcessHandler(command, storeOutput=True)
p.run()
for i in xrange(10):
    print(p.output)
    time.sleep(0.5)
p.wait()

在前面的示例中,您将看到 *p.output* 列表在增长。

执行

状态

可以通过 *poll()* 查询进程的状态,如果进程仍在运行,则返回 None,如果进程无错误结束则返回 0,如果进程被信号终止则返回负值(仅限 Unix)。

import time
import signal
from mozprocess import processhandler

command = './proc_sleep_echo.sh'
p = processhandler.ProcessHandler(command)
p.run()
time.sleep(2)
print("poll status: %s" % p.poll())
time.sleep(1)
p.kill(signal.SIGKILL)
print("poll status: %s" % p.poll())

超时

可以为 *run()* 方法提供超时。如果进程持续时间超过超时秒数,它将被停止。

执行后,如果达到超时,则属性 *timedOut* 将设置为 True。

也可以提供函数(*obj = ProcessHandler(…, onTimeout=functions)*),如果达到超时,则将调用这些函数。

from mozprocess import processhandler

def ontimeout():
    print("REACHED TIMEOUT")

command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat'
functions = [ontimeout]
p = processhandler.ProcessHandler(command, onTimeout=functions)
p.run(timeout=2)
p.wait()
print("timedOut = %s" % p.timedOut)

默认情况下,进程将在超时时被终止,但可以通过将 *kill_on_timeout* 设置为 *False* 来防止这种情况。

p = processhandler.ProcessHandler(command, onTimeout=functions, kill_on_timeout=False)
p.run(timeout=2)
p.wait()
print("timedOut = %s" % p.timedOut)

在这种情况下,超时后将无法获得任何输出,但进程仍将运行。

等待

可以等到进程退出,如前面使用 *wait()* 方法所示,或者等到超时结束(如果给出)。请注意,在后一种情况下,进程在超时后仍然处于活动状态。

command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat'
p = processhandler.ProcessHandler(command)
p.run()
p.wait(timeout=2)
print("timedOut = %s" % p.timedOut)
p.wait()

终止

您可以使用 *kill* 方法请求终止进程。如果在初始化进程处理程序类时将参数“ignore_children”设置为 False,则所有子进程也将被终止。

除了 Windows 之外,您还可以指定用于终止进程的信号(例如:*kill(signal.SIGKILL)*)。

import time
from mozprocess import processhandler

command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat'
p = processhandler.ProcessHandler(command)
p.run()
time.sleep(2)
p.kill()

执行结束

您可以提供一个函数或函数列表,以便在进程结束时调用,使用初始化参数 *onFinish*。

from mozprocess import processhandler

def finish():
    print("Finished!!")

command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat'

p = processhandler.ProcessHandler(command, onFinish=finish)
p.run()
p.wait()

子进程管理

考虑以下脚本

proc_child.sh:

#!/bin/sh
for i in a b c d e
do
    echo $i
    sleep 1
done

proc_parent.sh:

#!/bin/sh
./proc_child.sh
for i in 1 2 3 4 5
do
    echo $i
    sleep 1
done

对于 Windows 用户,请考虑

proc_child.bat:

@echo off
FOR %%A IN (a b c d e) DO (
    ECHO %%A
    REM TIMEOUT /T 1 /NOBREAK
    PING -n 2 127.0.0.1 > NUL
)

proc_parent.bat:

@echo off
call proc_child.bat
FOR %%A IN (1 2 3 4 5) DO (
    ECHO %%A
    REM TIMEOUT /T 1 /NOBREAK
    PING -n 2 127.0.0.1 > NUL
)

对于启动其他进程的进程,mozprocess 允许您获取正在运行的子进程的状态、等待子进程终止以及终止子进程。

忽略子进程

默认情况下,*ignore_children* 选项为 False。在这种情况下,终止主进程将同时终止其所有子进程。

import time
from mozprocess import processhandler

def finish():
    print("Finished")

command = './proc_parent.sh'
p = processhandler.ProcessHandler(command, ignore_children=False, onFinish=finish)
p.run()
time.sleep(2)
print("kill")
p.kill()

如果 *ignore_children* 设置为 *True*,则终止将仅应用于主进程,该进程将在停止(join)之前等待子进程执行结束。

import time
from mozprocess import processhandler

def finish():
    print("Finished")

command = './proc_parent.sh'
p = processhandler.ProcessHandler(command, ignore_children=True, onFinish=finish)
p.run()
time.sleep(2)
print("kill")
p.kill()

API 文档

mozprocess.run_and_wait(cwd=None, env=None, text=True, timeout=None, timeout_handler=None, output_timeout=None, output_timeout_handler=None, output_line_handler=None)

运行一个进程并等待其完成,并可选地支持逐行输出处理和超时。

在超时或输出超时时,回调应终止进程;许多客户端在超时回调中使用 mozcrash.kill_and_get_minidump()。

run_and_wait 并非旨在作为 subprocess 的通用替代品。需要不同选项或行为的客户端应直接使用 subprocess。

参数:
  • args – 要运行的命令。可以是字符串或列表。

  • cwd – 命令的工作目录。

  • env – 用于进程的环境(默认为 os.environ)。

  • text – 如果为 True,则以文本模式打开流;否则使用二进制模式。

  • timeout – 在调用 timeout_handler 之前等待进程完成的秒数

  • timeout_handler – 如果达到超时则要调用的函数

  • output_timeout – 等待进程生成输出的秒数

  • output_timeout_handler – 如果达到 output_timeout 则要调用的函数

  • output_line_handler – 对于进程的每一行输出要调用的函数

class mozprocess.ProcessHandlerMixin(cmd, args=None, cwd=None, env=None, ignore_children=False, kill_on_timeout=True, processOutputLine=(), processStderrLine=(), onTimeout=(), onFinish=(), **kwargs)

用于启动和操作本地进程的类。

参数:
  • cmd – 要运行的命令。可以是字符串或列表。如果指定为列表,则第一个元素将被解释为命令,所有其他元素将被解释为该命令的参数。

  • args – 传递给命令的参数列表(默认为 None)。当cmd指定为列表时,不能设置此参数。

  • cwd – 命令的工作目录(默认为 None)。

  • env – 是进程要使用的环境(默认为 os.environ)。

  • ignore_children – 当为 True 时,导致系统忽略子进程,默认为 False(跟踪子进程)。

  • kill_on_timeout – 当为 True 时,当达到超时时间时,进程将被杀死。当为 False 时,调用者负责杀死进程。如果这样做,可能会导致对 wait() 的调用无限期挂起。(默认为 True。)

  • processOutputLine – 为进程产生的每一行输出调用的函数或函数列表(默认为空列表)。

  • processStderrLine – 为进程产生的每一行错误输出 - stderr - 调用的函数或函数列表(默认为空列表)。如果未指定此参数,则 stderr 行将发送到processOutputLine回调。

  • onTimeout – 进程超时时要调用的函数或函数列表。

  • onFinish – 进程正常终止且未超时时要调用的函数或函数列表。

  • kwargs – 要直接传递到 Popen 的其他关键字参数。

注意:默认情况下将跟踪子进程。如果由于任何原因我们无法跟踪子进程并且 ignore_children 设置为 False,那么我们将回退到仅跟踪根进程。回退将被记录。

__init__(cmd, args=None, cwd=None, env=None, ignore_children=False, kill_on_timeout=True, processOutputLine=(), processStderrLine=(), onTimeout=(), onFinish=(), **kwargs)
property commandline

命令行的字符串值(命令 + 参数)

kill(sig=None, timeout=None)

杀死托管进程。

如果使用“ignore_children=False”(默认值)创建进程,那么它也将杀死由其生成的所以子进程。如果在创建进程时指定了“ignore_children=True”,则只会杀死根进程。

请注意,这不会管理任何状态,保存任何输出等,它会立即杀死进程。

参数:

sig – 用于杀死进程的信号,默认为 SIGKILL(对 Windows 无效)

run(timeout=None, outputTimeout=None)

启动进程。

如果 timeout 不为 None,则允许进程继续运行指定秒数,然后将其杀死。如果进程因超时而被杀死,则将调用 onTimeout 处理程序。

如果 outputTimeout 不为 None,则允许进程继续运行指定秒数,在此期间不产生任何输出,然后将其杀死。

property timedOut

如果进程因任何原因超时,则为 True。

wait(timeout=None)

等待所有输出读取完毕且进程终止。

如果 timeout 不为 None,则将在 timeout 秒后返回。此超时只会导致 wait 函数返回,不会杀死进程。

返回进程退出代码值:- 如果进程尚未终止,则为 None - 如果进程被信号 N 杀死,则为负数(仅限 Unix) - 如果进程无故障结束,则为“0”

class mozprocess.ProcessHandler(cmd, logfile=None, stream=True, storeOutput=True, **kwargs)

用于处理具有默认输出处理程序的进程的便捷类。

默认情况下,所有输出都发送到 stdout。可以通过将stream参数设置为 None 来禁用此功能。

如果指定了 processOutputLine 关键字参数,则此参数指定的函数或函数列表将为每一行输出调用;如果 stream 为 True(默认值),则输出将不会自动写入 stdout。

如果 storeOutput==True,则进程产生的输出将保存为 self.output。

如果 logfile 不为 None,则进程产生的输出将追加到给定的文件。