Час очікування на виклик функції


300

Я викликаю функцію в Python, яка, як я знаю, може зупинити і змусити мене перезапустити сценарій.

Як мені зателефонувати у функцію або в що я її завертую, якщо сценарій займе більше 5 секунд, сценарій скасовує його та робить щось інше?

Відповіді:


227

Ви можете використовувати пакет сигналів, якщо ви працюєте в 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 секунд після дзвінка викликається alarm.alarm(10)обробник. Це створює виняток, який ви можете перехопити із звичайного коду Python.

Цей модуль не грає добре з потоками (але тоді, хто це робить?)

Зауважте, що оскільки ми створюємо виняток, коли виникає час очікування, він може виявитись і потрапив до функції, наприклад однієї такої функції:

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

5
Я використовую Python 2.5.4. Існує така помилка: Traceback (останній дзвінок останній): Файл "aa.py", рядок 85, у func signal.signal (signal.SIGALRM, обробник) AttributeError: "Модуль" об'єкта не має атрибута "SIGALRM"
flypen

11
@flypen це тому, що signal.alarmі пов'язані з ними SIGALRMнедоступні на платформах Windows.
Подвійний АА

2
Якщо процесів багато, і кожен виклик signal.signal--- чи всі вони працюватимуть належним чином? Чи не буде кожен signal.signalвиклик скасовувати "одночасний"?
броунів

1
Попередження для тих, хто бажає використовувати це з розширенням C: Обробник сигналу Python не буде викликаний, поки функція C не поверне контроль до інтерпретатора Python. Для цього випадку використання, використовуйте ATOzTOA у відповідь: stackoverflow.com/a/14924210/1286628
wkschwartz

13
Я друге попередження про нитки. signal.alarm працює лише на головній потоці. Я намагався використовувати це в поглядах Джанго - негайний збій з багатослівністю лише про головну нитку.
Дж. Л. Пейрет

154

Ви можете використовувати multiprocessing.Processсаме це.

Код

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
Як я можу отримати повернене значення цільового методу?
bad_keypoints

4
Здається, це не працює, якщо викликана функція застрягає в блоці вводу / виводу.
судо

4
@bad_keypoints Дивіться цю відповідь: stackoverflow.com/a/10415215/1384471 В основному ви передаєте список, під яким ви вкладаєте відповідь.
Петро

1
@sudo потім видаліть join(). що робить вашу кількість x одночасних підпроцесів запущеними, поки вони не закінчать свою роботу, або кількість, визначена в join(10). Якщо у вас є блокуючий введення / виведення для 10 процесів, використовуючи приєднання (10), ви встановили їх чекати всіх максимум 10 для запущеного процесу EACH. Використовуйте прапор демона, як у цьому прикладі stackoverflow.com/a/27420072/2480481 . Звичайно, ви можете передати прапор daemon=Trueбезпосередньо у multiprocessing.Process()функцію.
м3нда

2
@ATOzTOA Проблема з цим рішенням, принаймні для моїх цілей, полягає в тому, що він потенційно не дозволяє дітям ступати після очищення. З документації про припинення функціїterminate() ... 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

Як мені зателефонувати у функцію або в що я її завертую, якщо сценарій займе більше 5 секунд, сценарій її скасує?

Я розмістив суть, яка вирішує це питання / проблему з декоратором та а threading.Timer. Ось це з поломкою.

Імпорт та налаштування для сумісності

Він був протестований на Python 2 та 3. Він також повинен працювати під Unix / Linux та Windows.

По-перше, імпорт. Ці спроби зберегти код узгодженим незалежно від версії Python:

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

Використовуйте незалежний від версії код:

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

Тепер ми імпортували нашу функціональність зі стандартної бібліотеки.

exit_after декоратор

Далі нам потрібна функція для завершення роботи main()з дочірнього потоку:

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

А ось і сам декоратор:

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

Використання

А ось використання, яке безпосередньо відповідає на ваше запитання про вихід через 5 секунд !:

@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')

Демонстрація:

>>> 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

Виклик другої функції не закінчиться, натомість процес повинен завершитись із зворотним простеженням!

KeyboardInterrupt не завжди зупиняє сплячу нитку

Зауважте, що сон не завжди буде перерваний перериванням клавіатури на Python 2 в Windows, наприклад:

@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

також не є можливим переривання коду, що працює в розширеннях, якщо він прямо не перевіряє PyErr_CheckSignals(), див. ігноровані Cython, Python та KeyboardInterrupt

Я б уникнув спати нитку більше секунди, в будь-якому випадку - це еон в процесорний час.

Як мені зателефонувати у функцію або в що я її завертую, якщо сценарій займе більше 5 секунд, сценарій скасовує його та робить щось інше?

Щоб зловити його і зробити щось інше, ви можете зловити KeyboardInterrupt.

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

Я ще не читав весь ваш пост, але просто задумався: а що, якщо флеш дорівнює 0? Це буде трактуватися як помилкове у висловленні if, що знаходиться під ним, правда?
Koenraad van Duin

2
Чому я повинен дзвонити thread.interrupt_main(), чому я не можу безпосередньо підняти виняток?
Anirban Nag 'tintinmj'

