Timeout su una chiamata di funzione


300

Sto chiamando una funzione in Python che conosco potrebbe bloccarsi e costringermi a riavviare lo script.

Come posso chiamare la funzione o come inserirla in modo tale che se impiega più di 5 secondi lo script la annulla e fa qualcos'altro?

Risposte:


227

È possibile utilizzare il pacchetto di segnali se si esegue UNIX:

In [1]: import signal

# Register an handler for the timeout
In [2]: def handler(signum, frame):
   ...:     print("Forever is over!")
   ...:     raise Exception("end of time")
   ...: 

# This function *may* run for an indetermined time...
In [3]: def loop_forever():
   ...:     import time
   ...:     while 1:
   ...:         print("sec")
   ...:         time.sleep(1)
   ...:         
   ...:         

# Register the signal function handler
In [4]: signal.signal(signal.SIGALRM, handler)
Out[4]: 0

# Define a timeout for your function
In [5]: signal.alarm(10)
Out[5]: 0

In [6]: try:
   ...:     loop_forever()
   ...: except Exception, exc: 
   ...:     print(exc)
   ....: 
sec
sec
sec
sec
sec
sec
sec
sec
Forever is over!
end of time

# Cancel the timer if the function returned before timeout
# (ok, mine won't but yours maybe will :)
In [7]: signal.alarm(0)
Out[7]: 0

10 secondi dopo la chiamata alarm.alarm(10), viene chiamato il gestore. Ciò solleva un'eccezione che puoi intercettare dal normale codice Python.

Questo modulo non funziona bene con i thread (ma poi, chi lo fa?)

Si noti che poiché solleviamo un'eccezione quando si verifica il timeout, potrebbe essere catturato e ignorato all'interno della funzione, ad esempio di una di queste funzioni:

def loop_forever():
    while 1:
        print('sec')
        try:
            time.sleep(10)
        except:
            continue

5
Uso Python 2.5.4. Si è verificato un errore simile: Traceback (ultima chiamata più recente): file "aa.py", riga 85, in func signal.signal (signal.SIGALRM, gestore) AttributeError: l'oggetto 'module' non ha attributi 'SIGALRM'
flypen

11
@flypen perché signal.alarme i relativi SIGALRMnon sono disponibili su piattaforme Windows.
Doppio AA

2
Se ci sono molti processi e ciascuno chiama signal.signal--- funzioneranno tutti correttamente? Ciascuna signal.signalchiamata non annullerà quella "concorrente"?
brownian,

1
Avviso per coloro che desiderano utilizzarlo con un'estensione C: Il gestore del segnale Python non verrà chiamato fino a quando la funzione C non restituirà il controllo all'interprete Python. Per questo caso d'uso, utilizzare di ATOzTOA risposta: stackoverflow.com/a/14924210/1286628
wkschwartz

13
In secondo luogo l'avvertimento sui thread. signal.alarm funziona solo sul thread principale. Ho provato a usarlo nelle viste Django - fallito immediatamente con la verbosità solo sul thread principale.
JL Peyret,

154

Puoi usare multiprocessing.Processper fare esattamente questo.

Codice

import multiprocessing
import time

# bar
def bar():
    for i in range(100):
        print "Tick"
        time.sleep(1)

if __name__ == '__main__':
    # Start bar as a process
    p = multiprocessing.Process(target=bar)
    p.start()

    # Wait for 10 seconds or until process finishes
    p.join(10)

    # If thread is still active
    if p.is_alive():
        print "running... let's kill it..."

        # Terminate
        p.terminate()
        p.join()

36
Come posso ottenere il valore di ritorno del metodo target?
bad_keypoints l'

4
Questo non sembra funzionare se la funzione chiamata viene bloccata su un blocco I / O.
sudo,

4
@bad_keypoints Vedi questa risposta: stackoverflow.com/a/10415215/1384471 Fondamentalmente, passi un elenco lungo il quale inserisci la risposta.
Peter,

1
@sudo quindi rimuovere il join(). che rende il tuo numero x di sottoprocessi simultanei in esecuzione fino a quando non completano il loro lavoro o la quantità definita in join(10). Nel caso in cui tu abbia un I / O di blocco per 10 processi, usando join (10) li hai impostati per attendere tutti al massimo 10 per OGNI processo avviato. Usa flag demone come questo esempio stackoverflow.com/a/27420072/2480481 . Ovviamente puoi passare il flag daemon=Truedirettamente per multiprocessing.Process()funzionare.
m3nda,

