diff options
Diffstat (limited to 'python/gevent/timeout.py')
-rw-r--r-- | python/gevent/timeout.py | 373 |
1 files changed, 0 insertions, 373 deletions
diff --git a/python/gevent/timeout.py b/python/gevent/timeout.py deleted file mode 100644 index 1cc05e4..0000000 --- a/python/gevent/timeout.py +++ /dev/null @@ -1,373 +0,0 @@ -# Copyright (c) 2009-2010 Denis Bilenko. See LICENSE for details. -""" -Timeouts. - -Many functions in :mod:`gevent` have a *timeout* argument that allows -limiting the time the function will block. When that is not available, -the :class:`Timeout` class and :func:`with_timeout` function in this -module add timeouts to arbitrary code. - -.. warning:: - - Timeouts can only work when the greenlet switches to the hub. - If a blocking function is called or an intense calculation is ongoing during - which no switches occur, :class:`Timeout` is powerless. -""" -from __future__ import absolute_import, print_function, division - -from gevent._compat import string_types -from gevent._util import _NONE - -from greenlet import getcurrent -from gevent._hub_local import get_hub_noargs as get_hub - -__all__ = [ - 'Timeout', - 'with_timeout', -] - - -class _FakeTimer(object): - # An object that mimics the API of get_hub().loop.timer, but - # without allocating any native resources. This is useful for timeouts - # that will never expire. - # Also partially mimics the API of Timeout itself for use in _start_new_or_dummy - - # This object is used as a singleton, so it should be - # immutable. - __slots__ = () - - @property - def pending(self): - return False - - active = pending - - @property - def seconds(self): - return None - - timer = exception = seconds - - def start(self, *args, **kwargs): - # pylint:disable=unused-argument - raise AssertionError("non-expiring timer cannot be started") - - def stop(self): - return - - cancel = stop - - stop = close = cancel - - def __enter__(self): - return self - - def __exit__(self, _t, _v, _tb): - return - -_FakeTimer = _FakeTimer() - - -class Timeout(BaseException): - """ - Timeout(seconds=None, exception=None, ref=True, priority=-1) - - Raise *exception* in the current greenlet after *seconds* - have elapsed:: - - timeout = Timeout(seconds, exception) - timeout.start() - try: - ... # exception will be raised here, after *seconds* passed since start() call - finally: - timeout.close() - - .. note:: - - If the code that the timeout was protecting finishes - executing before the timeout elapses, be sure to ``close`` the - timeout so it is not unexpectedly raised in the future. Even if it - is raised, it is a best practice to close it. This ``try/finally`` - construct or a ``with`` statement is a recommended pattern. (If - the timeout object will be started again, use ``cancel`` instead - of ``close``; this is rare.) - - When *exception* is omitted or ``None``, the ``Timeout`` instance - itself is raised:: - - >>> import gevent - >>> gevent.Timeout(0.1).start() - >>> gevent.sleep(0.2) #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - Timeout: 0.1 seconds - - If the *seconds* argument is not given or is ``None`` (e.g., - ``Timeout()``), then the timeout will never expire and never raise - *exception*. This is convenient for creating functions which take - an optional timeout parameter of their own. (Note that this is **not** - the same thing as a *seconds* value of ``0``.) - - :: - - def function(args, timeout=None): - "A function with an optional timeout." - timer = Timeout(timeout) - with timer: - ... - - .. caution:: - - A *seconds* value less than ``0.0`` (e.g., ``-1``) is poorly defined. In the future, - support for negative values is likely to do the same thing as a value - of ``None`` or ``0`` - - A *seconds* value of ``0`` requests that the event loop spin and poll for I/O; - it will immediately expire as soon as control returns to the event loop. - - .. rubric:: Use As A Context Manager - - To simplify starting and canceling timeouts, the ``with`` - statement can be used:: - - with gevent.Timeout(seconds, exception) as timeout: - pass # ... code block ... - - This is equivalent to the try/finally block above with one - additional feature: if *exception* is the literal ``False``, the - timeout is still raised, but the context manager suppresses it, so - the code outside the with-block won't see it. - - This is handy for adding a timeout to the functions that don't - support a *timeout* parameter themselves:: - - data = None - with gevent.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 - - .. caution:: - - If ``readline()`` above catches and doesn't re-raise - :exc:`BaseException` (for example, with a bare ``except:``), then - your timeout will fail to function and control won't be returned - to you when you expect. - - .. rubric:: Catching Timeouts - - When catching timeouts, keep in mind that the one you catch may - not be the one you have set (a calling function may have set its - own timeout); if you going to silence a timeout, always check that - it's the instance you need:: - - timeout = Timeout(1) - timeout.start() - try: - ... - except Timeout as t: - if t is not timeout: - raise # not my timeout - finally: - timeout.close() - - - .. versionchanged:: 1.1b2 - - If *seconds* is not given or is ``None``, no longer allocate a - native timer object that will never be started. - - .. versionchanged:: 1.1 - - Add warning about negative *seconds* values. - - .. versionchanged:: 1.3a1 - - Timeout objects now have a :meth:`close` - method that must be called when the timeout will no longer be - used to properly clean up native resources. - The ``with`` statement does this automatically. - - """ - - # We inherit a __dict__ from BaseException, so __slots__ actually - # makes us larger. - - def __init__(self, seconds=None, exception=None, ref=True, priority=-1, - _one_shot=False): - BaseException.__init__(self) - self.seconds = seconds - self.exception = exception - self._one_shot = _one_shot - if seconds is None: - # Avoid going through the timer codepath if no timeout is - # desired; this avoids some CFFI interactions on PyPy that can lead to a - # RuntimeError if this implementation is used during an `import` statement. See - # https://bitbucket.org/pypy/pypy/issues/2089/crash-in-pypy-260-linux64-with-gevent-11b1 - # and https://github.com/gevent/gevent/issues/618. - # Plus, in general, it should be more efficient - - self.timer = _FakeTimer - else: - # XXX: A timer <= 0 could cause libuv to block the loop; we catch - # that case in libuv/loop.py - self.timer = get_hub().loop.timer(seconds or 0.0, ref=ref, priority=priority) - - def start(self): - """Schedule the timeout.""" - if self.pending: - raise AssertionError('%r is already started; to restart it, cancel it first' % self) - - if self.seconds is None: - # "fake" timeout (never expires) - return - - if self.exception is None or self.exception is False or isinstance(self.exception, string_types): - # timeout that raises self - throws = self - else: - # regular timeout with user-provided exception - throws = self.exception - - # Make sure the timer updates the current time so that we don't - # expire prematurely. - self.timer.start(getcurrent().throw, throws, update=True) - - @classmethod - def start_new(cls, timeout=None, exception=None, ref=True, _one_shot=False): - """Create a started :class:`Timeout`. - - This is a shortcut, the exact action depends on *timeout*'s type: - - * If *timeout* is a :class:`Timeout`, then call its :meth:`start` method - if it's not already begun. - * Otherwise, create a new :class:`Timeout` instance, passing (*timeout*, *exception*) as - arguments, then call its :meth:`start` method. - - Returns the :class:`Timeout` instance. - """ - if isinstance(timeout, Timeout): - if not timeout.pending: - timeout.start() - return timeout - timeout = cls(timeout, exception, ref=ref, _one_shot=_one_shot) - timeout.start() - return timeout - - @staticmethod - def _start_new_or_dummy(timeout, exception=None, ref=True): - # Internal use only in 1.1 - # Return an object with a 'cancel' method; if timeout is None, - # this will be a shared instance object that does nothing. Otherwise, - # return an actual Timeout. Because negative values are hard to reason about, - # and are often used as sentinels in Python APIs, in the future it's likely - # that a negative timeout will also return the shared instance. - # This saves the previously common idiom of 'timer = Timeout.start_new(t) if t is not None else None' - # followed by 'if timer is not None: timer.cancel()'. - # That idiom was used to avoid any object allocations. - # A staticmethod is slightly faster under CPython, compared to a classmethod; - # under PyPy in synthetic benchmarks it makes no difference. - if timeout is None: - return _FakeTimer - return Timeout.start_new(timeout, exception, ref, _one_shot=True) - - @property - def pending(self): - """True if the timeout is scheduled to be raised.""" - return self.timer.pending or self.timer.active - - def cancel(self): - """ - If the timeout is pending, cancel it. Otherwise, do nothing. - - The timeout object can be :meth:`started <start>` again. If - you will not start the timeout again, you should use - :meth:`close` instead. - """ - self.timer.stop() - if self._one_shot: - self.close() - - def close(self): - """ - Close the timeout and free resources. The timer cannot be started again - after this method has been used. - """ - self.timer.stop() - self.timer.close() - self.timer = _FakeTimer - - def __repr__(self): - classname = type(self).__name__ - 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 #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - Timeout - """ - if self.seconds is None: - return '' - - suffix = '' if self.seconds == 1 else 's' - - if self.exception is None: - return '%s second%s' % (self.seconds, suffix) - if self.exception is False: - return '%s second%s (silent)' % (self.seconds, suffix) - return '%s second%s: %s' % (self.seconds, suffix, self.exception) - - def __enter__(self): - """ - Start and return the timer. If the timer is already started, just return it. - """ - if not self.pending: - self.start() - return self - - def __exit__(self, typ, value, tb): - """ - Stop the timer. - - .. versionchanged:: 1.3a1 - The underlying native timer is also stopped. This object cannot be - used again. - """ - self.close() - if value is self and self.exception is False: - return True # Suppress the exception - - -def with_timeout(seconds, function, *args, **kwds): - """Wrap a call to *function* with a timeout; if the called - function fails to return before the timeout, cancel it and return a - flag value, provided by *timeout_value* keyword argument. - - If timeout expires but *timeout_value* is not provided, raise :class:`Timeout`. - - Keyword argument *timeout_value* is not passed to *function*. - """ - timeout_value = kwds.pop("timeout_value", _NONE) - timeout = Timeout.start_new(seconds, _one_shot=True) - try: - try: - return function(*args, **kwds) - except Timeout as ex: - if ex is timeout and timeout_value is not _NONE: - return timeout_value - raise - finally: - timeout.cancel() |