Improved timeout docs.

This commit is contained in:
Ryan Williams
2010-02-19 11:02:22 -05:00
parent f40449af12
commit 11a0b8bc4f
4 changed files with 103 additions and 86 deletions

View File

@@ -105,90 +105,9 @@ Though Eventlet has many modules, much of the most-used stuff is accessible simp
.. 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.
This class is a way to add timeouts to anything. It raises *exception* in the current greenthread after *timeout* seconds. When *exception* is omitted or ``None``, the Timeout instance itself is raised.
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.
Timeout objects are context managers, and so can be used in with statements.
See :class:`Timeout <eventlet.timeout.Timeout>` for more details.
These are the basic primitives of Eventlet; there are a lot more out there in the other Eventlet modules; check out the :doc:`modules`.

View File

@@ -14,4 +14,5 @@ Module Reference
modules/pools
modules/queue
modules/semaphore
modules/timeout
modules/wsgi

93
doc/modules/timeout.rst Normal file
View File

@@ -0,0 +1,93 @@
:mod:`timeout` -- Universal Timeouts
========================================
.. class:: eventlet.timeout.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
.. automethod:: cancel
.. autoattribute:: pending
.. function:: eventlet.timeout.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.

View File

@@ -51,14 +51,18 @@ class Timeout(BaseException):
@property
def pending(self):
"""Return True if the timeout is scheduled to be raised."""
"""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 the timeout is pending, cancel it. If not using Timeouts in
``with`` statements, always call cancel() in a ``finally`` after the
block of code that is getting timed out. If not cancelled, the timeout
will be raised later on, in some unexpected section of the
application."""
if self.timer is not None:
self.timer.cancel()
self.timer = None