Korzystanie z modułu "podproces" z timeoutem

Oto Kod Pythona do uruchomienia dowolnego polecenia zwracającego jego dane stdout lub wywołania wyjątku na niezerowych kodach wyjścia:

proc = subprocess.Popen(
    cmd,
    stderr=subprocess.STDOUT,  # Merge stdout and stderr
    stdout=subprocess.PIPE,
    shell=True)

communicate jest używane do oczekiwania na zakończenie procesu:

stdoutdata, stderrdata = proc.communicate()

Moduł subprocess nie obsługuje timeout--możliwość zabicia procesu działającego przez więcej niż X liczbę sekund--dlatego communicate może trwać wieczność.

Jaki jest najprostszy sposób implementacji timeoutów w programie Pythona przeznaczonym do pracy w systemie Windows a Linux?

Author: Sridhar Ratnakumar, 2009-07-28

30 answers

W Pythonie 3.3+:

from subprocess import STDOUT, check_output

output = check_output(cmd, stderr=STDOUT, timeout=seconds)

output jest ciągiem bajtów, który zawiera scalone dane stdout, stderr polecenia.

check_output podnosi CalledProcessError na niezerowy status wyjścia, jak określono w tekście pytania, w przeciwieństwie do metody proc.communicate().

Usunąłem shell=True ponieważ często jest używany niepotrzebnie. Zawsze możesz go dodać Z Powrotem, Jeśli cmd rzeczywiście tego wymaga. Jeśli dodasz shell=True, tzn. jeśli proces potomny wywoła własnych Potomków; check_output() może powrócić znacznie później niż timeout wskazuje, patrz błąd limitu czasu Podprocesu .

Funkcja timeout jest dostępna w Pythonie 2.x przez subprocess32 backport modułu podprocesu 3.2+.

 184
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-20 14:47:36

Niewiele wiem o szczegółach niskiego poziomu, ale biorąc pod uwagę, że w python 2.6 API oferuje możliwość oczekiwania na wątki i zakończyć procesy, a co z uruchomieniem procesu w osobnym wątek?

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)

Wyjście tego fragmentu w mojej maszynie to:

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

Gdzie widać, że w pierwszym wykonaniu proces zakończone poprawnie( kod powrotu 0), natomiast w drugim proces został zakończony(kod powrotu -15).

Nie testowałem w windows; ale oprócz aktualizacji przykładu polecenie, myślę, że powinno działać, ponieważ nie znalazłem w dokumentuje wszystko, co mówi ten wątek.Dołącz lub przetworz.Zakończ nie jest obsługiwana.
 207
Author: jcollado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-28 07:39:08

Odpowiedź Jcollado można uprościć używając wątku .Timer klasa:

import shlex
from subprocess import Popen, PIPE
from threading import Timer

def run(cmd, timeout_sec):
    proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
    timer = Timer(timeout_sec, proc.kill)
    try:
        timer.start()
        stdout, stderr = proc.communicate()
    finally:
        timer.cancel()

# Examples: both take 1 second
run("sleep 1", 5)  # process ends normally at 1 second
run("sleep 5", 1)  # timeout happens at 1 second
 134
Author: sussudio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-18 01:27:29

Jeśli jesteś na Unixie,

import signal
  ...
class Alarm(Exception):
    pass

def alarm_handler(signum, frame):
    raise Alarm

signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(5*60)  # 5 minutes
try:
    stdoutdata, stderrdata = proc.communicate()
    signal.alarm(0)  # reset the alarm
except Alarm:
    print "Oops, taking too long!"
    # whatever else
 82
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-12 01:54:07

Oto rozwiązanie Alexa Martelli jako modułu z prawidłowym zabijaniem procesów. Inne podejścia nie działają, ponieważ nie używają proc.communicate (). Więc jeśli masz proces, który wytwarza dużo danych wyjściowych, wypełni swój bufor wyjściowy, a następnie zablokuje, aż coś z niego przeczytasz.

from os import kill
from signal import alarm, signal, SIGALRM, SIGKILL
from subprocess import PIPE, Popen

def run(args, cwd = None, shell = False, kill_tree = True, timeout = -1, env = None):
    '''
    Run a command with a timeout after which it will be forcibly
    killed.
    '''
    class Alarm(Exception):
        pass
    def alarm_handler(signum, frame):
        raise Alarm
    p = Popen(args, shell = shell, cwd = cwd, stdout = PIPE, stderr = PIPE, env = env)
    if timeout != -1:
        signal(SIGALRM, alarm_handler)
        alarm(timeout)
    try:
        stdout, stderr = p.communicate()
        if timeout != -1:
            alarm(0)
    except Alarm:
        pids = [p.pid]
        if kill_tree:
            pids.extend(get_process_children(p.pid))
        for pid in pids:
            # process might have died before getting to this line
            # so wrap to avoid OSError: no such process
            try: 
                kill(pid, SIGKILL)
            except OSError:
                pass
        return -9, '', ''
    return p.returncode, stdout, stderr

def get_process_children(pid):
    p = Popen('ps --no-headers -o pid --ppid %d' % pid, shell = True,
              stdout = PIPE, stderr = PIPE)
    stdout, stderr = p.communicate()
    return [int(p) for p in stdout.split()]

if __name__ == '__main__':
    print run('find /', shell = True, timeout = 3)
    print run('find', shell = True)
 43
Author: Björn Lindqvist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-11 00:04:03

Zaskoczony nikt nie wspomniał o użyciu timeout

timeout 5 ping -c 3 somehost

To nie będzie działać dla każdego przypadku użycia oczywiście, ale jeśli masz do czynienia z prostym skryptem, to jest trudne do pokonania.

Dostępne również jako gtimeout w coreutils poprzez homebrew dla użytkowników komputerów mac.

 18
Author: Karsten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-21 04:43:41

Zmodyfikowałem sussudio odpowiedz Teraz funkcja zwraca: (returncode, stdout, stderr, timeout) - stdout i stderr jest dekodowane do utf-8 string

def kill_proc(proc, timeout):
  timeout["value"] = True
  proc.kill()

def run(cmd, timeout_sec):
  proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  timeout = {"value": False}
  timer = Timer(timeout_sec, kill_proc, [proc, timeout])
  timer.start()
  stdout, stderr = proc.communicate()
  timer.cancel()
  return proc.returncode, stdout.decode("utf-8"), stderr.decode("utf-8"), timeout["value"]
 17
Author: Michal Zmuda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-28 16:59:18

timeout jest teraz wspierane przez call() i communicate() w module podprocesowym (od Python3.3):

import subprocess

subprocess.call("command", timeout=20, shell=True)

Wywoła polecenie i wywoła wyjątek

subprocess.TimeoutExpired

Jeśli polecenie nie zakończy się po 20 sekundach.

Możesz następnie obsłużyć wyjątek, aby kontynuować kod, coś w stylu:

try:
    subprocess.call("command", timeout=20, shell=True)
except subprocess.TimeoutExpired:
    # insert code here
Mam nadzieję, że to pomoże.
 17
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-22 16:59:59

Nie wiem dlaczego nie jest wymieniany, ale od Pythona 3.5 JEST NOWY subprocess.run polecenie uniwersalne (które ma zastąpić check_call, check_output ...) i który ma również parametr timeout.

Podproces.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)

Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.

Wyświetla wyjątek subprocess.TimeoutExpired, gdy timeout wygasł.

 13
Author: Jean-François Fabre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-30 09:19:58

Inną opcją jest zapisanie do pliku tymczasowego, aby zapobiec blokowaniu stdout zamiast konieczności przeszukiwania za pomocą communicate (). To działało dla mnie tam, gdzie inne odpowiedzi nie; na przykład w windows.

    outFile =  tempfile.SpooledTemporaryFile() 
    errFile =   tempfile.SpooledTemporaryFile() 
    proc = subprocess.Popen(args, stderr=errFile, stdout=outFile, universal_newlines=False)
    wait_remaining_sec = timeout

    while proc.poll() is None and wait_remaining_sec > 0:
        time.sleep(1)
        wait_remaining_sec -= 1

    if wait_remaining_sec <= 0:
        killProc(proc.pid)
        raise ProcessIncompleteError(proc, timeout)

    # read temp streams from start
    outFile.seek(0);
    errFile.seek(0);
    out = outFile.read()
    err = errFile.read()
    outFile.close()
    errFile.close()
 10
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-10 12:51:15

