Как добавить тайм-аут к функции в Python

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

#! /usr/bin/env python
"""Provide way to add timeout specifications to arbitrary functions.

There are many ways to add a timeout to a function, but no solution
is both cross-platform and capable of terminating the procedure. This
module use the multiprocessing module to solve both of those problems."""

################################################################################

__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '11 February 2010'
__version__ = '$Revision: 3 $'

################################################################################

import inspect
import sys
import time
import multiprocessing

################################################################################

def add_timeout(function, limit=60):
    """Add a timeout parameter to a function and return it.

    It is illegal to pass anything other than a function as the first
    parameter. If the limit is not given, it gets a default value equal
    to one minute. The function is wrapped and returned to the caller."""
    assert inspect.isfunction(function)
    if limit <= 0:
        raise ValueError()
    return _Timeout(function, limit)

class NotReadyError(Exception): pass

################################################################################

def _target(queue, function, *args, **kwargs):
    """Run a function with arguments and return output via a queue.

    This is a helper function for the Process created in _Timeout. It runs
    the function with positional arguments and keyword arguments and then
    returns the function's output by way of a queue. If an exception gets
    raised, it is returned to _Timeout to be raised by the value property."""
    try:
        queue.put((True, function(*args, **kwargs)))
    except:
        queue.put((False, sys.exc_info()[1]))

class _Timeout:

    """Wrap a function and add a timeout (limit) attribute to it.

    Instances of this class are automatically generated by the add_timeout
    function defined above. Wrapping a function allows asynchronous calls
    to be made and termination of execution after a timeout has passed."""

    def __init__(self, function, limit):
        """Initialize instance in preparation for being called."""
        self.__limit = limit
        self.__function = function
        self.__timeout = time.clock()
        self.__process = multiprocessing.Process()
        self.__queue = multiprocessing.Queue()

    def __call__(self, *args, **kwargs):
        """Execute the embedded function object asynchronously.

        The function given to the constructor is transparently called and
        requires that "ready" be intermittently polled. If and when it is
        True, the "value" property may then be checked for returned data."""
        self.cancel()
        self.__queue = multiprocessing.Queue(1)
        args = (self.__queue, self.__function) + args
        self.__process = multiprocessing.Process(target=_target,
                                                 args=args,
                                                 kwargs=kwargs)
        self.__process.daemon = True
        self.__process.start()
        self.__timeout = self.__limit + time.clock()

    def cancel(self):
        """Terminate any possible execution of the embedded function."""
        if self.__process.is_alive():
            self.__process.terminate()

    @property
    def ready(self):
        """Read-only property indicating status of "value" property."""
        if self.__queue.full():
            return True
        elif not self.__queue.empty():
            return True
        elif self.__timeout < time.clock():
            self.cancel()
        else:
            return False

    @property
    def value(self):
        """Read-only property containing data returned from function."""
        if self.ready is True:
            flag, load = self.__queue.get()
            if flag:
                return load
            raise load
        raise NotReadyError()

    def __get_limit(self):
        return self.__limit

    def __set_limit(self, value):
        if value <= 0:
            raise ValueError()
        self.__limit = value

    limit = property(__get_limit, __set_limit,
                     doc="Property for controlling the value of the timeout.")

Править: Этот код был написан для Python 3.x и не был разработан для методов класса как художественное оформление. multiprocessing модуль не был разработан для изменения экземпляров класса через границы процесса.

26
задан 23 revs, 2 users 100% 10 July 2010 в 19:10
поделиться

2 ответа

Ajax не является междоменным. Ваша лучшая ставка состоит в том, чтобы создать php-страницу на локальном домене, который делает проверку, и перейти к -that - с вашим ajax звонка.

-121--4244655-

дело в том, что я могу получить параметры от провайдера, но не в состоянии извлечь их из ответа... я использовал OpenID::AX::FetchResponse.from_success_response (open_id_response) как объект для удержания ответа... какой метод я использую для извлечения электронной почты, прозвища, страны и т.д....

-121--313960-

Основная проблема кода заключается в чрезмерном использовании функции предотвращения конфликтов пространства имен с двойным подчеркиванием в классе, который вообще не предназначен для подкласса.

В общем, self. __ foo - это кодовый запах, который должен сопровождаться комментарием в соответствии с # Это миксин, и мы не хотим, чтобы произвольные подклассы имели конфликт пространства имен .

Далее клиентский API этого метода будет выглядеть следующим образом:

def mymethod(): pass

mymethod = add_timeout(mymethod, 15)

# start the processing    
timeout_obj = mymethod()
try:
    # access the property, which is really a function call
    ret = timeout_obj.value
except TimeoutError:
    # handle a timeout here
    ret = None

Это совсем не очень пифонично, и лучшим клиентским API будет:

@timeout(15)
def mymethod(): pass

try:
    my_method()
except TimeoutError:
    pass

Вы используете @ property в своем классе для того, что является изменяющим состояние средством доступа, это не очень хорошая идея. Например, что произойдет при двойном доступе к .value? Похоже, это произойдет из-за того, что queue.get () вернет мусор, так как очередь уже пуста.

Полное удаление свойства @. Не используйте его в этом контексте, он не подходит для вашего случая использования. Блокируйте вызов при вызове и возвращайте значение или поднимайте само исключение. Если значение действительно должно быть доступно позже, сделайте его методом, таким как .get () или .value ().

Этот код для _target следует немного переписать:

def _target(queue, function, *args, **kwargs):
    try:
        queue.put((True, function(*args, **kwargs)))
    except:
        queue.put((False, exc_info())) # get *all* the exec info, don't do exc_info[1]

# then later:
    raise exc_info[0], exc_info[1], exc_info[2]

Это путь трассировка стека будет сохранена правильно и видна программисту.

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

12
ответ дан 28 November 2019 в 17:22
поделиться

Вот как получить синтаксис декоратора, упомянутый Джерубом

def timeout(limit=None):
    if limit is None:
        limit = DEFAULT_TIMEOUT
    if limit <= 0:
        raise TimeoutError() # why not ValueError here?
    def wrap(function):
        return _Timeout(function,limit)
    return wrap

@timeout(15)
def mymethod(): pass
5
ответ дан 28 November 2019 в 17:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: