Merge
This commit is contained in:
@@ -99,4 +99,91 @@ Though Eventlet has many modules, much of the most-used stuff is accessible simp
|
|||||||
|
|
||||||
Queues are a fundamental construct for communicating data between execution units. Eventlet's Queue class is used to communicate between greenthreads, and provides a bunch of useful features for doing that. See :class:`queue.Queue` for more details.
|
Queues are a fundamental construct for communicating data between execution units. Eventlet's Queue class is used to communicate between greenthreads, and provides a bunch of useful features for doing that. See :class:`queue.Queue` for more details.
|
||||||
|
|
||||||
|
.. class:: eventlet.Timeout
|
||||||
|
Raises *exception* in the current greenthread after *timeout* seconds::
|
||||||
|
|
||||||
|
timeout = Timeout(seconds, exception)
|
||||||
|
try:
|
||||||
|
... # execution here is limited by timeout
|
||||||
|
finally:
|
||||||
|
timeout.cancel()
|
||||||
|
|
||||||
|
When *exception* is omitted or ``None``, the :class:`Timeout` instance
|
||||||
|
itself is raised:
|
||||||
|
|
||||||
|
>>> Timeout(0.1)
|
||||||
|
>>> eventlet.sleep(0.2)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
Timeout: 0.1 seconds
|
||||||
|
|
||||||
|
In Python 2.5 and newer, you can use the ``with`` statement for additional
|
||||||
|
convenience::
|
||||||
|
|
||||||
|
with Timeout(seconds, exception) as timeout:
|
||||||
|
pass # ... code block ...
|
||||||
|
|
||||||
|
This is equivalent to the try/finally block in the first example.
|
||||||
|
|
||||||
|
There is an additional feature when using the ``with`` statement: if
|
||||||
|
*exception* is ``False``, the timeout is still raised, but the with
|
||||||
|
statement suppresses it, so the code outside the with-block won't see it::
|
||||||
|
|
||||||
|
data = None
|
||||||
|
with Timeout(5, False):
|
||||||
|
data = mysock.makefile().readline()
|
||||||
|
if data is None:
|
||||||
|
... # 5 seconds passed without reading a line
|
||||||
|
else:
|
||||||
|
... # a line was read within 5 seconds
|
||||||
|
|
||||||
|
As a very special case, if *seconds* is None, the timer is not scheduled,
|
||||||
|
and is only useful if you're planning to raise it directly.
|
||||||
|
|
||||||
|
There are two Timeout caveats to be aware of:
|
||||||
|
|
||||||
|
* If the code block in the try/finally or with-block never cooperatively yields, the timeout cannot be raised. In Eventlet, this should rarely be a problem, but be aware that you cannot time out CPU-only operations with this class.
|
||||||
|
* If the code block catches and doesn't re-raise :class:`BaseException` (for example, with ``except:``), then it will catch the Timeout exception, and might not abort as intended.
|
||||||
|
|
||||||
|
When catching timeouts, keep in mind that the one you catch may not be the
|
||||||
|
one you have set; if you going to silence a timeout, always check that it's
|
||||||
|
the same instance that you set::
|
||||||
|
|
||||||
|
timeout = Timeout(1)
|
||||||
|
try:
|
||||||
|
...
|
||||||
|
except Timeout, t:
|
||||||
|
if t is not timeout:
|
||||||
|
raise # not my timeout
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: eventlet.with_timeout(seconds, function, *args, **kwds)
|
||||||
|
|
||||||
|
Wrap a call to some (yielding) function with a timeout; if the called
|
||||||
|
function fails to return before the timeout, cancel it and return a flag
|
||||||
|
value.
|
||||||
|
|
||||||
|
:param seconds: seconds before timeout occurs
|
||||||
|
:type seconds: int or float
|
||||||
|
:param func: the callable to execute with a timeout; it must cooperatively yield, or else the timeout will not be able to trigger
|
||||||
|
:param \*args: positional arguments to pass to *func*
|
||||||
|
:param \*\*kwds: keyword arguments to pass to *func*
|
||||||
|
:param timeout_value: value to return if timeout occurs (by default raises
|
||||||
|
:class:`Timeout`)
|
||||||
|
|
||||||
|
:rtype: Value returned by *func* if *func* returns before *seconds*, else
|
||||||
|
*timeout_value* if provided, else raises :class:`Timeout`.
|
||||||
|
|
||||||
|
:exception Timeout: if *func* times out and no ``timeout_value`` has
|
||||||
|
been provided.
|
||||||
|
:exception: Any exception raised by *func*
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
data = with_timeout(30, urllib2.open, 'http://www.google.com/', timeout_value="")
|
||||||
|
|
||||||
|
Here *data* is either the result of the ``get()`` call, or the empty string
|
||||||
|
if it took too long to return. Any exception raised by the ``get()`` call
|
||||||
|
is passed through to the caller.
|
||||||
|
|
||||||
These are the basic primitives of Eventlet; there are a lot more out there in the other Eventlet modules; check out the :doc:`modules`.
|
These are the basic primitives of Eventlet; there are a lot more out there in the other Eventlet modules; check out the :doc:`modules`.
|
||||||
|
@@ -5,21 +5,25 @@ try:
|
|||||||
from eventlet import greenthread
|
from eventlet import greenthread
|
||||||
from eventlet import greenpool
|
from eventlet import greenpool
|
||||||
from eventlet import queue
|
from eventlet import queue
|
||||||
|
from eventlet import timeout
|
||||||
|
|
||||||
sleep = greenthread.sleep
|
sleep = greenthread.sleep
|
||||||
|
|
||||||
spawn = greenthread.spawn
|
spawn = greenthread.spawn
|
||||||
spawn_n = greenthread.spawn_n
|
spawn_n = greenthread.spawn_n
|
||||||
spawn_after = greenthread.spawn_after
|
spawn_after = greenthread.spawn_after
|
||||||
call_after_global = greenthread.call_after_global
|
|
||||||
TimeoutError = greenthread.TimeoutError
|
Timeout = timeout.Timeout
|
||||||
exc_after = greenthread.exc_after
|
with_timeout = timeout.with_timeout
|
||||||
with_timeout = greenthread.with_timeout
|
|
||||||
|
|
||||||
GreenPool = greenpool.GreenPool
|
GreenPool = greenpool.GreenPool
|
||||||
GreenPile = greenpool.GreenPile
|
GreenPile = greenpool.GreenPile
|
||||||
|
|
||||||
Queue = queue.Queue
|
Queue = queue.Queue
|
||||||
|
|
||||||
|
# deprecated
|
||||||
|
TimeoutError = timeout.Timeout
|
||||||
|
exc_after = greenthread.exc_after
|
||||||
|
call_after_global = greenthread.call_after_global
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# this is to make Debian packaging easier
|
# this is to make Debian packaging easier
|
||||||
import traceback
|
import traceback
|
||||||
|
@@ -4,7 +4,8 @@ import time
|
|||||||
|
|
||||||
from eventlet.pools import Pool
|
from eventlet.pools import Pool
|
||||||
from eventlet.processes import DeadProcess
|
from eventlet.processes import DeadProcess
|
||||||
from eventlet import api
|
from eventlet import timeout
|
||||||
|
from eventlet import greenthread
|
||||||
|
|
||||||
|
|
||||||
class ConnectTimeout(Exception):
|
class ConnectTimeout(Exception):
|
||||||
@@ -89,7 +90,7 @@ class BaseConnectionPool(Pool):
|
|||||||
|
|
||||||
if next_delay > 0:
|
if next_delay > 0:
|
||||||
# set up a continuous self-calling loop
|
# set up a continuous self-calling loop
|
||||||
self._expiration_timer = api.call_after(next_delay,
|
self._expiration_timer = greenthread.spawn_after(next_delay,
|
||||||
self._schedule_expiration)
|
self._schedule_expiration)
|
||||||
|
|
||||||
def _expire_old_connections(self, now):
|
def _expire_old_connections(self, now):
|
||||||
@@ -248,7 +249,7 @@ class TpooledConnectionPool(BaseConnectionPool):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def connect(cls, db_module, connect_timeout, *args, **kw):
|
def connect(cls, db_module, connect_timeout, *args, **kw):
|
||||||
timeout = api.exc_after(connect_timeout, ConnectTimeout())
|
timeout = timeout.Timeout(connect_timeout, ConnectTimeout())
|
||||||
try:
|
try:
|
||||||
from eventlet import tpool
|
from eventlet import tpool
|
||||||
conn = tpool.execute(db_module.connect, *args, **kw)
|
conn = tpool.execute(db_module.connect, *args, **kw)
|
||||||
@@ -268,7 +269,7 @@ class RawConnectionPool(BaseConnectionPool):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def connect(cls, db_module, connect_timeout, *args, **kw):
|
def connect(cls, db_module, connect_timeout, *args, **kw):
|
||||||
timeout = api.exc_after(connect_timeout, ConnectTimeout())
|
timeout = timeout.Timeout(connect_timeout, ConnectTimeout())
|
||||||
try:
|
try:
|
||||||
return db_module.connect(*args, **kw)
|
return db_module.connect(*args, **kw)
|
||||||
finally:
|
finally:
|
||||||
|
@@ -2,7 +2,7 @@ from eventlet import patcher
|
|||||||
from eventlet.green import thread
|
from eventlet.green import thread
|
||||||
from eventlet.green import time
|
from eventlet.green import time
|
||||||
|
|
||||||
__patched__ = ['_start_new_thread', '_allocate_lock', '_get_ident']
|
__patched__ = ['_start_new_thread', '_allocate_lock', '_get_ident', '_sleep']
|
||||||
|
|
||||||
patcher.inject('threading',
|
patcher.inject('threading',
|
||||||
globals(),
|
globals(),
|
||||||
|
@@ -2,6 +2,7 @@ import sys
|
|||||||
|
|
||||||
from eventlet import event
|
from eventlet import event
|
||||||
from eventlet import hubs
|
from eventlet import hubs
|
||||||
|
from eventlet import timeout
|
||||||
from eventlet.hubs import timer
|
from eventlet.hubs import timer
|
||||||
from eventlet.support import greenlets as greenlet
|
from eventlet.support import greenlets as greenlet
|
||||||
import warnings
|
import warnings
|
||||||
@@ -9,7 +10,6 @@ import warnings
|
|||||||
__all__ = ['getcurrent', 'sleep', 'spawn', 'spawn_n', 'call_after_global', 'call_after_local', 'GreenThread']
|
__all__ = ['getcurrent', 'sleep', 'spawn', 'spawn_n', 'call_after_global', 'call_after_local', 'GreenThread']
|
||||||
|
|
||||||
getcurrent = greenlet.getcurrent
|
getcurrent = greenlet.getcurrent
|
||||||
TimeoutError = hubs.TimeoutError
|
|
||||||
|
|
||||||
def sleep(seconds=0):
|
def sleep(seconds=0):
|
||||||
"""Yield control to another eligible coroutine until at least *seconds* have
|
"""Yield control to another eligible coroutine until at least *seconds* have
|
||||||
@@ -170,57 +170,17 @@ def exc_after(seconds, *throw_args):
|
|||||||
else:
|
else:
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("Instead of exc_after, which is deprecated, use "
|
||||||
|
"Timeout(seconds, exception)",
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
if seconds is None: # dummy argument, do nothing
|
if seconds is None: # dummy argument, do nothing
|
||||||
return timer.Timer(seconds, lambda: None)
|
return timer.Timer(seconds, lambda: None)
|
||||||
hub = hubs.get_hub()
|
hub = hubs.get_hub()
|
||||||
return hub.schedule_call_local(seconds, getcurrent().throw, *throw_args)
|
return hub.schedule_call_local(seconds, getcurrent().throw, *throw_args)
|
||||||
|
|
||||||
|
# deprecate, remove
|
||||||
def with_timeout(seconds, func, *args, **kwds):
|
TimeoutError = timeout.Timeout
|
||||||
"""Wrap a call to some (yielding) function with a timeout; if the called
|
with_timeout = timeout.with_timeout
|
||||||
function fails to return before the timeout, cancel it and return a flag
|
|
||||||
value.
|
|
||||||
|
|
||||||
:param seconds: seconds before timeout occurs
|
|
||||||
:type seconds: int or float
|
|
||||||
:param func: the callable to execute with a timeout; must be one of the
|
|
||||||
functions that implicitly or explicitly yields
|
|
||||||
:param \*args: positional arguments to pass to *func*
|
|
||||||
:param \*\*kwds: keyword arguments to pass to *func*
|
|
||||||
:param timeout_value: value to return if timeout occurs (default raise
|
|
||||||
:class:`~eventlet.api.TimeoutError`)
|
|
||||||
|
|
||||||
:rtype: Value returned by *func* if *func* returns before *seconds*, else
|
|
||||||
*timeout_value* if provided, else raise ``TimeoutError``
|
|
||||||
|
|
||||||
:exception TimeoutError: if *func* times out and no ``timeout_value`` has
|
|
||||||
been provided.
|
|
||||||
:exception *any*: Any exception raised by *func*
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
data = with_timeout(30, httpc.get, 'http://www.google.com/', timeout_value="")
|
|
||||||
|
|
||||||
Here *data* is either the result of the ``get()`` call, or the empty string if
|
|
||||||
it took too long to return. Any exception raised by the ``get()`` call is
|
|
||||||
passed through to the caller.
|
|
||||||
"""
|
|
||||||
# Recognize a specific keyword argument, while also allowing pass-through
|
|
||||||
# of any other keyword arguments accepted by func. Use pop() so we don't
|
|
||||||
# pass timeout_value through to func().
|
|
||||||
has_timeout_value = "timeout_value" in kwds
|
|
||||||
timeout_value = kwds.pop("timeout_value", None)
|
|
||||||
error = TimeoutError()
|
|
||||||
timeout = exc_after(seconds, error)
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
return func(*args, **kwds)
|
|
||||||
except TimeoutError, ex:
|
|
||||||
if ex is error and has_timeout_value:
|
|
||||||
return timeout_value
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
timeout.cancel()
|
|
||||||
|
|
||||||
def _spawn_n(seconds, func, args, kwargs):
|
def _spawn_n(seconds, func, args, kwargs):
|
||||||
hub = hubs.get_hub()
|
hub = hubs.get_hub()
|
||||||
|
@@ -83,12 +83,9 @@ def get_hub():
|
|||||||
hub = _threadlocal.hub = _threadlocal.Hub()
|
hub = _threadlocal.hub = _threadlocal.Hub()
|
||||||
return hub
|
return hub
|
||||||
|
|
||||||
class TimeoutError(Exception):
|
from eventlet import timeout
|
||||||
"""Exception raised if an asynchronous operation times out"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def trampoline(fd, read=None, write=None, timeout=None,
|
def trampoline(fd, read=None, write=None, timeout=None,
|
||||||
timeout_exc=TimeoutError):
|
timeout_exc=timeout.Timeout):
|
||||||
"""Suspend the current coroutine until the given socket object or file
|
"""Suspend the current coroutine until the given socket object or file
|
||||||
descriptor is ready to *read*, ready to *write*, or the specified
|
descriptor is ready to *read*, ready to *write*, or the specified
|
||||||
*timeout* elapses, depending on arguments specified.
|
*timeout* elapses, depending on arguments specified.
|
||||||
|
@@ -29,7 +29,10 @@ class event_wrapper(object):
|
|||||||
if self.impl is not None:
|
if self.impl is not None:
|
||||||
self.impl.delete()
|
self.impl.delete()
|
||||||
self.impl = None
|
self.impl = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pending(self):
|
||||||
|
return bool(self.impl and self.impl.pending())
|
||||||
|
|
||||||
class Hub(BaseHub):
|
class Hub(BaseHub):
|
||||||
|
|
||||||
|
@@ -29,6 +29,10 @@ class Timer(object):
|
|||||||
@property
|
@property
|
||||||
def cancelled(self):
|
def cancelled(self):
|
||||||
return self._cancelled
|
return self._cancelled
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pending(self):
|
||||||
|
return not (self._cancelled or self.called)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
secs = getattr(self, 'seconds', None)
|
secs = getattr(self, 'seconds', None)
|
||||||
|
@@ -28,8 +28,9 @@ from Queue import Full, Empty
|
|||||||
|
|
||||||
_NONE = object()
|
_NONE = object()
|
||||||
from eventlet.hubs import get_hub
|
from eventlet.hubs import get_hub
|
||||||
from eventlet.greenthread import getcurrent, exc_after
|
from eventlet.greenthread import getcurrent
|
||||||
from eventlet.event import Event
|
from eventlet.event import Event
|
||||||
|
from eventlet.timeout import Timeout
|
||||||
|
|
||||||
__all__ = ['Queue', 'PriorityQueue', 'LifoQueue', 'LightQueue', 'Full', 'Empty']
|
__all__ = ['Queue', 'PriorityQueue', 'LifoQueue', 'LightQueue', 'Full', 'Empty']
|
||||||
|
|
||||||
@@ -211,7 +212,7 @@ class LightQueue(object):
|
|||||||
elif block:
|
elif block:
|
||||||
waiter = ItemWaiter(item)
|
waiter = ItemWaiter(item)
|
||||||
self.putters.add(waiter)
|
self.putters.add(waiter)
|
||||||
timeout = exc_after(timeout, Full)
|
timeout = Timeout(timeout, Full)
|
||||||
try:
|
try:
|
||||||
if self.getters:
|
if self.getters:
|
||||||
self._schedule_unlock()
|
self._schedule_unlock()
|
||||||
@@ -259,7 +260,7 @@ class LightQueue(object):
|
|||||||
raise Empty
|
raise Empty
|
||||||
elif block:
|
elif block:
|
||||||
waiter = Waiter()
|
waiter = Waiter()
|
||||||
timeout = exc_after(timeout, Empty)
|
timeout = Timeout(timeout, Empty)
|
||||||
try:
|
try:
|
||||||
self.getters.add(waiter)
|
self.getters.add(waiter)
|
||||||
if self.putters:
|
if self.putters:
|
||||||
|
128
eventlet/timeout.py
Normal file
128
eventlet/timeout.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Copyright (c) 2009-2010 Denis Bilenko and Eventlet contributors. See LICENSE for details.
|
||||||
|
from eventlet.support import greenlets as greenlet
|
||||||
|
from eventlet.hubs import get_hub
|
||||||
|
|
||||||
|
__all__ = ['Timeout',
|
||||||
|
'with_timeout']
|
||||||
|
|
||||||
|
_NONE = object()
|
||||||
|
|
||||||
|
try:
|
||||||
|
BaseException
|
||||||
|
except NameError: # Python < 2.5
|
||||||
|
class BaseException:
|
||||||
|
# not subclassing from object() intentionally, because in
|
||||||
|
# that case "raise Timeout" fails with TypeError.
|
||||||
|
pass
|
||||||
|
|
||||||
|
# deriving from BaseException so that "except Exception, e" doesn't catch
|
||||||
|
# Timeout exceptions.
|
||||||
|
class Timeout(BaseException):
|
||||||
|
"""Raises *exception* in the current greenthread after *timeout* seconds.
|
||||||
|
|
||||||
|
When *exception* is omitted or ``None``, the :class:`Timeout` instance
|
||||||
|
itself is raised. If *seconds* is None, the timer is not scheduled, and is
|
||||||
|
only useful if you're planning to raise it directly.
|
||||||
|
|
||||||
|
Timeout objects are context managers, and so can be used in with statements.
|
||||||
|
When used in a with statement, if *exception* is ``False``, the timeout is
|
||||||
|
still raised, but the context manager suppresses it, so the code outside the
|
||||||
|
with-block won't see it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, seconds=None, exception=None):
|
||||||
|
self.seconds = seconds
|
||||||
|
self.exception = exception
|
||||||
|
self.timer = None
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""Schedule the timeout. This is called on construction, so
|
||||||
|
it should not be called explicitly, unless the timer has been
|
||||||
|
cancelled."""
|
||||||
|
assert not self.pending, '%r is already started; to restart it, cancel it first' % self
|
||||||
|
if self.seconds is None: # "fake" timeout (never expires)
|
||||||
|
self.timer = None
|
||||||
|
elif self.exception is None or self.exception is False: # timeout that raises self
|
||||||
|
self.timer = get_hub().schedule_call_global(self.seconds, greenlet.getcurrent().throw, self)
|
||||||
|
else: # regular timeout with user-provided exception
|
||||||
|
self.timer = get_hub().schedule_call_global(self.seconds, greenlet.getcurrent().throw, self.exception)
|
||||||
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pending(self):
|
||||||
|
"""Return True if the timeout is scheduled to be raised."""
|
||||||
|
if self.timer is not None:
|
||||||
|
return self.timer.pending
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
"""If the timeout is pending, cancel it. Otherwise, do nothing."""
|
||||||
|
if self.timer is not None:
|
||||||
|
self.timer.cancel()
|
||||||
|
self.timer = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
classname = self.__class__.__name__
|
||||||
|
except AttributeError: # Python < 2.5
|
||||||
|
classname = 'Timeout'
|
||||||
|
if self.pending:
|
||||||
|
pending = ' pending'
|
||||||
|
else:
|
||||||
|
pending = ''
|
||||||
|
if self.exception is None:
|
||||||
|
exception = ''
|
||||||
|
else:
|
||||||
|
exception = ' exception=%r' % self.exception
|
||||||
|
return '<%s at %s seconds=%s%s%s>' % (classname, hex(id(self)), self.seconds, exception, pending)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
>>> raise Timeout
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
Timeout
|
||||||
|
"""
|
||||||
|
if self.seconds is None:
|
||||||
|
return ''
|
||||||
|
if self.seconds == 1:
|
||||||
|
suffix = ''
|
||||||
|
else:
|
||||||
|
suffix = 's'
|
||||||
|
if self.exception is None:
|
||||||
|
return '%s second%s' % (self.seconds, suffix)
|
||||||
|
elif self.exception is False:
|
||||||
|
return '%s second%s (silent)' % (self.seconds, suffix)
|
||||||
|
else:
|
||||||
|
return '%s second%s (%s)' % (self.seconds, suffix, self.exception)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if self.timer is None:
|
||||||
|
self.start()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, typ, value, tb):
|
||||||
|
self.cancel()
|
||||||
|
if value is self and self.exception is False:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def with_timeout(seconds, function, *args, **kwds):
|
||||||
|
"""Wrap a call to some (yielding) function with a timeout; if the called
|
||||||
|
function fails to return before the timeout, cancel it and return a flag
|
||||||
|
value.
|
||||||
|
"""
|
||||||
|
timeout_value = kwds.pop("timeout_value", _NONE)
|
||||||
|
timeout = Timeout(seconds)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
return function(*args, **kwds)
|
||||||
|
except Timeout, ex:
|
||||||
|
if ex is timeout and timeout_value is not _NONE:
|
||||||
|
return timeout_value
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
timeout.cancel()
|
||||||
|
|
@@ -99,9 +99,9 @@ class LimitedTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
TEST_TIMEOUT = 1
|
TEST_TIMEOUT = 1
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
from eventlet import api
|
import eventlet
|
||||||
self.timer = api.exc_after(self.TEST_TIMEOUT,
|
self.timer = eventlet.Timeout(self.TEST_TIMEOUT,
|
||||||
TestIsTakingTooLong(self.TEST_TIMEOUT))
|
TestIsTakingTooLong(self.TEST_TIMEOUT))
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.timer.cancel()
|
self.timer.cancel()
|
||||||
|
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
from tests import skipped, skip_unless, skip_with_pyevent
|
from tests import skipped, skip_unless, skip_with_pyevent
|
||||||
from unittest import TestCase, main
|
from unittest import TestCase, main
|
||||||
from eventlet import api
|
|
||||||
from eventlet import event
|
from eventlet import event
|
||||||
from eventlet import db_pool
|
from eventlet import db_pool
|
||||||
|
import eventlet
|
||||||
import os
|
import os
|
||||||
|
|
||||||
class DBTester(object):
|
class DBTester(object):
|
||||||
@@ -145,7 +145,7 @@ class DBConnectionPool(DBTester):
|
|||||||
results.append(2)
|
results.append(2)
|
||||||
evt.send()
|
evt.send()
|
||||||
evt2 = event.Event()
|
evt2 = event.Event()
|
||||||
api.spawn(a_query)
|
eventlet.spawn(a_query)
|
||||||
results.append(1)
|
results.append(1)
|
||||||
self.assertEqual([1], results)
|
self.assertEqual([1], results)
|
||||||
evt.wait()
|
evt.wait()
|
||||||
@@ -232,8 +232,8 @@ class DBConnectionPool(DBTester):
|
|||||||
results.append(2)
|
results.append(2)
|
||||||
evt2.send()
|
evt2.send()
|
||||||
|
|
||||||
api.spawn(long_running_query)
|
eventlet.spawn(long_running_query)
|
||||||
api.spawn(short_running_query)
|
eventlet.spawn(short_running_query)
|
||||||
evt.wait()
|
evt.wait()
|
||||||
evt2.wait()
|
evt2.wait()
|
||||||
results.sort()
|
results.sort()
|
||||||
@@ -304,17 +304,17 @@ class DBConnectionPool(DBTester):
|
|||||||
self.connection = self.pool.get()
|
self.connection = self.pool.get()
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
api.sleep(0.01) # not long enough to trigger the idle timeout
|
eventlet.sleep(0.01) # not long enough to trigger the idle timeout
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
self.connection = self.pool.get()
|
self.connection = self.pool.get()
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
api.sleep(0.01) # idle timeout should have fired but done nothing
|
eventlet.sleep(0.01) # idle timeout should have fired but done nothing
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
self.connection = self.pool.get()
|
self.connection = self.pool.get()
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
api.sleep(0.03) # long enough to trigger idle timeout for real
|
eventlet.sleep(0.03) # long enough to trigger idle timeout for real
|
||||||
self.assertEquals(len(self.pool.free_items), 0)
|
self.assertEquals(len(self.pool.free_items), 0)
|
||||||
|
|
||||||
@skipped
|
@skipped
|
||||||
@@ -323,11 +323,11 @@ class DBConnectionPool(DBTester):
|
|||||||
self.pool = self.create_pool(max_size=2, max_idle=0.02)
|
self.pool = self.create_pool(max_size=2, max_idle=0.02)
|
||||||
self.connection, conn2 = self.pool.get(), self.pool.get()
|
self.connection, conn2 = self.pool.get(), self.pool.get()
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
api.sleep(0.01)
|
eventlet.sleep(0.01)
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
conn2.close()
|
conn2.close()
|
||||||
self.assertEquals(len(self.pool.free_items), 2)
|
self.assertEquals(len(self.pool.free_items), 2)
|
||||||
api.sleep(0.02) # trigger cleanup of conn1 but not conn2
|
eventlet.sleep(0.02) # trigger cleanup of conn1 but not conn2
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
|
|
||||||
@skipped
|
@skipped
|
||||||
@@ -337,12 +337,12 @@ class DBConnectionPool(DBTester):
|
|||||||
self.connection = self.pool.get()
|
self.connection = self.pool.get()
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
api.sleep(0.01) # not long enough to trigger the age timeout
|
eventlet.sleep(0.01) # not long enough to trigger the age timeout
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
self.connection = self.pool.get()
|
self.connection = self.pool.get()
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
api.sleep(0.05) # long enough to trigger age timeout
|
eventlet.sleep(0.05) # long enough to trigger age timeout
|
||||||
self.assertEquals(len(self.pool.free_items), 0)
|
self.assertEquals(len(self.pool.free_items), 0)
|
||||||
|
|
||||||
@skipped
|
@skipped
|
||||||
@@ -352,9 +352,9 @@ class DBConnectionPool(DBTester):
|
|||||||
self.connection, conn2 = self.pool.get(), self.pool.get()
|
self.connection, conn2 = self.pool.get(), self.pool.get()
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
api.sleep(0) # not long enough to trigger the age timeout
|
eventlet.sleep(0) # not long enough to trigger the age timeout
|
||||||
self.assertEquals(len(self.pool.free_items), 1)
|
self.assertEquals(len(self.pool.free_items), 1)
|
||||||
api.sleep(0.2) # long enough to trigger age timeout
|
eventlet.sleep(0.2) # long enough to trigger age timeout
|
||||||
self.assertEquals(len(self.pool.free_items), 0)
|
self.assertEquals(len(self.pool.free_items), 0)
|
||||||
conn2.close() # should not be added to the free items
|
conn2.close() # should not be added to the free items
|
||||||
self.assertEquals(len(self.pool.free_items), 0)
|
self.assertEquals(len(self.pool.free_items), 0)
|
||||||
@@ -374,13 +374,13 @@ class DBConnectionPool(DBTester):
|
|||||||
def retrieve(pool, ev):
|
def retrieve(pool, ev):
|
||||||
c = pool.get()
|
c = pool.get()
|
||||||
ev.send(c)
|
ev.send(c)
|
||||||
api.spawn(retrieve, self.pool, e)
|
eventlet.spawn(retrieve, self.pool, e)
|
||||||
api.sleep(0) # these two sleeps should advance the retrieve
|
eventlet.sleep(0) # these two sleeps should advance the retrieve
|
||||||
api.sleep(0) # coroutine until it's waiting in get()
|
eventlet.sleep(0) # coroutine until it's waiting in get()
|
||||||
self.assertEquals(self.pool.free(), 0)
|
self.assertEquals(self.pool.free(), 0)
|
||||||
self.assertEquals(self.pool.waiting(), 1)
|
self.assertEquals(self.pool.waiting(), 1)
|
||||||
self.pool.put(self.connection)
|
self.pool.put(self.connection)
|
||||||
timer = api.exc_after(1, api.TimeoutError)
|
timer = eventlet.Timeout(1)
|
||||||
conn = e.wait()
|
conn = e.wait()
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
self.assertEquals(self.pool.free(), 0)
|
self.assertEquals(self.pool.free(), 0)
|
||||||
|
@@ -64,6 +64,6 @@ class TestEvent(LimitedTestCase):
|
|||||||
self.assertRaises(RuntimeError, evt.wait)
|
self.assertRaises(RuntimeError, evt.wait)
|
||||||
evt.reset()
|
evt.reset()
|
||||||
# shouldn't see the RuntimeError again
|
# shouldn't see the RuntimeError again
|
||||||
eventlet.exc_after(0.001, eventlet.TimeoutError('from test_double_exception'))
|
eventlet.Timeout(0.001)
|
||||||
self.assertRaises(eventlet.TimeoutError, evt.wait)
|
self.assertRaises(eventlet.Timeout, evt.wait)
|
||||||
|
|
||||||
|
@@ -246,7 +246,7 @@ class TestGreenIo(LimitedTestCase):
|
|||||||
try:
|
try:
|
||||||
# try and get some data off of this pipe
|
# try and get some data off of this pipe
|
||||||
# but bail before any is sent
|
# but bail before any is sent
|
||||||
eventlet.exc_after(0.01, eventlet.TimeoutError)
|
eventlet.Timeout(0.01)
|
||||||
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
client.connect(('127.0.0.1', bound_port))
|
client.connect(('127.0.0.1', bound_port))
|
||||||
wrap_rfile = client.makefile()
|
wrap_rfile = client.makefile()
|
||||||
|
@@ -120,7 +120,7 @@ class GreenPool(tests.LimitedTestCase):
|
|||||||
self.assertEquals(pool.free(), num_free)
|
self.assertEquals(pool.free(), num_free)
|
||||||
def wait_long_time(e):
|
def wait_long_time(e):
|
||||||
e.wait()
|
e.wait()
|
||||||
timer = eventlet.exc_after(1, eventlet.TimeoutError)
|
timer = eventlet.Timeout(1)
|
||||||
try:
|
try:
|
||||||
evt = event.Event()
|
evt = event.Event()
|
||||||
for x in xrange(num_free):
|
for x in xrange(num_free):
|
||||||
@@ -132,7 +132,7 @@ class GreenPool(tests.LimitedTestCase):
|
|||||||
|
|
||||||
# if the runtime error is not raised it means the pool had
|
# if the runtime error is not raised it means the pool had
|
||||||
# some unexpected free items
|
# some unexpected free items
|
||||||
timer = eventlet.exc_after(0, RuntimeError())
|
timer = eventlet.Timeout(0, RuntimeError)
|
||||||
try:
|
try:
|
||||||
self.assertRaises(RuntimeError, pool.spawn, wait_long_time, evt)
|
self.assertRaises(RuntimeError, pool.spawn, wait_long_time, evt)
|
||||||
finally:
|
finally:
|
||||||
@@ -182,7 +182,7 @@ class GreenPool(tests.LimitedTestCase):
|
|||||||
tp = pools.TokenPool(max_size=1)
|
tp = pools.TokenPool(max_size=1)
|
||||||
token = tp.get() # empty out the pool
|
token = tp.get() # empty out the pool
|
||||||
def do_receive(tp):
|
def do_receive(tp):
|
||||||
timer = eventlet.exc_after(0, RuntimeError())
|
timer = eventlet.Timeout(0, RuntimeError())
|
||||||
try:
|
try:
|
||||||
t = tp.get()
|
t = tp.get()
|
||||||
self.fail("Shouldn't have recieved anything from the pool")
|
self.fail("Shouldn't have recieved anything from the pool")
|
||||||
@@ -316,8 +316,8 @@ class GreenPile(tests.LimitedTestCase):
|
|||||||
for i in xrange(4):
|
for i in xrange(4):
|
||||||
p.spawn(passthru, i)
|
p.spawn(passthru, i)
|
||||||
# now it should be full and this should time out
|
# now it should be full and this should time out
|
||||||
eventlet.exc_after(0, eventlet.TimeoutError)
|
eventlet.Timeout(0)
|
||||||
self.assertRaises(eventlet.TimeoutError, p.spawn, passthru, "time out")
|
self.assertRaises(eventlet.Timeout, p.spawn, passthru, "time out")
|
||||||
# verify that the spawn breakage didn't interrupt the sequence
|
# verify that the spawn breakage didn't interrupt the sequence
|
||||||
# and terminates properly
|
# and terminates properly
|
||||||
for i in xrange(4,10):
|
for i in xrange(4,10):
|
||||||
|
@@ -10,7 +10,7 @@ if parent_dir not in sys.path:
|
|||||||
|
|
||||||
# hacky hacks: skip test__api_timeout when under 2.4 because otherwise it SyntaxErrors
|
# hacky hacks: skip test__api_timeout when under 2.4 because otherwise it SyntaxErrors
|
||||||
if sys.version_info < (2,5):
|
if sys.version_info < (2,5):
|
||||||
argv = sys.argv + ["--exclude=.*test__api_timeout.*"]
|
argv = sys.argv + ["--exclude=.*timeout_test_with_statement.*"]
|
||||||
else:
|
else:
|
||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
from unittest import TestCase, main
|
from unittest import TestCase, main
|
||||||
|
|
||||||
|
import eventlet
|
||||||
from eventlet import api
|
from eventlet import api
|
||||||
from eventlet import coros
|
from eventlet import coros
|
||||||
from eventlet import pools
|
from eventlet import pools
|
||||||
@@ -11,7 +12,6 @@ class IntPool(pools.Pool):
|
|||||||
|
|
||||||
|
|
||||||
class TestIntPool(TestCase):
|
class TestIntPool(TestCase):
|
||||||
mode = 'static'
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.pool = IntPool(min_size=0, max_size=4)
|
self.pool = IntPool(min_size=0, max_size=4)
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ class TestIntPool(TestCase):
|
|||||||
self.assertEquals(self.pool.get(), two)
|
self.assertEquals(self.pool.get(), two)
|
||||||
|
|
||||||
def test_putting_to_queue(self):
|
def test_putting_to_queue(self):
|
||||||
timer = api.exc_after(0.1, api.TimeoutError)
|
timer = eventlet.Timeout(0.1)
|
||||||
try:
|
try:
|
||||||
size = 2
|
size = 2
|
||||||
self.pool = IntPool(min_size=0, max_size=size)
|
self.pool = IntPool(min_size=0, max_size=size)
|
||||||
|
@@ -3,7 +3,7 @@ import eventlet
|
|||||||
from eventlet import event
|
from eventlet import event
|
||||||
|
|
||||||
def do_bail(q):
|
def do_bail(q):
|
||||||
eventlet.exc_after(0, RuntimeError())
|
eventlet.Timeout(0, RuntimeError())
|
||||||
try:
|
try:
|
||||||
result = q.get()
|
result = q.get()
|
||||||
return result
|
return result
|
||||||
|
@@ -27,7 +27,7 @@ def assimilate_patched(name):
|
|||||||
test_method()
|
test_method()
|
||||||
restart_hub()
|
restart_hub()
|
||||||
globals()[method_name] = test_main
|
globals()[method_name] = test_main
|
||||||
modobj.test_main.__name__ = name + '.test_main'
|
test_main.__name__ = name + '.test_main'
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print "No test_main for %s, assuming it tests on import" % name
|
print "No test_main for %s, assuming it tests on import" % name
|
||||||
|
|
||||||
|
@@ -1,106 +0,0 @@
|
|||||||
from __future__ import with_statement
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import weakref
|
|
||||||
import time
|
|
||||||
from eventlet.api import sleep, timeout, TimeoutError, _SilentException
|
|
||||||
DELAY = 0.01
|
|
||||||
|
|
||||||
class Error(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_api(self):
|
|
||||||
# Nothing happens if with-block finishes before the timeout expires
|
|
||||||
with timeout(DELAY*2):
|
|
||||||
sleep(DELAY)
|
|
||||||
sleep(DELAY*2) # check if timer was actually cancelled
|
|
||||||
|
|
||||||
# An exception will be raised if it's not
|
|
||||||
try:
|
|
||||||
with timeout(DELAY):
|
|
||||||
sleep(DELAY*2)
|
|
||||||
except TimeoutError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise AssertionError('must raise TimeoutError')
|
|
||||||
|
|
||||||
# You can customize the exception raised:
|
|
||||||
try:
|
|
||||||
with timeout(DELAY, IOError("Operation takes way too long")):
|
|
||||||
sleep(DELAY*2)
|
|
||||||
except IOError, ex:
|
|
||||||
assert str(ex)=="Operation takes way too long", repr(ex)
|
|
||||||
|
|
||||||
# Providing classes instead of values should be possible too:
|
|
||||||
try:
|
|
||||||
with timeout(DELAY, ValueError):
|
|
||||||
sleep(DELAY*2)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# basically, anything that greenlet.throw accepts work:
|
|
||||||
try:
|
|
||||||
1/0
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
with timeout(DELAY, *sys.exc_info()):
|
|
||||||
sleep(DELAY*2)
|
|
||||||
raise AssertionError('should not get there')
|
|
||||||
raise AssertionError('should not get there')
|
|
||||||
except ZeroDivisionError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise AssertionError('should not get there')
|
|
||||||
|
|
||||||
# It's possible to cancel the timer inside the block:
|
|
||||||
with timeout(DELAY) as timer:
|
|
||||||
timer.cancel()
|
|
||||||
sleep(DELAY*2)
|
|
||||||
|
|
||||||
# To silent the exception, pass None as second parameter. The with-block
|
|
||||||
# will be interrupted with _SilentException, but it won't be propagated
|
|
||||||
# outside.
|
|
||||||
XDELAY=0.1
|
|
||||||
start = time.time()
|
|
||||||
with timeout(XDELAY, None):
|
|
||||||
sleep(XDELAY*10)
|
|
||||||
delta = (time.time()-start)
|
|
||||||
assert delta<XDELAY*10, delta
|
|
||||||
|
|
||||||
# passing None as seconds disables the timer
|
|
||||||
with timeout(None):
|
|
||||||
sleep(DELAY)
|
|
||||||
sleep(DELAY)
|
|
||||||
|
|
||||||
def test_ref(self):
|
|
||||||
err = Error()
|
|
||||||
err_ref = weakref.ref(err)
|
|
||||||
with timeout(DELAY*2, err):
|
|
||||||
sleep(DELAY)
|
|
||||||
del err
|
|
||||||
assert not err_ref(), repr(err_ref())
|
|
||||||
|
|
||||||
def test_nested_timeout(self):
|
|
||||||
with timeout(DELAY, None):
|
|
||||||
with timeout(DELAY*2, None):
|
|
||||||
sleep(DELAY*3)
|
|
||||||
raise AssertionError('should not get there')
|
|
||||||
|
|
||||||
with timeout(DELAY, _SilentException()):
|
|
||||||
with timeout(DELAY*2, _SilentException()):
|
|
||||||
sleep(DELAY*3)
|
|
||||||
raise AssertionError('should not get there')
|
|
||||||
|
|
||||||
# this case fails and there's no intent to fix it.
|
|
||||||
# just don't do it like that
|
|
||||||
#with timeout(DELAY, _SilentException):
|
|
||||||
# with timeout(DELAY*2, _SilentException):
|
|
||||||
# sleep(DELAY*3)
|
|
||||||
# assert False, 'should not get there'
|
|
||||||
|
|
||||||
|
|
||||||
if __name__=='__main__':
|
|
||||||
unittest.main()
|
|
||||||
|
|
@@ -19,7 +19,7 @@ class TestQueue(LimitedTestCase):
|
|||||||
def test_send_last(self):
|
def test_send_last(self):
|
||||||
q = coros.queue()
|
q = coros.queue()
|
||||||
def waiter(q):
|
def waiter(q):
|
||||||
timer = api.exc_after(0.1, api.TimeoutError)
|
timer = eventlet.Timeout(0.1)
|
||||||
self.assertEquals(q.wait(), 'hi2')
|
self.assertEquals(q.wait(), 'hi2')
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ class TestQueue(LimitedTestCase):
|
|||||||
q = coros.queue()
|
q = coros.queue()
|
||||||
|
|
||||||
def do_receive(q, evt):
|
def do_receive(q, evt):
|
||||||
api.exc_after(0, RuntimeError())
|
eventlet.Timeout(0, RuntimeError())
|
||||||
try:
|
try:
|
||||||
result = q.wait()
|
result = q.wait()
|
||||||
evt.send(result)
|
evt.send(result)
|
||||||
@@ -120,7 +120,7 @@ class TestQueue(LimitedTestCase):
|
|||||||
def waiter(q, evt):
|
def waiter(q, evt):
|
||||||
evt.send(q.wait())
|
evt.send(q.wait())
|
||||||
def do_receive(q, evt):
|
def do_receive(q, evt):
|
||||||
api.exc_after(0, RuntimeError())
|
eventlet.Timeout(0, RuntimeError())
|
||||||
try:
|
try:
|
||||||
result = q.wait()
|
result = q.wait()
|
||||||
evt.send(result)
|
evt.send(result)
|
||||||
@@ -139,7 +139,7 @@ class TestQueue(LimitedTestCase):
|
|||||||
|
|
||||||
def test_two_bogus_waiters(self):
|
def test_two_bogus_waiters(self):
|
||||||
def do_receive(q, evt):
|
def do_receive(q, evt):
|
||||||
api.exc_after(0, RuntimeError())
|
eventlet.Timeout(0, RuntimeError())
|
||||||
try:
|
try:
|
||||||
result = q.wait()
|
result = q.wait()
|
||||||
evt.send(result)
|
evt.send(result)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from eventlet.coros import Event
|
from eventlet.event import Event
|
||||||
from eventlet.api import spawn, sleep, exc_after, with_timeout
|
from eventlet.api import spawn, sleep, with_timeout
|
||||||
|
import eventlet
|
||||||
from tests import LimitedTestCase
|
from tests import LimitedTestCase
|
||||||
|
|
||||||
DELAY= 0.01
|
DELAY= 0.01
|
||||||
@@ -30,7 +31,7 @@ class TestEvent(LimitedTestCase):
|
|||||||
event2 = Event()
|
event2 = Event()
|
||||||
|
|
||||||
spawn(event1.send, 'hello event1')
|
spawn(event1.send, 'hello event1')
|
||||||
exc_after(0, ValueError('interrupted'))
|
eventlet.Timeout(0, ValueError('interrupted'))
|
||||||
try:
|
try:
|
||||||
result = event1.wait()
|
result = event1.wait()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from eventlet import pool, coros, api, hubs
|
from eventlet import pool, coros, api, hubs, timeout
|
||||||
from tests import LimitedTestCase
|
from tests import LimitedTestCase
|
||||||
from unittest import main
|
from unittest import main
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ class TestCoroutinePool(LimitedTestCase):
|
|||||||
api.sleep(0)
|
api.sleep(0)
|
||||||
self.assertEqual(pool.free(), 1)
|
self.assertEqual(pool.free(), 1)
|
||||||
# shouldn't block when trying to get
|
# shouldn't block when trying to get
|
||||||
t = api.exc_after(0.1, api.TimeoutError)
|
t = timeout.Timeout(0.1)
|
||||||
try:
|
try:
|
||||||
pool.execute(api.sleep, 1)
|
pool.execute(api.sleep, 1)
|
||||||
finally:
|
finally:
|
||||||
|
54
tests/timeout_test.py
Normal file
54
tests/timeout_test.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from tests import LimitedTestCase
|
||||||
|
from eventlet import timeout
|
||||||
|
from eventlet import greenthread
|
||||||
|
DELAY = 0.01
|
||||||
|
|
||||||
|
class TestDirectRaise(LimitedTestCase):
|
||||||
|
def test_direct_raise_class(self):
|
||||||
|
try:
|
||||||
|
raise timeout.Timeout
|
||||||
|
except timeout.Timeout, t:
|
||||||
|
assert not t.pending, repr(t)
|
||||||
|
|
||||||
|
def test_direct_raise_instance(self):
|
||||||
|
tm = timeout.Timeout()
|
||||||
|
try:
|
||||||
|
raise tm
|
||||||
|
except timeout.Timeout, t:
|
||||||
|
assert tm is t, (tm, t)
|
||||||
|
assert not t.pending, repr(t)
|
||||||
|
|
||||||
|
def test_repr(self):
|
||||||
|
# just verify these don't crash
|
||||||
|
tm = timeout.Timeout(1)
|
||||||
|
greenthread.sleep(0)
|
||||||
|
repr(tm)
|
||||||
|
str(tm)
|
||||||
|
tm.cancel()
|
||||||
|
tm = timeout.Timeout(None, RuntimeError)
|
||||||
|
repr(tm)
|
||||||
|
str(tm)
|
||||||
|
tm = timeout.Timeout(None, False)
|
||||||
|
repr(tm)
|
||||||
|
str(tm)
|
||||||
|
|
||||||
|
class TestWithTimeout(LimitedTestCase):
|
||||||
|
def test_with_timeout(self):
|
||||||
|
self.assertRaises(timeout.Timeout, timeout.with_timeout, DELAY, greenthread.sleep, DELAY*10)
|
||||||
|
X = object()
|
||||||
|
r = timeout.with_timeout(DELAY, greenthread.sleep, DELAY*10, timeout_value=X)
|
||||||
|
self.assert_(r is X, (r, X))
|
||||||
|
r = timeout.with_timeout(DELAY*10, greenthread.sleep,
|
||||||
|
DELAY, timeout_value=X)
|
||||||
|
self.assert_(r is None, r)
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_outer_timer(self):
|
||||||
|
def longer_timeout():
|
||||||
|
# this should not catch the outer timeout's exception
|
||||||
|
return timeout.with_timeout(DELAY * 10,
|
||||||
|
greenthread.sleep, DELAY * 20,
|
||||||
|
timeout_value='b')
|
||||||
|
self.assertRaises(timeout.Timeout,
|
||||||
|
timeout.with_timeout, DELAY, longer_timeout)
|
||||||
|
|
116
tests/timeout_test_with_statement.py
Normal file
116
tests/timeout_test_with_statement.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
""" Tests with-statement behavior of Timeout class. Don't import when
|
||||||
|
using Python 2.4. """
|
||||||
|
|
||||||
|
from __future__ import with_statement
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
import weakref
|
||||||
|
import time
|
||||||
|
from eventlet import sleep
|
||||||
|
from eventlet.timeout import Timeout
|
||||||
|
from tests import LimitedTestCase
|
||||||
|
DELAY = 0.01
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Test(LimitedTestCase):
|
||||||
|
def test_api(self):
|
||||||
|
# Nothing happens if with-block finishes before the timeout expires
|
||||||
|
t = Timeout(DELAY*2)
|
||||||
|
sleep(0) # make it pending
|
||||||
|
assert t.pending, repr(t)
|
||||||
|
with t:
|
||||||
|
assert t.pending, repr(t)
|
||||||
|
sleep(DELAY)
|
||||||
|
# check if timer was actually cancelled
|
||||||
|
assert not t.pending, repr(t)
|
||||||
|
sleep(DELAY*2)
|
||||||
|
|
||||||
|
# An exception will be raised if it's not
|
||||||
|
try:
|
||||||
|
with Timeout(DELAY) as t:
|
||||||
|
sleep(DELAY*2)
|
||||||
|
except Timeout, ex:
|
||||||
|
assert ex is t, (ex, t)
|
||||||
|
else:
|
||||||
|
raise AssertionError('must raise Timeout')
|
||||||
|
|
||||||
|
# You can customize the exception raised:
|
||||||
|
try:
|
||||||
|
with Timeout(DELAY, IOError("Operation takes way too long")):
|
||||||
|
sleep(DELAY*2)
|
||||||
|
except IOError, ex:
|
||||||
|
assert str(ex)=="Operation takes way too long", repr(ex)
|
||||||
|
|
||||||
|
# Providing classes instead of values should be possible too:
|
||||||
|
try:
|
||||||
|
with Timeout(DELAY, ValueError):
|
||||||
|
sleep(DELAY*2)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
1/0
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
with Timeout(DELAY, sys.exc_info()[0]):
|
||||||
|
sleep(DELAY*2)
|
||||||
|
raise AssertionError('should not get there')
|
||||||
|
raise AssertionError('should not get there')
|
||||||
|
except ZeroDivisionError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise AssertionError('should not get there')
|
||||||
|
|
||||||
|
# It's possible to cancel the timer inside the block:
|
||||||
|
with Timeout(DELAY) as timer:
|
||||||
|
timer.cancel()
|
||||||
|
sleep(DELAY*2)
|
||||||
|
|
||||||
|
# To silent the exception before exiting the block, pass False as second parameter.
|
||||||
|
XDELAY=0.1
|
||||||
|
start = time.time()
|
||||||
|
with Timeout(XDELAY, False):
|
||||||
|
sleep(XDELAY*2)
|
||||||
|
delta = (time.time()-start)
|
||||||
|
assert delta<XDELAY*2, delta
|
||||||
|
|
||||||
|
# passing None as seconds disables the timer
|
||||||
|
with Timeout(None):
|
||||||
|
sleep(DELAY)
|
||||||
|
sleep(DELAY)
|
||||||
|
|
||||||
|
def test_ref(self):
|
||||||
|
err = Error()
|
||||||
|
err_ref = weakref.ref(err)
|
||||||
|
with Timeout(DELAY*2, err):
|
||||||
|
sleep(DELAY)
|
||||||
|
del err
|
||||||
|
assert not err_ref(), repr(err_ref())
|
||||||
|
|
||||||
|
def test_nested_timeout(self):
|
||||||
|
with Timeout(DELAY, False):
|
||||||
|
with Timeout(DELAY*2, False):
|
||||||
|
sleep(DELAY*3)
|
||||||
|
raise AssertionError('should not get there')
|
||||||
|
|
||||||
|
with Timeout(DELAY) as t1:
|
||||||
|
with Timeout(DELAY*2) as t2:
|
||||||
|
try:
|
||||||
|
sleep(DELAY*3)
|
||||||
|
except Timeout, ex:
|
||||||
|
assert ex is t1, (ex, t1)
|
||||||
|
assert not t1.pending, t1
|
||||||
|
assert t2.pending, t2
|
||||||
|
assert not t2.pending, t2
|
||||||
|
|
||||||
|
with Timeout(DELAY*2) as t1:
|
||||||
|
with Timeout(DELAY) as t2:
|
||||||
|
try:
|
||||||
|
sleep(DELAY*3)
|
||||||
|
except Timeout, ex:
|
||||||
|
assert ex is t2, (ex, t2)
|
||||||
|
assert t1.pending, t1
|
||||||
|
assert not t2.pending, t2
|
||||||
|
assert not t1.pending, t1
|
Reference in New Issue
Block a user