Dodałem rozwiązanie z threadingiem z jcollado do mojego modułu Pythona easyprocess.

Zainstaluj:

pip install easyprocess

Przykład:

from easyprocess import Proc

# shell is not supported!
stdout=Proc('ping localhost').call(timeout=1.5).stdout
print stdout
 6
Author: ponty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-16 18:02:26

Oto moje rozwiązanie, używałem wątku i zdarzenia:

import subprocess
from threading import Thread, Event

def kill_on_timeout(done, timeout, proc):
    if not done.wait(timeout):
        proc.kill()

def exec_command(command, timeout):

    done = Event()
    proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    watcher = Thread(target=kill_on_timeout, args=(done, timeout, proc))
    watcher.daemon = True
    watcher.start()

    data, stderr = proc.communicate()
    done.set()

    return data, stderr, proc.returncode

W akcji:

In [2]: exec_command(['sleep', '10'], 5)
Out[2]: ('', '', -9)

In [3]: exec_command(['sleep', '10'], 11)
Out[3]: ('', '', 0)
 6
Author: rsk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-02 19:59:19

Rozwiązaniem, którego używam jest prefiks polecenia powłoki z timelimit . Jeśli komand trwa zbyt długo, timelimit go zatrzyma, a Popen będzie miał kod zwrotny ustawiony przez timelimit. Jeśli jest > 128, oznacza to, że timelimit zabił proces.

Zobacz także podproces Pythona z timeoutem i dużym wyjściem (>64K)

 5
Author: bortzmeyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:41

Jeśli używasz Pythona 2, Spróbuj

import subprocess32

try:
    output = subprocess32.check_output(command, shell=True, timeout=3)
except subprocess32.TimeoutExpired as e:
    print e
 5
Author: ThOong Ku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-15 08:10:19

Prepending Linux command timeout nie jest złe obejście i to działało dla mnie.

cmd = "timeout 20 "+ cmd
subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(output, err) = p.communicate()
 5
Author: Vikram Hosakote,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-09 05:22:39

Zaimplementowałem to, co mogłem zebrać z kilku z nich. To działa w Windows, a ponieważ jest to wiki społeczności, pomyślałem, że podzielę się również moim kodem: {]}

class Command(threading.Thread):
    def __init__(self, cmd, outFile, errFile, timeout):
        threading.Thread.__init__(self)
        self.cmd = cmd
        self.process = None
        self.outFile = outFile
        self.errFile = errFile
        self.timed_out = False
        self.timeout = timeout

    def run(self):
        self.process = subprocess.Popen(self.cmd, stdout = self.outFile, \
            stderr = self.errFile)

        while (self.process.poll() is None and self.timeout > 0):
            time.sleep(1)
            self.timeout -= 1

        if not self.timeout > 0:
            self.process.terminate()
            self.timed_out = True
        else:
            self.timed_out = False

Następnie z innej klasy lub pliku:

        outFile =  tempfile.SpooledTemporaryFile()
        errFile =   tempfile.SpooledTemporaryFile()

        executor = command.Command(c, outFile, errFile, timeout)
        executor.daemon = True
        executor.start()

        executor.join()
        if executor.timed_out:
            out = 'timed out'
        else:
            outFile.seek(0)
            errFile.seek(0)
            out = outFile.read()
            err = errFile.read()

        outFile.close()
        errFile.close()
 3
Author: joslinm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-02 18:47:14

Gdy zrozumiesz pełną obsługę maszyn w *Unixie, łatwo znajdziesz prostsze rozwiązanie:

Rozważ ten prosty przykład, jak sprawić, by timeoutable communicate() meth za pomocą select.select () (dostępne obecnie na *nix). Można to również zapisać za pomocą epoll / poll/kqueue, ale wybrać.wariant select() może być dla Ciebie dobrym przykładem. I główne ograniczenia select.select () (speed i 1024 max fds) nie nadają się do zastosowania w twoim zadaniu.

To działa pod *nix, nie tworzy wątków, nie używa sygnałów, może być pobierany z dowolnego wątku (nie tylko głównego) i szybko można odczytać 250MB / s danych ze stdout na moim komputerze (i5 2.3 ghz).

Jest problem w join ' ING stdout/stderr na końcu komunikacji. Jeśli masz ogromną wydajność programu, może to prowadzić do dużego zużycia pamięci. Można jednak wywołać metodę communicate() kilka razy z mniejszymi timeoutami.

class Popen(subprocess.Popen):
    def communicate(self, input=None, timeout=None):
        if timeout is None:
            return subprocess.Popen.communicate(self, input)

        if self.stdin:
            # Flush stdio buffer, this might block if user
            # has been writing to .stdin in an uncontrolled
            # fashion.
            self.stdin.flush()
            if not input:
                self.stdin.close()

        read_set, write_set = [], []
        stdout = stderr = None

        if self.stdin and input:
            write_set.append(self.stdin)
        if self.stdout:
            read_set.append(self.stdout)
            stdout = []
        if self.stderr:
            read_set.append(self.stderr)
            stderr = []

        input_offset = 0
        deadline = time.time() + timeout

        while read_set or write_set:
            try:
                rlist, wlist, xlist = select.select(read_set, write_set, [], max(0, deadline - time.time()))
            except select.error as ex:
                if ex.args[0] == errno.EINTR:
                    continue
                raise

            if not (rlist or wlist):
                # Just break if timeout
                # Since we do not close stdout/stderr/stdin, we can call
                # communicate() several times reading data by smaller pieces.
                break

            if self.stdin in wlist:
                chunk = input[input_offset:input_offset + subprocess._PIPE_BUF]
                try:
                    bytes_written = os.write(self.stdin.fileno(), chunk)
                except OSError as ex:
                    if ex.errno == errno.EPIPE:
                        self.stdin.close()
                        write_set.remove(self.stdin)
                    else:
                        raise
                else:
                    input_offset += bytes_written
                    if input_offset >= len(input):
                        self.stdin.close()
                        write_set.remove(self.stdin)

            # Read stdout / stderr by 1024 bytes
            for fn, tgt in (
                (self.stdout, stdout),
                (self.stderr, stderr),
            ):
                if fn in rlist:
                    data = os.read(fn.fileno(), 1024)
                    if data == '':
                        fn.close()
                        read_set.remove(fn)
                    tgt.append(data)

        if stdout is not None:
            stdout = ''.join(stdout)
        if stderr is not None:
            stderr = ''.join(stderr)

        return (stdout, stderr)
 2
Author: Vadim Fint,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-11 13:46:13

Możesz to zrobić używając select

import subprocess
from datetime import datetime
from select import select

def call_with_timeout(cmd, timeout):
    started = datetime.now()
    sp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    while True:
        p = select([sp.stdout], [], [], timeout)
        if p[0]:
            p[0][0].read()
        ret = sp.poll()
        if ret is not None:
            return ret
        if (datetime.now()-started).total_seconds() > timeout:
            sp.kill()
            return None
 2
Author: dspeyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-01 04:08:24

Używałem killableprocess pomyślnie na Windows, Linux i Mac. Jeśli używasz Pythona Cygwin, będziesz potrzebował wersji killableprocess OSAFA, ponieważ w przeciwnym razie natywne procesy Windows nie zostaną zabite.

 1
Author: Heikki Toivonen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-12 23:15:47

Chociaż nie oglądałem go szeroko, ten dekorator , który znalazłem w ActiveState, wydaje się być całkiem przydatny do tego typu rzeczy. Wraz z subprocess.Popen(..., close_fds=True), przynajmniej jestem gotowy do tworzenia skryptów powłokowych w Pythonie.

 1
Author: Ehtesh Choudhury,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-24 01:42:21

