Подтвердить что ты не робот

Тайм-аут подпроцесса Python?

Есть ли какой-либо аргумент или параметры для установки тайм-аута для подпроцесса Python.Popen?

Что-то вроде этого:

subprocess.Popen(['..'], ..., timeout=20)?

4b9b3361

Ответ 1

Я бы посоветовал взглянуть на Timer class в модуле потоковой передачи. Я использовал его для реализации тайм-аута для Popen.

Сначала создайте обратный вызов:

    def timeout( p ):
        if p.poll() is None:
            print 'Error: process taking too long to complete--terminating'
            p.kill()

Затем откройте процесс:

    proc = Popen( ... )

Затем создайте таймер, который вызовет обратный вызов, передающий ему процесс.

    t = threading.Timer( 10.0, timeout, [proc] )
    t.start()
    t.join()

Где-то позже в программе вы можете добавить строку:

    t.cancel()

В противном случае программа python будет продолжать работать до тех пор, пока таймер не закончит работу.

EDIT: мне сообщили, что существует условие гонки, что подпроцесс p может заканчиваться между вызовами p.poll() и p.kill(). Я считаю, что следующий код может исправить это:

    import errno

    def timeout( p ):
        if p.poll() is None:
            try:
                p.kill()
                print 'Error: process taking too long to complete--terminating'
            except OSError as e:
                if e.errno != errno.ESRCH:
                    raise

Хотя вы можете очистить обработку исключений, чтобы конкретно обрабатывать только конкретное исключение, которое происходит, когда подпроцесс уже завершен нормально.

Ответ 2

subprocess.Popen не блокируется, поэтому вы можете сделать что-то вроде этого:

import time

p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
  p.kill()
  print 'timed out'
else:
  print p.communicate()

У него есть недостаток в том, что вы должны всегда ждать не менее 20 секунд, чтобы он закончил.

Ответ 3

import subprocess, threading

class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None

    def run(self, timeout):
        def target():
            print 'Thread started'
            self.process = subprocess.Popen(self.cmd, shell=True)
            self.process.communicate()
            print 'Thread finished'

        thread = threading.Thread(target=target)
        thread.start()

        thread.join(timeout)
        if thread.is_alive():
            print 'Terminating process'
            self.process.terminate()
            thread.join()
        print self.process.returncode

command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)

Результат этого должен быть:

Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15

где можно видеть, что при первом выполнении процесс завершился правильно (код возврата 0), а во втором - завершение процесса (код возврата -15).

Я не тестировал в окнах; но, помимо обновления команды example, я думаю, что она должна работать, поскольку я не нашел в документации ничего, что говорит о том, что thread.join или process.terminate не поддерживается.

Ответ 4

Вы могли бы сделать

from twisted.internet import reactor, protocol, error, defer

class DyingProcessProtocol(protocol.ProcessProtocol):
    def __init__(self, timeout):
        self.timeout = timeout

    def connectionMade(self):
        @defer.inlineCallbacks
        def killIfAlive():
            try:
                yield self.transport.signalProcess('KILL')
            except error.ProcessExitedAlready:
                pass

        d = reactor.callLater(self.timeout, killIfAlive)

reactor.spawnProcess(DyingProcessProtocol(20), ...)

используя API-интерфейс скрученного асинхронного процесса.

Ответ 5

Авто-тайм-аут подпроцесса python не встроен, поэтому вам придется создавать свои собственные.

Это работает для меня на Ubuntu 12.10 под управлением python 2.7.3

Поместите это в файл с именем test.py

#!/usr/bin/python
import subprocess
import threading

class RunMyCmd(threading.Thread):
    def __init__(self, cmd, timeout):
        threading.Thread.__init__(self)
        self.cmd = cmd 
        self.timeout = timeout

    def run(self):
        self.p = subprocess.Popen(self.cmd)
        self.p.wait()

    def run_the_process(self):
        self.start()
        self.join(self.timeout)

        if self.is_alive():
            self.p.terminate()   #if your process needs a kill -9 to make 
                                 #it go away, use self.p.kill() here instead.

            self.join()

RunMyCmd(["sleep", "20"], 3).run_the_process()

Сохраните его и запустите:

python test.py

Команда sleep 20 занимает 20 секунд. Если он не завершится через 3 секунды (это не произойдет), процесс прекратится.

[email protected]:~$  python test.py 
[email protected]:~$ 

Между тем, когда процесс запускается, выполняется три секунды, и оно завершается.

Ответ 6

К сожалению, такого решения нет. Мне удалось сделать это с помощью потокового таймера, который будет запускаться вместе с процессом, который убьет его после таймаута, но я столкнулся с некоторыми проблемами устаревших файловых дескрипторов из-за процессов зомби или некоторых таких.

Ответ 7

Нет времени. Я предполагаю, что вы ищете, чтобы убить подпроцесс через некоторое время. Поскольку вы можете сигнализировать о подпроцессе, вы также можете его убить.

общий подход к отправке сигнала на подпроцесс:

proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)

Вы можете использовать этот механизм для завершения после периода ожидания.

Ответ 8

В Python 3.3 также имеется аргумент timeout для вспомогательных вспомогательных функций в модуле подпроцесса.

https://docs.python.org/3/library/subprocess.html

Ответ 9

Да, https://pypi.python.org/pypi/python-subprocess2 расширит модуль Popen двумя дополнительными функциями,

Popen.waitUpTo(timeout=seconds)

Это позволит подождать до определенного количества секунд для завершения процесса, в противном случае вернуть None

и

Popen.waitOrTerminate

Это будет ждать до одной точки, а затем вызовет .terminate(), затем .kill(), одну или другую или некоторую комбинацию обоих, см. документы для получения полной информации:

http://htmlpreview.github.io/?https://github.com/kata198/python-subprocess2/blob/master/doc/subprocess2.html

Ответ 10

Для Linux вы можете использовать сигнал. Это зависит от платформы, поэтому для Windows требуется другое решение. Однако он может работать с Mac.

def launch_cmd(cmd, timeout=0):
    '''Launch an external command

    It launchs the program redirecting the program STDIO
    to a communication pipe, and appends those responses to
    a list.  Waits for the program to exit, then returns the
    ouput lines.

    Args:
        cmd: command Line of the external program to launch
        time: time to wait for the command to complete, 0 for indefinitely
    Returns:
        A list of the response lines from the program    
    '''

    import subprocess
    import signal

    class Alarm(Exception):
        pass

    def alarm_handler(signum, frame):
        raise Alarm

    lines = []

    if not launch_cmd.init:
        launch_cmd.init = True
        signal.signal(signal.SIGALRM, alarm_handler)

    p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    signal.alarm(timeout)  # timeout sec

    try:
        for line in p.stdout:
            lines.append(line.rstrip())
        p.wait()
        signal.alarm(0)  # disable alarm
    except:
        print "launch_cmd taking too long!"
        p.kill()

    return lines        
launch_cmd.init = False