Будь-які думки щодо обгортання multiprocessing.connection.Clientз цим? - Намагаючись вирішити: stackoverflow.com/questions/57817955 / ...
WWII

51

У мене є інша пропозиція, яка є чистою функцією (з тим самим API, що і пропозиція про нарізку) і, здається, працює добре (на основі пропозицій з цієї теми)

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
Ви також повинні відновити початковий обробник сигналу. Дивіться stackoverflow.com/questions/492519/…
Мартін Конечний

9
Ще одне зауваження: метод сигналу Unix працює лише в тому випадку, якщо ви застосовуєте його в основному потоці. Застосування його в підрядці викидає виняток і не спрацює.
Мартін Конечний

12
Це не найкраще рішення, оскільки воно працює лише на Linux.
макс

17
Макс, не відповідає дійсності - працює на будь-якому POSIX-сумісному Unix. Я думаю, що ваш коментар повинен бути більш точним, не працює в Windows.
Кріс Джонсон

6
Вам слід уникати встановлення кваргів на порожній вислів. Поширений метод Python - це те, що аргументи за замовчуванням для функцій можуть змінюватися. Тож цей словник буде спільним для всіх дзвінків на timeout. Набагато краще встановити за замовчуванням значення Noneта, у першому рядку функції, додати kwargs = kwargs or {}. Арги добре, оскільки кортежі не змінюються.
scottmrogowski

32

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

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

Тоді настільки ж просто, як провести тайм-аут тесту чи будь-якої функції, яка вам подобається:

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

14
Будьте обережні, оскільки ця функція не припиняється після досягнення тайм-ауту!
Сільвейн

Зауважте, що в Windows це породжує абсолютно новий процес - який буде вживатися у час очікування, можливо, багато, якщо залежність потребує тривалого часу.
Аарон Холл

1
Так, для цього потрібні певні налаштування. Це залишає нитки йти назавжди.
судо

2
IDK, якщо це найкращий спосіб, але ви можете спробувати / зловити Exceptionвсередині func_wrapper і зробити pool.close()після цього, щоб забезпечити, щоб нитка завжди відмирала після цього, незважаючи ні на що. Тоді ви можете кинути TimeoutErrorабо що завгодно після цього. Здається, працює для мене.
судо

2
Це корисно, але коли я це зробив багато разів, я отримую RuntimeError: can't start new thread. Чи все-таки спрацює, якщо я проігнорую це чи щось інше я можу зробити, щоб обійти це? Спасибі заздалегідь!
Бенджі

20

stopitПакет, знайдений на PyPI, здається обробляти таймаут добре.

Мені подобається @stopit.threading_timeoutableдекоратор, який додає timeoutпараметр до декорованої функції, який робить те, що ви очікуєте, він зупиняє функцію.

Перевірте це на pypi: https://pypi.python.org/pypi/stopit


1
Це дуже зручно і безпечно для ниток! Дякую і плюс один! Це найкращий варіант, який я знайшов поки що і навіть краще, ніж прийнята відповідь !!
Ях’я

Бібліотечні претензії, деякі функції не працюють у Windows.
Стефан Симік

16

Є багато пропозицій, але жодна з використанням concurrent.futures, що, на мою думку, є найбільш розбірливим способом впоратися з цим.

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)

Супер просте для читання та обслуговування.

Ми робимо пул, подаємо єдиний процес, а потім чекаємо до 5 секунд, перш ніж підняти TimeoutError, який ви могли б зловити та обробити, як би вам це було потрібно.

