from __future__ import absolute_import, division, print_function
import functools
-import platform
-import textwrap
-import traceback
import sys
import warnings
-from tornado.log import app_log
from tornado.stack_context import ExceptionStackContext, wrap
-from tornado.util import raise_exc_info, ArgReplacer, is_finalizing
+from tornado.util import ArgReplacer
try:
from concurrent import futures
typing = None
-# Can the garbage collector handle cycles that include __del__ methods?
-# This is true in cpython beginning with version 3.4 (PEP 442).
-_GC_CYCLE_FINALIZERS = (platform.python_implementation() == 'CPython' and
- sys.version_info >= (3, 4))
-
-
class ReturnValueIgnoredError(Exception):
pass
-# This class and associated code in the future object is derived
-# from the Trollius project, a backport of asyncio to Python 2.x - 3.x
-
-
-class _TracebackLogger(object):
- """Helper to log a traceback upon destruction if not cleared.
-
- This solves a nasty problem with Futures and Tasks that have an
- exception set: if nobody asks for the exception, the exception is
- never logged. This violates the Zen of Python: 'Errors should
- never pass silently. Unless explicitly silenced.'
-
- However, we don't want to log the exception as soon as
- set_exception() is called: if the calling code is written
- properly, it will get the exception and handle it properly. But
- we *do* want to log it if result() or exception() was never called
- -- otherwise developers waste a lot of time wondering why their
- buggy code fails silently.
-
- An earlier attempt added a __del__() method to the Future class
- itself, but this backfired because the presence of __del__()
- prevents garbage collection from breaking cycles. A way out of
- this catch-22 is to avoid having a __del__() method on the Future
- class itself, but instead to have a reference to a helper object
- with a __del__() method that logs the traceback, where we ensure
- that the helper object doesn't participate in cycles, and only the
- Future has a reference to it.
-
- The helper object is added when set_exception() is called. When
- the Future is collected, and the helper is present, the helper
- object is also collected, and its __del__() method will log the
- traceback. When the Future's result() or exception() method is
- called (and a helper object is present), it removes the the helper
- object, after calling its clear() method to prevent it from
- logging.
-
- One downside is that we do a fair amount of work to extract the
- traceback from the exception, even when it is never logged. It
- would seem cheaper to just store the exception object, but that
- references the traceback, which references stack frames, which may
- reference the Future, which references the _TracebackLogger, and
- then the _TracebackLogger would be included in a cycle, which is
- what we're trying to avoid! As an optimization, we don't
- immediately format the exception; we only do the work when
- activate() is called, which call is delayed until after all the
- Future's callbacks have run. Since usually a Future has at least
- one callback (typically set by 'yield From') and usually that
- callback extracts the callback, thereby removing the need to
- format the exception.
-
- PS. I don't claim credit for this solution. I first heard of it
- in a discussion about closing files when they are collected.
- """
-
- __slots__ = ('exc_info', 'formatted_tb')
-
- def __init__(self, exc_info):
- self.exc_info = exc_info
- self.formatted_tb = None
-
- def activate(self):
- exc_info = self.exc_info
- if exc_info is not None:
- self.exc_info = None
- self.formatted_tb = traceback.format_exception(*exc_info)
-
- def clear(self):
- self.exc_info = None
- self.formatted_tb = None
-
- def __del__(self, is_finalizing=is_finalizing):
- if not is_finalizing() and self.formatted_tb:
- app_log.error('Future exception was never retrieved: %s',
- ''.join(self.formatted_tb).rstrip())
-
-
-class Future(object):
- """Placeholder for an asynchronous result.
-
- A ``Future`` encapsulates the result of an asynchronous
- operation. In synchronous applications ``Futures`` are used
- to wait for the result from a thread or process pool; in
- Tornado they are normally used with `.IOLoop.add_future` or by
- yielding them in a `.gen.coroutine`.
-
- `tornado.concurrent.Future` is an alias for `asyncio.Future` when
- that package is available (Python 3.4+). Unlike
- `concurrent.futures.Future`, the ``Futures`` used by Tornado and
- `asyncio` are not thread-safe (and therefore faster for use with
- single-threaded event loops).
-
- In addition to ``exception`` and ``set_exception``, Tornado's
- ``Future`` implementation supports storing an ``exc_info`` triple
- to support better tracebacks on Python 2. To set an ``exc_info``
- triple, use `future_set_exc_info`, and to retrieve one, call
- `result()` (which will raise it).
-
- .. versionchanged:: 4.0
- `tornado.concurrent.Future` is always a thread-unsafe ``Future``
- with support for the ``exc_info`` methods. Previously it would
- be an alias for the thread-safe `concurrent.futures.Future`
- if that package was available and fall back to the thread-unsafe
- implementation if it was not.
-
- .. versionchanged:: 4.1
- If a `.Future` contains an error but that error is never observed
- (by calling ``result()``, ``exception()``, or ``exc_info()``),
- a stack trace will be logged when the `.Future` is garbage collected.
- This normally indicates an error in the application, but in cases
- where it results in undesired logging it may be necessary to
- suppress the logging by ensuring that the exception is observed:
- ``f.add_done_callback(lambda f: f.exception())``.
- .. versionchanged:: 5.0
-
- This class was previoiusly available under the name
- ``TracebackFuture``. This name, which was deprecated since
- version 4.0, has been removed. When `asyncio` is available
- ``tornado.concurrent.Future`` is now an alias for
- `asyncio.Future`. Like `asyncio.Future`, callbacks are now
- always scheduled on the `.IOLoop` and are never run
- synchronously.
-
- """
- def __init__(self):
- self._done = False
- self._result = None
- self._exc_info = None
-
- self._log_traceback = False # Used for Python >= 3.4
- self._tb_logger = None # Used for Python <= 3.3
-
- self._callbacks = []
-
- # Implement the Python 3.5 Awaitable protocol if possible
- # (we can't use return and yield together until py33).
- if sys.version_info >= (3, 3):
- exec(textwrap.dedent("""
- def __await__(self):
- return (yield self)
- """))
- else:
- # Py2-compatible version for use with cython.
- def __await__(self):
- result = yield self
- # StopIteration doesn't take args before py33,
- # but Cython recognizes the args tuple.
- e = StopIteration()
- e.args = (result,)
- raise e
-
- def cancel(self):
- """Cancel the operation, if possible.
-
- Tornado ``Futures`` do not support cancellation, so this method always
- returns False.
- """
- return False
-
- def cancelled(self):
- """Returns True if the operation has been cancelled.
-
- Tornado ``Futures`` do not support cancellation, so this method
- always returns False.
- """
- return False
-
- def running(self):
- """Returns True if this operation is currently running."""
- return not self._done
-
- def done(self):
- """Returns True if the future has finished running."""
- return self._done
-
- def _clear_tb_log(self):
- self._log_traceback = False
- if self._tb_logger is not None:
- self._tb_logger.clear()
- self._tb_logger = None
-
- def result(self, timeout=None):
- """If the operation succeeded, return its result. If it failed,
- re-raise its exception.
-
- This method takes a ``timeout`` argument for compatibility with
- `concurrent.futures.Future` but it is an error to call it
- before the `Future` is done, so the ``timeout`` is never used.
- """
- self._clear_tb_log()
- if self._result is not None:
- return self._result
- if self._exc_info is not None:
- try:
- raise_exc_info(self._exc_info)
- finally:
- self = None
- self._check_done()
- return self._result
-
- def exception(self, timeout=None):
- """If the operation raised an exception, return the `Exception`
- object. Otherwise returns None.
-
- This method takes a ``timeout`` argument for compatibility with
- `concurrent.futures.Future` but it is an error to call it
- before the `Future` is done, so the ``timeout`` is never used.
- """
- self._clear_tb_log()
- if self._exc_info is not None:
- return self._exc_info[1]
- else:
- self._check_done()
- return None
-
- def add_done_callback(self, fn):
- """Attaches the given callback to the `Future`.
-
- It will be invoked with the `Future` as its argument when the Future
- has finished running and its result is available. In Tornado
- consider using `.IOLoop.add_future` instead of calling
- `add_done_callback` directly.
- """
- if self._done:
- from tornado.ioloop import IOLoop
- IOLoop.current().add_callback(fn, self)
- else:
- self._callbacks.append(fn)
-
- def set_result(self, result):
- """Sets the result of a ``Future``.
-
- It is undefined to call any of the ``set`` methods more than once
- on the same object.
- """
- self._result = result
- self._set_done()
-
- def set_exception(self, exception):
- """Sets the exception of a ``Future.``"""
- self.set_exc_info(
- (exception.__class__,
- exception,
- getattr(exception, '__traceback__', None)))
-
- def exc_info(self):
- """Returns a tuple in the same format as `sys.exc_info` or None.
-
- .. versionadded:: 4.0
- """
- self._clear_tb_log()
- return self._exc_info
-
- def set_exc_info(self, exc_info):
- """Sets the exception information of a ``Future.``
-
- Preserves tracebacks on Python 2.
-
- .. versionadded:: 4.0
- """
- self._exc_info = exc_info
- self._log_traceback = True
- if not _GC_CYCLE_FINALIZERS:
- self._tb_logger = _TracebackLogger(exc_info)
-
- try:
- self._set_done()
- finally:
- # Activate the logger after all callbacks have had a
- # chance to call result() or exception().
- if self._log_traceback and self._tb_logger is not None:
- self._tb_logger.activate()
- self._exc_info = exc_info
-
- def _check_done(self):
- if not self._done:
- raise Exception("DummyFuture does not support blocking for results")
-
- def _set_done(self):
- self._done = True
- if self._callbacks:
- from tornado.ioloop import IOLoop
- loop = IOLoop.current()
- for cb in self._callbacks:
- loop.add_callback(cb, self)
- self._callbacks = None
-
- # On Python 3.3 or older, objects with a destructor part of a reference
- # cycle are never destroyed. It's no longer the case on Python 3.4 thanks to
- # the PEP 442.
- if _GC_CYCLE_FINALIZERS:
- def __del__(self, is_finalizing=is_finalizing):
- if is_finalizing() or not self._log_traceback:
- # set_exception() was not called, or result() or exception()
- # has consumed the exception
- return
-
- tb = traceback.format_exception(*self._exc_info)
-
- app_log.error('Future %r exception was never retrieved: %s',
- self, ''.join(tb).rstrip())
-
-
-if asyncio is not None:
- Future = asyncio.Future # noqa
+Future = asyncio.Future # noqa
if futures is None:
FUTURES = Future # type: typing.Union[type, typing.Tuple[type, ...]]