Python2如何让子进程超时

       总结:

        python3 subprocess  wait communicate 都有timeout 

       python2 没有,且一些属性都不一样

      有一天我碰到一个需求,我需要和子进程交互,并且需要让它在一定时间内超时。不幸的,Python2没有让communicate方法超时的办法,所以communicate会一直运行到返回或者子进程自己关闭它。我在StackOverflow上找到很多中办法来解决这个问题,但是我想我最喜欢的办法是使用Python的Threading模块的Timer类:

import subprocess
from threading import Timer

kill = lambda process: process.kill()
cmd = ['ping', 'www.google.com']
ping = subprocess.Popen(
    cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
my_timer = Timer(5, kill, [ping])
try:
    my_timer.start()
    stdout, stderr = ping.communicate()
finally:
    my_timer.cancel()

这个例子和我的实际需求并不完全一致,但是已经很像了。首先,这里我们有一个会一直运行下去的进程,并且我们需要和这个进程交互。在Linux上,如果你调用ping,那么它就会一直运行下去。这就是一个很好的例子。这里我写了一个lambda函数killing,这个函数会调用进程的kill方法。当我启动ping命令,并把它放在计时器里,这个计时器五秒超时,然后启动定时器。当这个进程运行时,我们搜集进程的标准输出和错误输出,直到进程死掉。最后,我们停止计时器清场。

 

Python3.5增加了一个能够接受一个timeout参数的run函数。根据文档,这个timeout参数会被传递给子进程的communicate方法,并且进程超时时会抛出一个TimeoutExpired异常。让我们来试一试:

>>> import subprocess
>>> cmd = ['ping', 'www.google.com']
>>> subprocess.run(cmd, timeout=5)
PING www.google.com (216.58.216.196) 56(84) bytes of data.
64 bytes from ord31s21-in-f4.1e100.net (216.58.216.196): icmp_seq=1 ttl=55 time=16.3 ms
64 bytes from ord31s21-in-f4.1e100.net (216.58.216.196): icmp_seq=2 ttl=55 time=19.4 ms
64 bytes from ord31s21-in-f4.1e100.net (216.58.216.196): icmp_seq=3 ttl=55 time=20.0 ms
64 bytes from ord31s21-in-f4.1e100.net (216.58.216.196): icmp_seq=4 ttl=55 time=19.4 ms
64 bytes from ord31s21-in-f4.1e100.net (216.58.216.196): icmp_seq=5 ttl=55 time=17.0 ms
Traceback (most recent call last):
  Python Shell, prompt 3, line 1
  File "/usr/local/lib/python3.5/subprocess.py", line 711, in run
    stderr=stderr)
subprocess.TimeoutExpired: Command '['ping', 'www.google.com']' timed out after 5 seconds

显然确实就像文档中所说。这真的很有用,通常我们需要捕获这个异常

>>> try:
...     subprocess.run(cmd, timeout=5)
... except subprocess.TimeoutExpired:
...     print('process ran too long')
... 
PING www.google.com (216.58.216.196) 56(84) bytes of data.
64 bytes from ord31s21-in-f196.1e100.net (216.58.216.196): icmp_seq=1 ttl=55 time=18.3 ms
64 bytes from ord31s21-in-f196.1e100.net (216.58.216.196): icmp_seq=2 ttl=55 time=21.1 ms
64 bytes from ord31s21-in-f196.1e100.net (216.58.216.196): icmp_seq=3 ttl=55 time=22.7 ms
64 bytes from ord31s21-in-f196.1e100.net (216.58.216.196): icmp_seq=4 ttl=55 time=20.3 ms
64 bytes from ord31s21-in-f196.1e100.net (216.58.216.196): icmp_seq=5 ttl=55 time=16.8 ms
process ran too long

现在我们捕获到了这个异常,我们可以做些其他的事情,比如保存异常信息。有趣的是,事实上,在Python3.3 subprocess就有了timeout参数。你可以在subprocess.call, check_output, 和 check_call中使用timeout参数,在Popen.wait()中也有timeout参数。

猜你喜欢

转载自hugoren.iteye.com/blog/2389175