To rozwiązanie zabija drzewo procesów w przypadku shell = True, przekazuje parametry procesowi (lub nie), ma limit czasu i pobiera stdout, stderr i wynik procesu wywołania zwrotnego (używa psutil do kill_proc_tree). Było to oparte na kilku rozwiązaniach opublikowanych w SO, w tym jcollado 's. Posting in response to comments by Anson and jradice in jcollado' s answer. Testowane w systemie Windows Srvr 2012 i Ubuntu 14.04. Pamiętaj, że w Ubuntu musisz zmienić rodzica.dzieci(...) telefon do rodzica.get_children(...).

def kill_proc_tree(pid, including_parent=True):
  parent = psutil.Process(pid)
  children = parent.children(recursive=True)
  for child in children:
    child.kill()
  psutil.wait_procs(children, timeout=5)
  if including_parent:
    parent.kill()
    parent.wait(5)

def run_with_timeout(cmd, current_dir, cmd_parms, timeout):
  def target():
    process = subprocess.Popen(cmd, cwd=current_dir, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

    # wait for the process to terminate
    if (cmd_parms == ""):
      out, err = process.communicate()
    else:
      out, err = process.communicate(cmd_parms)
    errcode = process.returncode

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

  thread.join(timeout)
  if thread.is_alive():
    me = os.getpid()
    kill_proc_tree(me, including_parent=False)
    thread.join()
 1
Author: Tomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-01 20:13:52

Jest pomysł, aby podklasować klasę Popen i rozszerzyć ją za pomocą prostych dekoratorów. Nazwijmy to ExpirablePopen.

from logging import error
from subprocess import Popen
from threading import Event
from threading import Thread


class ExpirablePopen(Popen):

    def __init__(self, *args, **kwargs):
        self.timeout = kwargs.pop('timeout', 0)
        self.timer = None
        self.done = Event()

        Popen.__init__(self, *args, **kwargs)

    def __tkill(self):
        timeout = self.timeout
        if not self.done.wait(timeout):
            error('Terminating process {} by timeout of {} secs.'.format(self.pid, timeout))
            self.kill()

    def expirable(func):
        def wrapper(self, *args, **kwargs):
            # zero timeout means call of parent method
            if self.timeout == 0:
                return func(self, *args, **kwargs)

            # if timer is None, need to start it
            if self.timer is None:
                self.timer = thr = Thread(target=self.__tkill)
                thr.daemon = True
                thr.start()

            result = func(self, *args, **kwargs)
            self.done.set()

            return result
        return wrapper

    wait = expirable(Popen.wait)
    communicate = expirable(Popen.communicate)


if __name__ == '__main__':
    from subprocess import PIPE

    print ExpirablePopen('ssh -T [email protected]', stdout=PIPE, timeout=1).communicate()
 1
Author: Alexander Yakovlev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-19 12:18:44

Miałem problem, że chciałem zakończyć podproces wielowątkowy, jeśli trwało to dłużej niż określony czas. Chciałem ustawić timeout w Popen(), ale nie zadziałało. Potem zdałem sobie sprawę, że {[2] } jest równe call() i wpadłem na pomysł, aby ustawić timeout w metodzie .wait(timeout=xxx), która w końcu zadziałała. Tak więc rozwiązałem to w ten sposób:

import os
import sys
import signal
import subprocess
from multiprocessing import Pool

cores_for_parallelization = 4
timeout_time = 15  # seconds

def main():
    jobs = [...YOUR_JOB_LIST...]
    with Pool(cores_for_parallelization) as p:
        p.map(run_parallel_jobs, jobs)

def run_parallel_jobs(args):
    # Define the arguments including the paths
    initial_terminal_command = 'C:\\Python34\\python.exe'  # Python executable
    function_to_start = 'C:\\temp\\xyz.py'  # The multithreading script
    final_list = [initial_terminal_command, function_to_start]
    final_list.extend(args)

    # Start the subprocess and determine the process PID
    subp = subprocess.Popen(final_list)  # starts the process
    pid = subp.pid

    # Wait until the return code returns from the function by considering the timeout. 
    # If not, terminate the process.
    try:
        returncode = subp.wait(timeout=timeout_time)  # should be zero if accomplished
    except subprocess.TimeoutExpired:
        # Distinguish between Linux and Windows and terminate the process if 
        # the timeout has been expired
        if sys.platform == 'linux2':
            os.kill(pid, signal.SIGTERM)
        elif sys.platform == 'win32':
            subp.terminate()

if __name__ == '__main__':
    main()
 1
Author: sjor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-19 16:21:23

Python 2.7

import time
import subprocess

def run_command(cmd, timeout=0):
    start_time = time.time()
    df = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    while timeout and df.poll() == None:
        if time.time()-start_time >= timeout:
            df.kill()
            return -1, ""
    output = '\n'.join(df.communicate()).strip()
    return df.returncode, output
 1
Author: Joy Wang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-08 00:11:38

Niestety, jestem związany bardzo surowymi zasadami dotyczącymi ujawniania kodu źródłowego przez mojego pracodawcę, więc nie mogę podać rzeczywistego kodu. Ale jak na mój gust najlepszym rozwiązaniem jest utworzenie podklasy nadrzędnej Popen.wait() do ankiety zamiast oczekiwania w nieskończoność i Popen.__init__ do zaakceptowania parametru timeout. Gdy to zrobisz, wszystkie inne metody Popen (wywołujące wait) będą działać zgodnie z oczekiwaniami, w tym communicate.

 0
Author: Patrick Narkinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-01 21:11:09

Https://pypi.python.org/pypi/python-subprocess2 dostarcza rozszerzenia do modułu podprocesu, które pozwalają czekać do określonego czasu, w przeciwnym razie zakończyć.

Więc, aby odczekać do 10 sekund na zakończenie procesu, w przeciwnym razie zabić:

pipe  = subprocess.Popen('...')

timeout =  10

results = pipe.waitOrTerminate(timeout)

Jest to zgodne zarówno z windows, jak i unix. "results" jest słownikiem, zawiera "returnCode", który jest zwrotem aplikacji (lub żadnego, jeśli musiała zostać zabita), a także "actionTaken". które będą "SUBPROCESS2_PROCESS_COMPLETED", jeśli proces zakończył się normalnie, lub maska "SUBPROCESS2_PROCESS_TERMINATED" i SUBPROCESS2_PROCESS_KILLED w zależności od podjętych działań (patrz dokumentacja po pełne szczegóły)

 0
Author: Tim Savannah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 13:52:16

W Pythonie 2.6+ użyj gevent

 from gevent.subprocess import Popen, PIPE, STDOUT

 def call_sys(cmd, timeout):
      p= Popen(cmd, shell=True, stdout=PIPE)
      output, _ = p.communicate(timeout=timeout)
      assert p.returncode == 0, p. returncode
      return output

 call_sys('./t.sh', 2)

 # t.sh example
 sleep 5
 echo done
 exit 1
 0
Author: whi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-01 12:15:03

Przykład przechwyconego wyjścia po timeout testowanego w Pythonie 3.7.8:

try:
    return subprocess.run(command, shell=True, capture_output=True, timeout=20, cwd=cwd, universal_newlines=True)
except subprocess.TimeoutExpired as e:
    print(e.output.decode(encoding="utf-8", errors="ignore"))
    assert False;

Podproces wyjątku.TimeoutExpired ma wyjście i inne elementy:

Cmd-polecenie, które zostało użyte do wywołania procesu potomnego.

Timeout-limit czasu w sekundach.

Output-wyjście procesu potomnego, jeśli zostało przechwycone przez run () lub check_output (). W Przeciwnym Razie, Żaden.

Stdout-Alias dla wyjścia, dla symetrii ze stderr.

Stderr-wyjście stderr dziecka proces, jeśli został przechwycony przez run (). W Przeciwnym Razie, Żaden.

Więcej informacji: https://docs.python.org/3/library/subprocess.html#subprocess.TimeoutExpired

 0
Author: Neaţu Ovidiu Gabriel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-26 18:04:54
import subprocess, optparse, os, sys, re, datetime, threading, time, glob, shutil, xml.dom.minidom, traceback

class OutputManager:
    def __init__(self, filename, mode, console, logonly):
        self.con = console
        self.logtoconsole = True
        self.logtofile = False

        if filename:
            try:
                self.f = open(filename, mode)
                self.logtofile = True
                if logonly == True:
                    self.logtoconsole = False
            except IOError:
                print (sys.exc_value)
                print ("Switching to console only output...\n")
                self.logtofile = False
                self.logtoconsole = True

    def write(self, data):
        if self.logtoconsole == True:
            self.con.write(data)
        if self.logtofile == True:
            self.f.write(data)
        sys.stdout.flush()

def getTimeString():
        return time.strftime("%Y-%m-%d", time.gmtime())

def runCommand(command):
    '''
    Execute a command in new thread and return the
    stdout and stderr content of it.
    '''
    try:
        Output = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]
    except Exception as e:
        print ("runCommand failed :%s" % (command))
        print (str(e))
        sys.stdout.flush()
        return None
    return Output

def GetOs():
    Os = ""
    if sys.platform.startswith('win32'):
        Os = "win"
    elif sys.platform.startswith('linux'):
        Os = "linux"
    elif sys.platform.startswith('darwin'):
        Os = "mac"
    return Os


def check_output(*popenargs, **kwargs):
    try:
        if 'stdout' in kwargs: 
            raise ValueError('stdout argument not allowed, it will be overridden.') 

        # Get start time.
        startTime = datetime.datetime.now()
        timeoutValue=3600

        cmd = popenargs[0]

        if sys.platform.startswith('win32'):
            process = subprocess.Popen( cmd, stdout=subprocess.PIPE, shell=True) 
        elif sys.platform.startswith('linux'):
            process = subprocess.Popen( cmd , stdout=subprocess.PIPE, shell=True ) 
        elif sys.platform.startswith('darwin'):
            process = subprocess.Popen( cmd , stdout=subprocess.PIPE, shell=True ) 

        stdoutdata, stderrdata = process.communicate( timeout = timeoutValue )
        retcode = process.poll()

        ####################################
        # Catch crash error and log it.
        ####################################
        OutputHandle = None
        try:
            if retcode >= 1:
                OutputHandle = OutputManager( 'CrashJob_' + getTimeString() + '.txt', 'a+', sys.stdout, False)
                OutputHandle.write( cmd )
                print (stdoutdata)
                print (stderrdata)
                sys.stdout.flush()
        except Exception as e:
            print (str(e))

    except subprocess.TimeoutExpired:
            ####################################
            # Catch time out error and log it.
            ####################################
            Os = GetOs()
            if Os == 'win':
                killCmd = "taskkill /FI \"IMAGENAME eq {0}\" /T /F"
            elif Os == 'linux':
                killCmd = "pkill {0)"
            elif Os == 'mac':
                # Linux, Mac OS
                killCmd = "killall -KILL {0}"

            runCommand(killCmd.format("java"))
            runCommand(killCmd.format("YouApp"))

            OutputHandle = None
            try:
                OutputHandle = OutputManager( 'KillJob_' + getTimeString() + '.txt', 'a+', sys.stdout, False)
                OutputHandle.write( cmd )
            except Exception as e:
                print (str(e))
    except Exception as e:
            for frame in traceback.extract_tb(sys.exc_info()[2]):
                        fname,lineno,fn,text = frame
                        print "Error in %s on line %d" % (fname, lineno)
 -2
Author: PeterZhao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-18 06:09:01

Próbowałem napisać coś prostszego.

#!/usr/bin/python

from subprocess import Popen, PIPE
import datetime
import time 

popen = Popen(["/bin/sleep", "10"]);
pid = popen.pid
sttime = time.time();
waittime =  3

print "Start time %s"%(sttime)

while True:
    popen.poll();
    time.sleep(1)
    rcode = popen.returncode
    now = time.time();
    if [ rcode is None ]  and  [ now > (sttime + waittime) ] :
        print "Killing it now"
        popen.kill()
 -3
Author: Jabir Ahmed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-05 21:01:52