2
@ATOzTOA il problema con questa soluzione, almeno per i miei scopi, è che potenzialmente non consente ai pedali dei bambini di pulire dopo se stessi. Dalla documentazione della funzione terminataterminate() ... Note that exit handlers and finally clauses, etc., will not be executed. Note that descendant processes of the process will not be terminated – they will simply become orphaned.
abalcerek,

78

Come posso chiamare la funzione o come inserirla in modo tale che se impiega più di 5 secondi lo script la annulla?

Ho pubblicato una sintesi che risolve questa domanda / problema con un decoratore e un threading.Timer. Eccolo con un guasto.

Importazioni e configurazioni per la compatibilità

È stato testato con Python 2 e 3. Dovrebbe funzionare anche con Unix / Linux e Windows.

Innanzitutto le importazioni. Questi tentano di mantenere coerente il codice indipendentemente dalla versione di Python:

from __future__ import print_function
import sys
import threading
from time import sleep
try:
    import thread
except ImportError:
    import _thread as thread

Usa il codice indipendente dalla versione:

try:
    range, _print = xrange, print
    def print(*args, **kwargs): 
        flush = kwargs.pop('flush', False)
        _print(*args, **kwargs)
        if flush:
            kwargs.get('file', sys.stdout).flush()            
except NameError:
    pass

Ora abbiamo importato le nostre funzionalità dalla libreria standard.

exit_after decoratore

Quindi abbiamo bisogno di una funzione per terminare il main()thread dal figlio:

def quit_function(fn_name):
    # print to stderr, unbuffered in Python 2.
    print('{0} took too long'.format(fn_name), file=sys.stderr)
    sys.stderr.flush() # Python 3 stderr is likely buffered.
    thread.interrupt_main() # raises KeyboardInterrupt

Ed ecco il decoratore stesso:

def exit_after(s):
    '''
    use as decorator to exit process if 
    function takes longer than s seconds
    '''
    def outer(fn):
        def inner(*args, **kwargs):
            timer = threading.Timer(s, quit_function, args=[fn.__name__])
            timer.start()
            try:
                result = fn(*args, **kwargs)
            finally:
                timer.cancel()
            return result
        return inner
    return outer

uso

Ed ecco l'uso che risponde direttamente alla tua domanda sull'uscita dopo 5 secondi !:

@exit_after(5)
def countdown(n):
    print('countdown started', flush=True)
    for i in range(n, -1, -1):
        print(i, end=', ', flush=True)
        sleep(1)
    print('countdown finished')

demo:

>>> countdown(3)
countdown started
3, 2, 1, 0, countdown finished
>>> countdown(10)
countdown started
10, 9, 8, 7, 6, countdown took too long
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in inner
  File "<stdin>", line 6, in countdown
KeyboardInterrupt

La seconda chiamata di funzione non finirà, invece il processo dovrebbe uscire con un traceback!

KeyboardInterrupt non ferma sempre un thread inattivo

Si noti che la sospensione non sarà sempre interrotta da un'interruzione della tastiera, su Python 2 su Windows, ad esempio:

@exit_after(1)
def sleep10():
    sleep(10)
    print('slept 10 seconds')

>>> sleep10()
sleep10 took too long         # Note that it hangs here about 9 more seconds
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in inner
  File "<stdin>", line 3, in sleep10
KeyboardInterrupt

né è probabile che interrompa il codice in esecuzione nelle estensioni a meno che non controlli esplicitamente per PyErr_CheckSignals(), vedere Cython, Python e KeyboardInterrupt ignorati

Eviterei di dormire un thread più di un secondo, in ogni caso - è un eone nel tempo del processore.

Come posso chiamare la funzione o come inserirla in modo tale che se impiega più di 5 secondi lo script la annulla e fa qualcos'altro?

Per catturarlo e fare qualcos'altro, puoi catturare KeyboardInterrupt.

>>> try:
...     countdown(10)
... except KeyboardInterrupt:
...     print('do something else')
... 
countdown started
10, 9, 8, 7, 6, countdown took too long
do something else

Non ho ancora letto tutto il tuo post, ma mi chiedevo solo: cosa succede se flush è 0? Ciò sarebbe interpretato come Falso nell'istruzione if sottostante, giusto?
Koenraad van Duin

