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.
|
||||
|
||||
.. 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`.
|
||||
|
@@ -5,21 +5,25 @@ try:
|
||||
from eventlet import greenthread
|
||||
from eventlet import greenpool
|
||||
from eventlet import queue
|
||||
from eventlet import timeout
|
||||
|
||||
sleep = greenthread.sleep
|
||||
|
||||
spawn = greenthread.spawn
|
||||
spawn_n = greenthread.spawn_n
|
||||
spawn_after = greenthread.spawn_after
|
||||
call_after_global = greenthread.call_after_global
|
||||
TimeoutError = greenthread.TimeoutError
|
||||
exc_after = greenthread.exc_after
|
||||
with_timeout = greenthread.with_timeout
|
||||
|
||||
Timeout = timeout.Timeout
|
||||
with_timeout = timeout.with_timeout
|
||||
|
||||
GreenPool = greenpool.GreenPool
|
||||
GreenPile = greenpool.GreenPile
|
||||
|
||||
Queue = queue.Queue
|
||||
|
||||
# deprecated
|
||||
TimeoutError = timeout.Timeout
|
||||
exc_after = greenthread.exc_after
|
||||
call_after_global = greenthread.call_after_global
|
||||
except ImportError:
|
||||
# this is to make Debian packaging easier
|
||||
import traceback
|
||||
|
@@ -4,7 +4,8 @@ import time
|
||||
|
||||
from eventlet.pools import Pool
|
||||
from eventlet.processes import DeadProcess
|
||||
from eventlet import api
|
||||
from eventlet import timeout
|
||||
from eventlet import greenthread
|
||||
|
||||
|
||||
class ConnectTimeout(Exception):
|
||||
@@ -89,7 +90,7 @@ class BaseConnectionPool(Pool):
|
||||
|
||||
if next_delay > 0:
|
||||
# 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)
|
||||
|
||||
def _expire_old_connections(self, now):
|
||||
@@ -248,7 +249,7 @@ class TpooledConnectionPool(BaseConnectionPool):
|
||||
|
||||
@classmethod
|
||||
def connect(cls, db_module, connect_timeout, *args, **kw):
|
||||
timeout = api.exc_after(connect_timeout, ConnectTimeout())
|
||||
timeout = timeout.Timeout(connect_timeout, ConnectTimeout())
|
||||
try:
|
||||
from eventlet import tpool
|
||||
conn = tpool.execute(db_module.connect, *args, **kw)
|
||||
@@ -268,7 +269,7 @@ class RawConnectionPool(BaseConnectionPool):
|
||||
|
||||
@classmethod
|
||||
def connect(cls, db_module, connect_timeout, *args, **kw):
|
||||
timeout = api.exc_after(connect_timeout, ConnectTimeout())
|
||||
timeout = timeout.Timeout(connect_timeout, ConnectTimeout())
|
||||
try:
|
||||
return db_module.connect(*args, **kw)
|
||||
finally:
|
||||
|
@@ -2,7 +2,7 @@ from eventlet import patcher
|
||||
from eventlet.green import thread
|
||||
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',
|
||||
globals(),
|
||||
|
@@ -2,6 +2,7 @@ import sys
|
||||
|
||||
from eventlet import event
|
||||
from eventlet import hubs
|
||||
from eventlet import timeout
|
||||
from eventlet.hubs import timer
|
||||
from eventlet.support import greenlets as greenlet
|
||||
import warnings
|
||||
@@ -9,7 +10,6 @@ import warnings
|
||||
__all__ = ['getcurrent', 'sleep', 'spawn', 'spawn_n', 'call_after_global', 'call_after_local', 'GreenThread']
|
||||
|
||||
getcurrent = greenlet.getcurrent
|
||||
TimeoutError = hubs.TimeoutError
|
||||
|
||||
def sleep(seconds=0):
|
||||
"""Yield control to another eligible coroutine until at least *seconds* have
|
||||
@@ -170,57 +170,17 @@ def exc_after(seconds, *throw_args):
|
||||
else:
|
||||
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
|
||||
return timer.Timer(seconds, lambda: None)
|
||||
hub = hubs.get_hub()
|
||||
return hub.schedule_call_local(seconds, getcurrent().throw, *throw_args)
|
||||
|
||||
|
||||
def with_timeout(seconds, func, *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; 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()
|
||||
# deprecate, remove
|
||||
TimeoutError = timeout.Timeout
|
||||
with_timeout = timeout.with_timeout
|
||||
|
||||
def _spawn_n(seconds, func, args, kwargs):
|
||||
hub = hubs.get_hub()
|
||||
|
@@ -83,12 +83,9 @@ def get_hub():
|
||||
hub = _threadlocal.hub = _threadlocal.Hub()
|
||||
return hub
|
||||
|
||||
class TimeoutError(Exception):
|
||||
"""Exception raised if an asynchronous operation times out"""
|
||||
pass
|
||||
|
||||
from eventlet import timeout
|
||||
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
|
||||
descriptor is ready to *read*, ready to *write*, or the specified
|
||||
*timeout* elapses, depending on arguments specified.
|
||||
|
@@ -29,7 +29,10 @@ class event_wrapper(object):
|
||||
if self.impl is not None:
|
||||
self.impl.delete()
|
||||
self.impl = None
|
||||
|
||||
|
||||
@property
|
||||
def pending(self):
|
||||
return bool(self.impl and self.impl.pending())
|
||||
|
||||
class Hub(BaseHub):
|
||||
|
||||
|
@@ -29,6 +29,10 @@ class Timer(object):
|
||||
@property
|
||||
def cancelled(self):
|
||||
return self._cancelled
|
||||
|
||||
@property
|
||||
def pending(self):
|
||||
return not (self._cancelled or self.called)
|
||||
|
||||
def __repr__(self):
|
||||
secs = getattr(self, 'seconds', None)
|
||||
|
@@ -28,8 +28,9 @@ from Queue import Full, Empty
|
||||
|
||||
_NONE = object()
|
||||
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.timeout import Timeout
|
||||
|
||||
__all__ = ['Queue', 'PriorityQueue', 'LifoQueue', 'LightQueue', 'Full', 'Empty']
|
||||
|
||||
@@ -211,7 +212,7 @@ class LightQueue(object):
|
||||
elif block:
|
||||
waiter = ItemWaiter(item)
|
||||
self.putters.add(waiter)
|
||||
timeout = exc_after(timeout, Full)
|
||||
timeout = Timeout(timeout, Full)
|
||||
try:
|
||||
if self.getters:
|
||||
self._schedule_unlock()
|
||||
@@ -259,7 +260,7 @@ class LightQueue(object):
|
||||
raise Empty
|
||||
elif block:
|
||||
waiter = Waiter()
|
||||
timeout = exc_after(timeout, Empty)
|
||||
timeout = Timeout(timeout, Empty)
|
||||
try:
|
||||
self.getters.add(waiter)
|
||||
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
|
||||
def setUp(self):
|
||||
from eventlet import api
|
||||
self.timer = api.exc_after(self.TEST_TIMEOUT,
|
||||
TestIsTakingTooLong(self.TEST_TIMEOUT))
|
||||
import eventlet
|
||||
self.timer = eventlet.Timeout(self.TEST_TIMEOUT,
|
||||
TestIsTakingTooLong(self.TEST_TIMEOUT))
|
||||
|
||||
def tearDown(self):
|
||||
self.timer.cancel()
|
||||
|
@@ -2,9 +2,9 @@
|
||||
|
||||
from tests import skipped, skip_unless, skip_with_pyevent
|
||||
from unittest import TestCase, main
|
||||
from eventlet import api
|
||||
from eventlet import event
|
||||
from eventlet import db_pool
|
||||
import eventlet
|
||||
import os
|
||||
|
||||
class DBTester(object):
|
||||
@@ -145,7 +145,7 @@ class DBConnectionPool(DBTester):
|
||||
results.append(2)
|
||||
evt.send()
|
||||
evt2 = event.Event()
|
||||
api.spawn(a_query)
|
||||
eventlet.spawn(a_query)
|
||||
results.append(1)
|
||||
self.assertEqual([1], results)
|
||||
evt.wait()
|
||||
@@ -232,8 +232,8 @@ class DBConnectionPool(DBTester):
|
||||
results.append(2)
|
||||
evt2.send()
|
||||
|
||||
api.spawn(long_running_query)
|
||||
api.spawn(short_running_query)
|
||||
eventlet.spawn(long_running_query)
|
||||
eventlet.spawn(short_running_query)
|
||||
evt.wait()
|
||||
evt2.wait()
|
||||
results.sort()
|
||||
@@ -304,17 +304,17 @@ class DBConnectionPool(DBTester):
|
||||
self.connection = self.pool.get()
|
||||
self.connection.close()
|
||||
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.connection = self.pool.get()
|
||||
self.connection.close()
|
||||
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.connection = self.pool.get()
|
||||
self.connection.close()
|
||||
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)
|
||||
|
||||
@skipped
|
||||
@@ -323,11 +323,11 @@ class DBConnectionPool(DBTester):
|
||||
self.pool = self.create_pool(max_size=2, max_idle=0.02)
|
||||
self.connection, conn2 = self.pool.get(), self.pool.get()
|
||||
self.connection.close()
|
||||
api.sleep(0.01)
|
||||
eventlet.sleep(0.01)
|
||||
self.assertEquals(len(self.pool.free_items), 1)
|
||||
conn2.close()
|
||||
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)
|
||||
|
||||
@skipped
|
||||
@@ -337,12 +337,12 @@ class DBConnectionPool(DBTester):
|
||||
self.connection = self.pool.get()
|
||||
self.connection.close()
|
||||
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.connection = self.pool.get()
|
||||
self.connection.close()
|
||||
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)
|
||||
|
||||
@skipped
|
||||
@@ -352,9 +352,9 @@ class DBConnectionPool(DBTester):
|
||||
self.connection, conn2 = self.pool.get(), self.pool.get()
|
||||
self.connection.close()
|
||||
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)
|
||||
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)
|
||||
conn2.close() # should not be added to the free items
|
||||
self.assertEquals(len(self.pool.free_items), 0)
|
||||
@@ -374,13 +374,13 @@ class DBConnectionPool(DBTester):
|
||||
def retrieve(pool, ev):
|
||||
c = pool.get()
|
||||
ev.send(c)
|
||||
api.spawn(retrieve, self.pool, e)
|
||||
api.sleep(0) # these two sleeps should advance the retrieve
|
||||
api.sleep(0) # coroutine until it's waiting in get()
|
||||
eventlet.spawn(retrieve, self.pool, e)
|
||||
eventlet.sleep(0) # these two sleeps should advance the retrieve
|
||||
eventlet.sleep(0) # coroutine until it's waiting in get()
|
||||
self.assertEquals(self.pool.free(), 0)
|
||||
self.assertEquals(self.pool.waiting(), 1)
|
||||
self.pool.put(self.connection)
|
||||
timer = api.exc_after(1, api.TimeoutError)
|
||||
timer = eventlet.Timeout(1)
|
||||
conn = e.wait()
|
||||
timer.cancel()
|
||||
self.assertEquals(self.pool.free(), 0)
|
||||
|
@@ -64,6 +64,6 @@ class TestEvent(LimitedTestCase):
|
||||
self.assertRaises(RuntimeError, evt.wait)
|
||||
evt.reset()
|
||||
# shouldn't see the RuntimeError again
|
||||
eventlet.exc_after(0.001, eventlet.TimeoutError('from test_double_exception'))
|
||||
self.assertRaises(eventlet.TimeoutError, evt.wait)
|
||||
eventlet.Timeout(0.001)
|
||||
self.assertRaises(eventlet.Timeout, evt.wait)
|
||||
|
||||
|
@@ -246,7 +246,7 @@ class TestGreenIo(LimitedTestCase):
|
||||
try:
|
||||
# try and get some data off of this pipe
|
||||
# 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.connect(('127.0.0.1', bound_port))
|
||||
wrap_rfile = client.makefile()
|
||||
|
@@ -120,7 +120,7 @@ class GreenPool(tests.LimitedTestCase):
|
||||
self.assertEquals(pool.free(), num_free)
|
||||
def wait_long_time(e):
|
||||
e.wait()
|
||||
timer = eventlet.exc_after(1, eventlet.TimeoutError)
|
||||
timer = eventlet.Timeout(1)
|
||||
try:
|
||||
evt = event.Event()
|
||||
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
|
||||
# some unexpected free items
|
||||
timer = eventlet.exc_after(0, RuntimeError())
|
||||
timer = eventlet.Timeout(0, RuntimeError)
|
||||
try:
|
||||
self.assertRaises(RuntimeError, pool.spawn, wait_long_time, evt)
|
||||
finally:
|
||||
@@ -182,7 +182,7 @@ class GreenPool(tests.LimitedTestCase):
|
||||
tp = pools.TokenPool(max_size=1)
|
||||
token = tp.get() # empty out the pool
|
||||
def do_receive(tp):
|
||||
timer = eventlet.exc_after(0, RuntimeError())
|
||||
timer = eventlet.Timeout(0, RuntimeError())
|
||||
try:
|
||||
t = tp.get()
|
||||
self.fail("Shouldn't have recieved anything from the pool")
|
||||
@@ -316,8 +316,8 @@ class GreenPile(tests.LimitedTestCase):
|
||||
for i in xrange(4):
|
||||
p.spawn(passthru, i)
|
||||
# now it should be full and this should time out
|
||||
eventlet.exc_after(0, eventlet.TimeoutError)
|
||||
self.assertRaises(eventlet.TimeoutError, p.spawn, passthru, "time out")
|
||||
eventlet.Timeout(0)
|
||||
self.assertRaises(eventlet.Timeout, p.spawn, passthru, "time out")
|
||||
# verify that the spawn breakage didn't interrupt the sequence
|
||||
# and terminates properly
|
||||
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
|
||||
if sys.version_info < (2,5):
|
||||
argv = sys.argv + ["--exclude=.*test__api_timeout.*"]
|
||||
argv = sys.argv + ["--exclude=.*timeout_test_with_statement.*"]
|
||||
else:
|
||||
argv = sys.argv
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
from unittest import TestCase, main
|
||||
|
||||
import eventlet
|
||||
from eventlet import api
|
||||
from eventlet import coros
|
||||
from eventlet import pools
|
||||
@@ -11,7 +12,6 @@ class IntPool(pools.Pool):
|
||||
|
||||
|
||||
class TestIntPool(TestCase):
|
||||
mode = 'static'
|
||||
def setUp(self):
|
||||
self.pool = IntPool(min_size=0, max_size=4)
|
||||
|
||||
@@ -103,7 +103,7 @@ class TestIntPool(TestCase):
|
||||
self.assertEquals(self.pool.get(), two)
|
||||
|
||||
def test_putting_to_queue(self):
|
||||
timer = api.exc_after(0.1, api.TimeoutError)
|
||||
timer = eventlet.Timeout(0.1)
|
||||
try:
|
||||
size = 2
|
||||
self.pool = IntPool(min_size=0, max_size=size)
|
||||
|
@@ -3,7 +3,7 @@ import eventlet
|
||||
from eventlet import event
|
||||
|
||||
def do_bail(q):
|
||||
eventlet.exc_after(0, RuntimeError())
|
||||
eventlet.Timeout(0, RuntimeError())
|
||||
try:
|
||||
result = q.get()
|
||||
return result
|
||||
|
@@ -27,7 +27,7 @@ def assimilate_patched(name):
|
||||
test_method()
|
||||
restart_hub()
|
||||
globals()[method_name] = test_main
|
||||
modobj.test_main.__name__ = name + '.test_main'
|
||||
test_main.__name__ = name + '.test_main'
|
||||
except AttributeError:
|
||||
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):
|
||||
q = coros.queue()
|
||||
def waiter(q):
|
||||
timer = api.exc_after(0.1, api.TimeoutError)
|
||||
timer = eventlet.Timeout(0.1)
|
||||
self.assertEquals(q.wait(), 'hi2')
|
||||
timer.cancel()
|
||||
|
||||
@@ -92,7 +92,7 @@ class TestQueue(LimitedTestCase):
|
||||
q = coros.queue()
|
||||
|
||||
def do_receive(q, evt):
|
||||
api.exc_after(0, RuntimeError())
|
||||
eventlet.Timeout(0, RuntimeError())
|
||||
try:
|
||||
result = q.wait()
|
||||
evt.send(result)
|
||||
@@ -120,7 +120,7 @@ class TestQueue(LimitedTestCase):
|
||||
def waiter(q, evt):
|
||||
evt.send(q.wait())
|
||||
def do_receive(q, evt):
|
||||
api.exc_after(0, RuntimeError())
|
||||
eventlet.Timeout(0, RuntimeError())
|
||||
try:
|
||||
result = q.wait()
|
||||
evt.send(result)
|
||||
@@ -139,7 +139,7 @@ class TestQueue(LimitedTestCase):
|
||||
|
||||
def test_two_bogus_waiters(self):
|
||||
def do_receive(q, evt):
|
||||
api.exc_after(0, RuntimeError())
|
||||
eventlet.Timeout(0, RuntimeError())
|
||||
try:
|
||||
result = q.wait()
|
||||
evt.send(result)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import unittest
|
||||
from eventlet.coros import Event
|
||||
from eventlet.api import spawn, sleep, exc_after, with_timeout
|
||||
from eventlet.event import Event
|
||||
from eventlet.api import spawn, sleep, with_timeout
|
||||
import eventlet
|
||||
from tests import LimitedTestCase
|
||||
|
||||
DELAY= 0.01
|
||||
@@ -30,7 +31,7 @@ class TestEvent(LimitedTestCase):
|
||||
event2 = Event()
|
||||
|
||||
spawn(event1.send, 'hello event1')
|
||||
exc_after(0, ValueError('interrupted'))
|
||||
eventlet.Timeout(0, ValueError('interrupted'))
|
||||
try:
|
||||
result = event1.wait()
|
||||
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 unittest import main
|
||||
|
||||
@@ -171,7 +171,7 @@ class TestCoroutinePool(LimitedTestCase):
|
||||
api.sleep(0)
|
||||
self.assertEqual(pool.free(), 1)
|
||||
# shouldn't block when trying to get
|
||||
t = api.exc_after(0.1, api.TimeoutError)
|
||||
t = timeout.Timeout(0.1)
|
||||
try:
|
||||
pool.execute(api.sleep, 1)
|
||||
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