diff options
| author | INADA Naoki <methane@users.noreply.github.com> | 2017-04-25 10:57:18 +0900 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-04-25 10:57:18 +0900 | 
| commit | 3e2ad8ec61a322370a6fbdfb2209cf74546f5e08 (patch) | |
| tree | 0285efcc8b8d9b7215e4b8dbe106960187b1e628 /Lib/asyncio/futures.py | |
| parent | f6448e5d65c349576df6e83b8324b9c208e77615 (diff) | |
| download | cpython-git-3e2ad8ec61a322370a6fbdfb2209cf74546f5e08.tar.gz | |
bpo-29617: Remove Python 3.3 support from asyncio (GH-232)
Diffstat (limited to 'Lib/asyncio/futures.py')
| -rw-r--r-- | Lib/asyncio/futures.py | 122 | 
1 files changed, 16 insertions, 106 deletions
| diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index d11d289307..39721eaf00 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -27,86 +27,6 @@ _FINISHED = base_futures._FINISHED  STACK_DEBUG = logging.DEBUG - 1  # heavy-duty debugging -class _TracebackLogger: -    """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 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__ = ('loop', 'source_traceback', 'exc', 'tb') - -    def __init__(self, future, exc): -        self.loop = future._loop -        self.source_traceback = future._source_traceback -        self.exc = exc -        self.tb = None - -    def activate(self): -        exc = self.exc -        if exc is not None: -            self.exc = None -            self.tb = traceback.format_exception(exc.__class__, exc, -                                                 exc.__traceback__) - -    def clear(self): -        self.exc = None -        self.tb = None - -    def __del__(self): -        if self.tb: -            msg = 'Future/Task exception was never retrieved\n' -            if self.source_traceback: -                src = ''.join(traceback.format_list(self.source_traceback)) -                msg += 'Future/Task created at (most recent call last):\n' -                msg += '%s\n' % src.rstrip() -            msg += ''.join(self.tb).rstrip() -            self.loop.call_exception_handler({'message': msg}) - -  class Future:      """This class is *almost* compatible with concurrent.futures.Future. @@ -164,25 +84,21 @@ class Future:      def __repr__(self):          return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info())) -    # On Python 3.3 and older, objects with a destructor part of a reference -    # cycle are never destroyed. It's not more the case on Python 3.4 thanks -    # to the PEP 442. -    if compat.PY34: -        def __del__(self): -            if not self._log_traceback: -                # set_exception() was not called, or result() or exception() -                # has consumed the exception -                return -            exc = self._exception -            context = { -                'message': ('%s exception was never retrieved' -                            % self.__class__.__name__), -                'exception': exc, -                'future': self, -            } -            if self._source_traceback: -                context['source_traceback'] = self._source_traceback -            self._loop.call_exception_handler(context) +    def __del__(self): +        if not self._log_traceback: +            # set_exception() was not called, or result() or exception() +            # has consumed the exception +            return +        exc = self._exception +        context = { +            'message': ('%s exception was never retrieved' +                        % self.__class__.__name__), +            'exception': exc, +            'future': self, +        } +        if self._source_traceback: +            context['source_traceback'] = self._source_traceback +        self._loop.call_exception_handler(context)      def cancel(self):          """Cancel the future and schedule callbacks. @@ -317,13 +233,7 @@ class Future:          self._exception = exception          self._state = _FINISHED          self._schedule_callbacks() -        if compat.PY34: -            self._log_traceback = True -        else: -            self._tb_logger = _TracebackLogger(self, exception) -            # Arrange for the logger to be activated after all callbacks -            # have had a chance to call result() or exception(). -            self._loop.call_soon(self._tb_logger.activate) +        self._log_traceback = True      def __iter__(self):          if not self.done(): | 