2
Perché devo chiamare thread.interrupt_main(), perché non posso sollevare direttamente un'eccezione?
Anirban Nag 'tintinmj'

Qualche idea su come concludere multiprocessing.connection.Client? - Cercando di risolvere: stackoverflow.com/questions/57817955/…
seconda

51

Ho una proposta diversa che è una funzione pura (con la stessa API del suggerimento per il threading) e sembra funzionare bene (sulla base di suggerimenti su questo thread)

def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):
    import signal

    class TimeoutError(Exception):
        pass

    def handler(signum, frame):
        raise TimeoutError()

    # set the timeout handler
    signal.signal(signal.SIGALRM, handler) 
    signal.alarm(timeout_duration)
    try:
        result = func(*args, **kwargs)
    except TimeoutError as exc:
        result = default
    finally:
        signal.alarm(0)

    return result

3
È inoltre necessario ripristinare il gestore del segnale originale. Vedi stackoverflow.com/questions/492519/…
Martin Konecny ​​l'

9
Un'altra nota: il metodo del segnale Unix funziona solo se lo stai applicando nel thread principale. Applicarlo in un sottoprocesso genera un'eccezione e non funzionerà.
Martin Konecny,

12
Questa non è la soluzione migliore perché funziona solo su Linux.
max

17
Max, non vero - funziona su qualsiasi unix compatibile con POSIX. Penso che il tuo commento dovrebbe essere più preciso, non funziona su Windows.
Chris Johnson,

6
Dovresti evitare di impostare kwargs su un dict vuoto. Un gotcha comune di Python è che gli argomenti predefiniti sulle funzioni sono mutabili. In questo modo il dizionario verrà condiviso tra tutte le chiamate a timeout. È molto meglio impostare il valore predefinito su Nonee, sulla prima riga della funzione, aggiungere kwargs = kwargs or {}. Args va bene perché le tuple non sono mutabili.
scottmrogowski,

32

Mi sono imbattuto in questo thread durante la ricerca di una chiamata di timeout sui test unitari. Non ho trovato nulla di semplice nelle risposte o nei pacchetti di terze parti, quindi ho scritto il decoratore di seguito che puoi inserire nel codice:

import multiprocessing.pool
import functools

def timeout(max_timeout):
    """Timeout decorator, parameter in seconds."""
    def timeout_decorator(item):
        """Wrap the original function."""
        @functools.wraps(item)
        def func_wrapper(*args, **kwargs):
            """Closure for function."""
            pool = multiprocessing.pool.ThreadPool(processes=1)
            async_result = pool.apply_async(item, args, kwargs)
            # raises a TimeoutError if execution exceeds max_timeout
            return async_result.get(max_timeout)
        return func_wrapper
    return timeout_decorator

Quindi è semplice come questo timeout di un test o qualsiasi funzione che ti piace:

@timeout(5.0)  # if execution takes longer than 5 seconds, raise a TimeoutError
def test_base_regression(self):
    ...

14
Fare attenzione poiché questo non termina la funzione dopo che è stato raggiunto il timeout!
Sylvain,

Si noti che su Windows, questo genera un processo completamente nuovo - che consumerà il tempo di timeout, forse di molto se le dipendenze impiegano molto tempo a configurarsi.
Aaron Hall

1
Sì, questo richiede alcune modifiche. Lascia i fili che vanno per sempre.
sudo,

2
IDK se questo è il modo migliore, ma puoi provare / catturare Exceptionall'interno di func_wrapper e fare pool.close()dopo la cattura per assicurarti che il filo muoia sempre dopo, qualunque cosa accada . Quindi puoi lanciare TimeoutErroro quello che vuoi dopo. Sembra funzionare per me.
sudo,

2
Questo è utile, ma una volta che l'ho fatto molte volte, ottengo RuntimeError: can't start new thread. Funzionerà comunque se lo ignoro o c'è qualcos'altro che posso fare per aggirare questo? Grazie in anticipo!
Benjie,

20

Il stopitpacchetto, trovato su pypi, sembra gestire bene i timeout.

Mi piace il @stopit.threading_timeoutabledecoratore, che aggiunge un timeoutparametro alla funzione decorata, che fa quello che ti aspetti, interrompe la funzione.

Dai un'occhiata su pypi: https://pypi.python.org/pypi/stopit