Native для python 3.2+ та підтримується до 2.7 (ф'ючерси на встановлення піп).

Переключення між потоками та процесами настільки ж просто, як і заміна ProcessPoolExecutorна ThreadPoolExecutor.

Якщо ви хочете припинити процес вчасно, я б запропонував заглянути в Pebble .


2
Що означає "Попередження: це не припиняє функцію, якщо тайм-аут" означає?
Скотт Стаффорд

5
@ScottStafford Процеси / теми не закінчуються лише тому, що піднято TimeoutError. Таким чином процес або потік все ще намагатимуться запустити до завершення і не автоматично дадуть вам зворотний контроль під час очікування.
Брайан

Чи дозволить мені це зберегти будь-які результати, які є проміжними на той час? наприклад, якщо у мене є рекурсивна функція, для якої я встановив час очікування на 5, і в цей час у мене є часткові результати, як мені написати функцію, щоб повернути часткові результати під час очікування?
SumNeuron

Я використовую саме це, однак у мене є 1000 завдань, кожне з яких дозволено за 5 секунд до часу очікування. Моя проблема полягає в тому, що сердечники забиваються на завдання, які ніколи не закінчуються, оскільки час очікування застосовується лише до загальної кількості завдань, а не до окремих завдань. concurrent.futures не забезпечує рішення цього afaik.
Бастіан

12

Чудовий, простий у використанні та надійний тайм-аут-декоратор проекту PyPi ( https://pypi.org/project/timeout-decorator/ )

установка :

pip install timeout-decorator

Використання :

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
Я ціную чітке рішення. Але чи міг би хто-небудь пояснити, як працює ця бібліотека, особливо коли мова йде про багатопотоковість. Особисто я боюся використовувати невідомий маханізм для обробки потоків або сигналів.
wsysuper

@wsysuper у lib є два режими роботи: відкрити нову нитку або новий підпроцес (який, мабуть, є безпечним для потоку)
Gil

це дуже добре працювало для мене!
Флоріан Хейгл

6

Я автор wrapt_timeout_decorator

Більшість представлених тут рішень з першим поглядом працюють підвісно під Linux - оскільки у нас є fork () та сигнали (), але у Windows все виглядає дещо інакше. А якщо мова йде про підрядки в Linux, ви більше не можете використовувати Signals.

Для нерестування процесу під Windows він повинен бути доступним - і багато прикрашених функцій або методів Class - ні.

Тож вам потрібно використовувати кращий підбирач, як кріп та багатопроцесорний процес (а не маринований і багатопроцесовий) - ось чому ви не можете використовувати ProcessPoolExecutor (або лише з обмеженою функціональністю).

Що стосується самого тайм-ауту - вам потрібно визначити, що означає час очікування - адже для Windows знадобиться чималий (і не визначений) час для нерестування процесу. Це може бути складним у коротких тайм-аутах. Припустимо, процес нересту займає близько 0,5 секунд (легко !!!). Якщо ви даєте тайм-аут 0,2 секунди, що має статися? Чи повинен час вимкнення функції через 0,5 + 0,2 секунди (нехай метод працює 0,2 секунди)? Або повинен затримати час виклику процесу через 0,2 секунди (у такому випадку декорована функція ВИНАГО буде таймаутом, оскільки в цей час вона навіть не породжується)?

Також вкладені декоратори можуть бути неприємними, і Ви не можете використовувати Сигнали в підрядці. Якщо ви хочете створити по-справжньому універсальний, кросплатформенний декоратор, все це потрібно врахувати (і протестувати).

Інші проблеми - це виняток виклику назад абоненту, а також проблеми ведення журналу (якщо використовується в оформленій функції - журнал до файлів в іншому процесі НЕ підтримується)

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

@ Алексис Еггермонт - на жаль, у мене немає достатньої кількості балів для коментарів - можливо, хтось інший може повідомити вас - я думаю, що я вирішив вашу проблему з імпортом.


3

timeout-decoratorне працюють в системі Windows, тому що Windows не підтримує signalдобре.

Якщо ви використовуєте timeout-decorator в системі Windows, ви отримаєте наступне

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

Деякі пропонували використовувати, use_signals=Falseале не спрацювали.

Автор @bitranox створив такий пакет:

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

Зразок коду:

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()

Надає наступне виняток:

TimeoutError: Function mytest timed out after 5 seconds

Це звучить як дуже приємне рішення. Як не дивно, ця лінія, from wrapt_timeout_decorator import * схоже, вбиває частину мого іншого імпорту. Наприклад, я отримую ModuleNotFoundError: No module named 'google.appengine', але я не отримую цієї помилки, якщо не імпортую wrapt_timeout_decorator
Alexis Eggermont

@AlexisEggermont Я збирався використати це з аппендином ... тож мені дуже цікаво, якщо ця помилка зберігалася?
PascalVKooten

2

Ми можемо використовувати сигнали для того ж. Я думаю, що наведений нижче приклад буде корисним для вас. Це дуже просто в порівнянні з нитками.

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
Краще було б вибрати конкретний виняток і зловити його лише. Голі try: ... except: ...завжди погана ідея.
hivert

Я погоджуюсь з вами hivert.
AR

Хоча я розумію причину, я не погоджуюсь як системний адміністратор / інтегратор - python-код відомий тим, що нехтувати поводженнями з помилками та керуванням однією з речей, яку ви очікуєте, недостатньо хороший для якісного програмного забезпечення. ви можете обробити 5 речей, які ви плануєте І загальну стратегію для інших речей. "Traceback, None" - це не стратегія, це образа.
Флоріан Хейгл

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
Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, як та / або чому він вирішує проблему, покращиться довгострокове значення відповіді
Dan Cornilescu

1

У мене була потреба у нестабільних тимчасових перериваннях (які SIGALARM не може зробити), які не заблокуються time.sleep (що не може зробити потоковий підхід). Я закінчив копіювати та злегка змінювати код звідси: http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/

Сам код:

#!/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()

і приклад використання:

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.'

Це також використовує сигнал, отже, він не працюватиме, якщо викликати з потоку.
garg10май

0

Ось невелике вдосконалення даного рішення на основі потоку.

Код нижче підтримує винятки :

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]

Викликаєте його з 5-секундним тайм-аутом:

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

1
Це створить новий виняток, приховуючи оригінальний трекбек. Дивіться мою версію нижче ...
Meitham

1
Це також небезпечно, ніби в межах runFunctionCatchExceptions()певних функцій Python отримання GIL викликається. Наприклад , наступне ніколи, або дуже довго, якщо повернення викликається в функції: eval(2**9999999999**9999999999). Див stackoverflow.com/questions/22138190 / ...
Мікко Ohtamaa
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.