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,则进程产生的输出将追加到给定的文件。