1
È molto maneggevole e sicuro per i thread! Grazie e Plus uno! Questa è l'opzione migliore che ho trovato finora e persino migliore della risposta accettata !!
Yahya,

Rivendicazioni della libreria, alcune funzionalità non funzionano in Windows.
Stefan Simik

16

Ci sono molti suggerimenti, ma nessuno usa concurrent.futures, che penso sia il modo più leggibile per gestirlo.

from concurrent.futures import ProcessPoolExecutor

# Warning: this does not terminate function if timeout
def timeout_five(fnc, *args, **kwargs):
    with ProcessPoolExecutor() as p:
        f = p.submit(fnc, *args, **kwargs)
        return f.result(timeout=5)

Super semplice da leggere e mantenere.

Realizziamo un pool, inviamo un singolo processo e quindi aspettiamo fino a 5 secondi prima di sollevare un TimeoutError che potresti catturare e gestire come necessario.

Originario di Python 3.2+ e backportato su 2.7 (installazione futures pip).

Passare da thread a processi è semplice come sostituirlo ProcessPoolExecutorcon ThreadPoolExecutor.

Se si desidera terminare il processo al timeout, suggerirei di esaminare Pebble .


2
Che cosa significa "Avviso: questo non termina la funzione se timeout" significa?
Scott Stafford,

5
@ScottStafford I processi / thread non terminano solo perché è stato generato un TimeoutError. Quindi il processo o il thread proveranno comunque a completarsi e non ti restituiranno automaticamente il controllo al timeout.
Brian,

Ciò mi consentirebbe di salvare eventuali risultati intermedi in quel momento? ad es. se ho una funzione ricorsiva che ho impostato il timeout su 5 e in quel momento ho risultati parziali, come posso scrivere la funzione per restituire i risultati parziali al timeout?
SumNeuron

Sto usando esattamente questo, tuttavia ho 1000 attività, ognuna è consentita 5 secondi prima del timeout. Il mio problema è che i core si intasano su attività che non finiscono mai perché il timeout viene applicato solo sul totale delle attività non su singole attività. concurrent.futures non fornisce una soluzione a questo afaik.
Bastiaan,

12

Time -decorator di progetti PyPi fantastico, facile da usare e affidabile ( https://pypi.org/project/timeout-decorator/ )

installazione :

pip install timeout-decorator

Utilizzo :

import time
import timeout_decorator

@timeout_decorator.timeout(5)
def mytest():
    print "Start"
    for i in range(1,10):
        time.sleep(1)
        print "%d seconds have passed" % i

if __name__ == '__main__':
    mytest()

2
Apprezzo la soluzione chiara. Qualcuno potrebbe spiegare come funziona questa libreria, specialmente quando si tratta di multithreading. Personalmente temo di usare un macanismo sconosciuto per gestire discussioni o segnali.
wsysuper,

@wsysuper the lib ha 2 modalità di funzionamento: aprire un nuovo thread o un nuovo sottoprocesso (che si suppone sia thread-safe)
Gil

questo ha funzionato molto bene per me!
Florian Heigl

6

Sono l'autore di wrapt_timeout_decorator

La maggior parte delle soluzioni presentate qui funziona in modo meraviglioso sotto Linux al primo sguardo - perché abbiamo fork () e signal () - ma su Windows le cose sembrano un po 'diverse. E quando si tratta di sottotread su Linux, non puoi più usare i segnali.

Per generare un processo in Windows, deve essere selezionabile e molte funzioni decorate o metodi Class non lo sono.

Quindi è necessario utilizzare un miglior pickler come aneto e multiprocesso (non decapaggio e multiprocesso) - ecco perché non è possibile utilizzare ProcessPoolExecutor (o solo con funzionalità limitate).

Per il timeout stesso - Devi definire cosa significa timeout - perché su Windows ci vorrà un tempo considerevole (e non determinabile) per generare il processo. Questo può essere complicato con brevi timeout. Supponiamo che la generazione del processo richieda circa 0,5 secondi (facilmente !!!). Se si concede un timeout di 0,2 secondi, cosa dovrebbe accadere? La funzione dovrebbe scadere dopo 0,5 + 0,2 secondi (quindi lasciare funzionare il metodo per 0,2 secondi)? O il processo chiamato dovrebbe andare in timeout dopo 0,2 secondi (in quel caso, la funzione decorata SEMPRE timeout, perché in quel momento non viene nemmeno generata)?

Anche i decoratori nidificati possono essere cattivi e non puoi usare i segnali in un sottotread. Se vuoi creare un decoratore veramente universale e multipiattaforma, tutto ciò deve essere preso in considerazione (e testato).

Altri problemi stanno restituendo al chiamante le eccezioni, nonché i problemi di registrazione (se utilizzato nella funzione decorata - la registrazione su file in un altro processo NON è supportata)

Ho provato a coprire tutti i casi limite, potresti esaminare il pacchetto wrapt_timeout_decorator o almeno testare le tue soluzioni ispirate agli unittest utilizzati lì.

@Alexis Eggermont - purtroppo non ho abbastanza punti per commentare - forse qualcun altro può avvisarti - Penso di aver risolto il tuo problema di importazione.


3

timeout-decoratornon funziona sul sistema Windows poiché Windows non supportava signalbene.

Se usi timeout-decorator nel sistema di Windows otterrai quanto segue

AttributeError: module 'signal' has no attribute 'SIGALRM'

Alcuni hanno suggerito di usare use_signals=False ma non ha funzionato per me.

Author @bitranox ha creato il seguente pacchetto:

pip install https://github.com/bitranox/wrapt-timeout-decorator/archive/master.zip

Esempio di codice:

import time
from wrapt_timeout_decorator import *

@timeout(5)
def mytest(message):
    print(message)
    for i in range(1,10):
        time.sleep(1)
        print('{} seconds have passed'.format(i))

def main():
    mytest('starting')


if __name__ == '__main__':
    main()

Fornisce la seguente eccezione:

TimeoutError: Function mytest timed out after 5 seconds

Sembra una soluzione molto bella. Stranamente, la linea from wrapt_timeout_decorator import * sembra uccidere alcune delle mie altre importazioni. Ad esempio ModuleNotFoundError: No module named 'google.appengine', ricevo , ma non visualizzo questo errore se non importare wrapt_timeout_decorator
Alexis Eggermont,

@AlexisEggermont Stavo per usarlo con Appengine ... quindi sono molto curioso se questo errore persiste?
PascalVKooten,

2

Possiamo usare i segnali per lo stesso. Penso che l'esempio che segue sarà utile per te. È molto semplice rispetto ai thread.

import signal

def timeout(signum, frame):
    raise myException

#this is an infinite loop, never ending under normal circumstances
def main():
    print 'Starting Main ',
    while 1:
        print 'in main ',

#SIGALRM is only usable on a unix platform
signal.signal(signal.SIGALRM, timeout)

#change 5 to however many seconds you need
signal.alarm(5)

try:
    main()
except myException:
    print "whoops"

1
Sarebbe meglio scegliere un'eccezione specifica e catturarla solo. La nuda try: ... except: ...è sempre una cattiva idea.
hivert,

Sono d'accordo con te Hivert.
AR,

mentre capisco il motivo, come amministratore di sistema / integratore non sono d'accordo - il codice Python è noto per trascurare la gestione degli errori e gestire l'unica cosa che ci si aspetta non è abbastanza buona per un software di qualità. puoi gestire le 5 cose che pianifichi E una strategia generica per altre cose. "Traceback, None" non è una strategia, è un insulto.
Florian Heigl

2
#!/usr/bin/python2
import sys, subprocess, threading
proc = subprocess.Popen(sys.argv[2:])
timer = threading.Timer(float(sys.argv[1]), proc.terminate)
timer.start()
proc.wait()
timer.cancel()
exit(proc.returncode)

7
Mentre questo codice può rispondere alla domanda, fornendo un contesto aggiuntivo riguardo a come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta
Dan Cornilescu,

1

Avevo bisogno di interruzioni temporizzate annidabili (che SIGALARM non può fare) che non verranno bloccate da time.sleep (che l'approccio basato su thread non può fare). Ho finito per copiare e modificare leggermente il codice da qui: http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/

Il codice stesso:

#!/usr/bin/python

# lightly modified version of http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/


"""alarm.py: Permits multiple SIGALRM events to be queued.

Uses a `heapq` to store the objects to be called when an alarm signal is
raised, so that the next alarm is always at the top of the heap.
"""

import heapq
import signal
from time import time

__version__ = '$Revision: 2539 $'.split()[1]

alarmlist = []

__new_alarm = lambda t, f, a, k: (t + time(), f, a, k)
__next_alarm = lambda: int(round(alarmlist[0][0] - time())) if alarmlist else None
__set_alarm = lambda: signal.alarm(max(__next_alarm(), 1))


class TimeoutError(Exception):
    def __init__(self, message, id_=None):
        self.message = message
        self.id_ = id_


class Timeout:
    ''' id_ allows for nested timeouts. '''
    def __init__(self, id_=None, seconds=1, error_message='Timeout'):
        self.seconds = seconds
        self.error_message = error_message
        self.id_ = id_
    def handle_timeout(self):
        raise TimeoutError(self.error_message, self.id_)
    def __enter__(self):
        self.this_alarm = alarm(self.seconds, self.handle_timeout)
    def __exit__(self, type, value, traceback):
        try:
            cancel(self.this_alarm) 
        except ValueError:
            pass


def __clear_alarm():
    """Clear an existing alarm.

    If the alarm signal was set to a callable other than our own, queue the
    previous alarm settings.
    """
    oldsec = signal.alarm(0)
    oldfunc = signal.signal(signal.SIGALRM, __alarm_handler)
    if oldsec > 0 and oldfunc != __alarm_handler:
        heapq.heappush(alarmlist, (__new_alarm(oldsec, oldfunc, [], {})))


def __alarm_handler(*zargs):
    """Handle an alarm by calling any due heap entries and resetting the alarm.

    Note that multiple heap entries might get called, especially if calling an
    entry takes a lot of time.
    """
    try:
        nextt = __next_alarm()
        while nextt is not None and nextt <= 0:
            (tm, func, args, keys) = heapq.heappop(alarmlist)
            func(*args, **keys)
            nextt = __next_alarm()
    finally:
        if alarmlist: __set_alarm()


def alarm(sec, func, *args, **keys):
    """Set an alarm.

    When the alarm is raised in `sec` seconds, the handler will call `func`,
    passing `args` and `keys`. Return the heap entry (which is just a big
    tuple), so that it can be cancelled by calling `cancel()`.
    """
    __clear_alarm()
    try:
        newalarm = __new_alarm(sec, func, args, keys)
        heapq.heappush(alarmlist, newalarm)
        return newalarm
    finally:
        __set_alarm()


def cancel(alarm):
    """Cancel an alarm by passing the heap entry returned by `alarm()`.

    It is an error to try to cancel an alarm which has already occurred.
    """
    __clear_alarm()
    try:
        alarmlist.remove(alarm)
        heapq.heapify(alarmlist)
    finally:
        if alarmlist: __set_alarm()

e un esempio di utilizzo:

import alarm
from time import sleep

try:
    with alarm.Timeout(id_='a', seconds=5):
        try:
            with alarm.Timeout(id_='b', seconds=2):
                sleep(3)
        except alarm.TimeoutError as e:
            print 'raised', e.id_
        sleep(30)
except alarm.TimeoutError as e:
    print 'raised', e.id_
else:
    print 'nope.'

Questo utilizza anche il segnale quindi non funzionerà se chiamato da un thread.
garg10may

0

Ecco un leggero miglioramento rispetto alla soluzione basata su thread fornita.

Il codice seguente supporta le eccezioni :

def runFunctionCatchExceptions(func, *args, **kwargs):
    try:
        result = func(*args, **kwargs)
    except Exception, message:
        return ["exception", message]

    return ["RESULT", result]


def runFunctionWithTimeout(func, args=(), kwargs={}, timeout_duration=10, default=None):
    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.result = default
        def run(self):
            self.result = runFunctionCatchExceptions(func, *args, **kwargs)
    it = InterruptableThread()
    it.start()
    it.join(timeout_duration)
    if it.isAlive():
        return default

    if it.result[0] == "exception":
        raise it.result[1]

    return it.result[1]

Richiamandolo con un timeout di 5 secondi:

result = timeout(remote_calculate, (myarg,), timeout_duration=5)

1
Ciò solleverà una nuova eccezione che nasconde il traceback originale. Vedi la mia versione di seguito ...
Meitham

1
Anche questo non è sicuro, come se all'interno di runFunctionCatchExceptions()alcune funzioni Python venisse chiamato GIL. Ad esempio, la seguente non avrebbe mai, o per molto tempo, di ritorno, se chiamato all'interno della funzione: eval(2**9999999999**9999999999). Vedere stackoverflow.com/questions/22138190/...
Mikko Ohtamaa
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.