]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
concurrent: Delete legacy Future implementation
authorBen Darnell <ben@bendarnell.com>
Fri, 6 Jul 2018 21:11:42 +0000 (17:11 -0400)
committerBen Darnell <ben@bendarnell.com>
Sat, 14 Jul 2018 20:58:48 +0000 (16:58 -0400)
tornado/concurrent.py

index 78b20919b90d47e9b28ba6babbb711ea6f4411ae..55f69c23842d692bcb8a7af0414bfd1d7ce04321 100644 (file)
@@ -29,15 +29,11 @@ directly.
 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
@@ -55,320 +51,11 @@ except ImportError:
     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, ...]]