Много попыток были предприняты в прошлом для добавления функциональности тайм-аута в 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
модуль не был разработан для изменения экземпляров класса через границы процесса.
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]
Это путь трассировка стека будет сохранена правильно и видна программисту.
Я думаю, что вы сделали разумную первую трещину при написании полезной библиотеки, мне нравится использование модуля обработки для достижения целей.
Вот как получить синтаксис декоратора, упомянутый Джерубом
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