]> granicus.if.org Git - python/commitdiff
bpo-29617: Remove Python 3.3 support from asyncio (GH-232)
authorINADA Naoki <methane@users.noreply.github.com>
Tue, 25 Apr 2017 01:57:18 +0000 (10:57 +0900)
committerGitHub <noreply@github.com>
Tue, 25 Apr 2017 01:57:18 +0000 (10:57 +0900)
12 files changed:
Lib/asyncio/base_events.py
Lib/asyncio/base_subprocess.py
Lib/asyncio/compat.py
Lib/asyncio/events.py
Lib/asyncio/futures.py
Lib/asyncio/proactor_events.py
Lib/asyncio/selector_events.py
Lib/asyncio/sslproto.py
Lib/asyncio/tasks.py
Lib/asyncio/test_utils.py
Lib/asyncio/transports.py
Lib/asyncio/unix_events.py

index 6624ac1d88d701a25d50d87fe1d515d489f7dbe2..3ff511be44a67e324bdb05332f74d044f6d52efd 100644 (file)
@@ -29,7 +29,6 @@ import sys
 import warnings
 import weakref
 
-from . import compat
 from . import coroutines
 from . import events
 from . import futures
@@ -499,16 +498,12 @@ class BaseEventLoop(events.AbstractEventLoop):
         """Returns True if the event loop was closed."""
         return self._closed
 
-    # 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.is_closed():
-                warnings.warn("unclosed event loop %r" % self, ResourceWarning,
-                              source=self)
-                if not self.is_running():
-                    self.close()
+    def __del__(self):
+        if not self.is_closed():
+            warnings.warn("unclosed event loop %r" % self, ResourceWarning,
+                          source=self)
+            if not self.is_running():
+                self.close()
 
     def is_running(self):
         """Returns True if the event loop is running."""
index a00d9d5732f09e49ffb8e2ed2904697933904bc4..cac8d962c0bf94a6312c047a2e81af17c7d75f05 100644 (file)
@@ -2,7 +2,6 @@ import collections
 import subprocess
 import warnings
 
-from . import compat
 from . import protocols
 from . import transports
 from .coroutines import coroutine
@@ -121,15 +120,11 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
 
             # Don't clear the _proc reference yet: _post_init() may still run
 
-    # 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._closed:
-                warnings.warn("unclosed transport %r" % self, ResourceWarning,
-                              source=self)
-                self.close()
+    def __del__(self):
+        if not self._closed:
+            warnings.warn("unclosed transport %r" % self, ResourceWarning,
+                          source=self)
+            self.close()
 
     def get_pid(self):
         return self._pid
index 4790bb4a35f02df055c0c3b5b2631145b3cc6540..520ec6870c8882e1eb81185893962f2227a11aa1 100644 (file)
@@ -2,17 +2,5 @@
 
 import sys
 
-PY34 = sys.version_info >= (3, 4)
 PY35 = sys.version_info >= (3, 5)
 PY352 = sys.version_info >= (3, 5, 2)
-
-
-def flatten_list_bytes(list_of_data):
-    """Concatenate a sequence of bytes-like objects."""
-    if not PY34:
-        # On Python 3.3 and older, bytes.join() doesn't handle
-        # memoryview.
-        list_of_data = (
-            bytes(data) if isinstance(data, memoryview) else data
-            for data in list_of_data)
-    return b''.join(list_of_data)
index e85634e588f5a5fc88d1e03608e1fdf9d22347a6..6af91374ecfacee999c073976f6798a5e99962b3 100644 (file)
@@ -19,20 +19,15 @@ import sys
 import threading
 import traceback
 
-from asyncio import compat
-
 
 def _get_function_source(func):
-    if compat.PY34:
-        func = inspect.unwrap(func)
-    elif hasattr(func, '__wrapped__'):
-        func = func.__wrapped__
+    func = inspect.unwrap(func)
     if inspect.isfunction(func):
         code = func.__code__
         return (code.co_filename, code.co_firstlineno)
     if isinstance(func, functools.partial):
         return _get_function_source(func.func)
-    if compat.PY34 and isinstance(func, functools.partialmethod):
+    if isinstance(func, functools.partialmethod):
         return _get_function_source(func.func)
     return None
 
index d11d289307eaf341a9ce411f6ae6c5e06b79d454..39721eaf00fd42a8ea24af21f9f9c82cbcaa2596 100644 (file)
@@ -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():
index ff12877fae2f23ac2dc8f8af20d566acbcd9f4de..c85d4dafddb2daa5ca7cfb7d1a0743b81acb413d 100644 (file)
@@ -10,7 +10,6 @@ import socket
 import warnings
 
 from . import base_events
-from . import compat
 from . import constants
 from . import futures
 from . import sslproto
@@ -86,15 +85,11 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin,
             self._read_fut.cancel()
             self._read_fut = None
 
-    # 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 self._sock is not None:
-                warnings.warn("unclosed transport %r" % self, ResourceWarning,
-                              source=self)
-                self.close()
+    def __del__(self):
+        if self._sock is not None:
+            warnings.warn("unclosed transport %r" % self, ResourceWarning,
+                          source=self)
+            self.close()
 
     def _fatal_error(self, exc, message='Fatal error on pipe transport'):
         if isinstance(exc, base_events._FATAL_ERROR_IGNORE):
index 9dbe550b01774ad9960ebd263893b4a9f65a864e..4b403560c31b2de9225bb2c6cf045301fea2cc39 100644 (file)
@@ -18,7 +18,6 @@ except ImportError:  # pragma: no cover
     ssl = None
 
 from . import base_events
-from . import compat
 from . import constants
 from . import events
 from . import futures
@@ -621,15 +620,11 @@ class _SelectorTransport(transports._FlowControlMixin,
             self._loop._remove_writer(self._sock_fd)
             self._loop.call_soon(self._call_connection_lost, None)
 
-    # 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 self._sock is not None:
-                warnings.warn("unclosed transport %r" % self, ResourceWarning,
-                              source=self)
-                self._sock.close()
+    def __del__(self):
+        if self._sock is not None:
+            warnings.warn("unclosed transport %r" % self, ResourceWarning,
+                          source=self)
+            self._sock.close()
 
     def _fatal_error(self, exc, message='Fatal error on transport'):
         # Should be called from exception handler only.
index ab7ff0bf93d076ad93524357990332b603c2724a..61d31a3b295ee47a0a239030fcf8ef26b4974ca6 100644 (file)
@@ -6,7 +6,6 @@ except ImportError:  # pragma: no cover
     ssl = None
 
 from . import base_events
-from . import compat
 from . import protocols
 from . import transports
 from .log import logger
@@ -325,15 +324,11 @@ class _SSLProtocolTransport(transports._FlowControlMixin,
         self._closed = True
         self._ssl_protocol._start_shutdown()
 
-    # 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._closed:
-                warnings.warn("unclosed transport %r" % self, ResourceWarning,
-                              source=self)
-                self.close()
+    def __del__(self):
+        if not self._closed:
+            warnings.warn("unclosed transport %r" % self, ResourceWarning,
+                          source=self)
+            self.close()
 
     def pause_reading(self):
         """Pause the receiving end.
index f91e70aecbad94ce7d0c2558e62cb3881b47903e..4fbcf3ed2e944777e7baa4f48534f4c6bc6f8ba0 100644 (file)
@@ -76,20 +76,16 @@ class Task(futures.Future):
         self._loop.call_soon(self._step)
         self.__class__._all_tasks.add(self)
 
-    # On Python 3.3 or older, objects with a destructor that are part of a
-    # reference cycle are never destroyed. That's not the case any more on
-    # Python 3.4 thanks to the PEP 442.
-    if compat.PY34:
-        def __del__(self):
-            if self._state == futures._PENDING and self._log_destroy_pending:
-                context = {
-                    'task': self,
-                    'message': 'Task was destroyed but it is pending!',
-                }
-                if self._source_traceback:
-                    context['source_traceback'] = self._source_traceback
-                self._loop.call_exception_handler(context)
-            futures.Future.__del__(self)
+    def __del__(self):
+        if self._state == futures._PENDING and self._log_destroy_pending:
+            context = {
+                'task': self,
+                'message': 'Task was destroyed but it is pending!',
+            }
+            if self._source_traceback:
+                context['source_traceback'] = self._source_traceback
+            self._loop.call_exception_handler(context)
+        futures.Future.__del__(self)
 
     def _repr_info(self):
         return base_tasks._task_repr_info(self)
index b12d5db2a9755dcc8722585ae578af7f052aba74..d273b08e8fac597558ca03fcf6f879687960ffbe 100644 (file)
@@ -26,7 +26,6 @@ except ImportError:  # pragma: no cover
     ssl = None
 
 from . import base_events
-from . import compat
 from . import events
 from . import futures
 from . import selectors
@@ -465,16 +464,6 @@ class TestCase(unittest.TestCase):
         # in an except block of a generator
         self.assertEqual(sys.exc_info(), (None, None, None))
 
-    if not compat.PY34:
-        # Python 3.3 compatibility
-        def subTest(self, *args, **kwargs):
-            class EmptyCM:
-                def __enter__(self):
-                    pass
-                def __exit__(self, *exc):
-                    pass
-            return EmptyCM()
-
 
 @contextlib.contextmanager
 def disable_logger():
index 0db0875715619d04b8c4a2f7a5fdbe934bb03981..a94079f433adfc642a7df3ed05c34a04e785a0ce 100644 (file)
@@ -1,7 +1,5 @@
 """Abstract Transport class."""
 
-from asyncio import compat
-
 __all__ = ['BaseTransport', 'ReadTransport', 'WriteTransport',
            'Transport', 'DatagramTransport', 'SubprocessTransport',
            ]
@@ -104,7 +102,7 @@ class WriteTransport(BaseTransport):
         The default implementation concatenates the arguments and
         calls write() on the result.
         """
-        data = compat.flatten_list_bytes(list_of_data)
+        data = b''.join(list_of_data)
         self.write(data)
 
     def write_eof(self):
index 2806ea8dc9089d8aa5322380ee46b8b82020f845..bf682a1a98a39f56be38a8e3e1ab4022fa06a132 100644 (file)
@@ -13,7 +13,6 @@ import warnings
 
 from . import base_events
 from . import base_subprocess
-from . import compat
 from . import constants
 from . import coroutines
 from . import events
@@ -413,15 +412,11 @@ class _UnixReadPipeTransport(transports.ReadTransport):
         if not self._closing:
             self._close(None)
 
-    # 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 self._pipe is not None:
-                warnings.warn("unclosed transport %r" % self, ResourceWarning,
-                              source=self)
-                self._pipe.close()
+    def __del__(self):
+        if self._pipe is not None:
+            warnings.warn("unclosed transport %r" % self, ResourceWarning,
+                          source=self)
+            self._pipe.close()
 
     def _fatal_error(self, exc, message='Fatal error on pipe transport'):
         # should be called by exception handler only
@@ -614,15 +609,11 @@ class _UnixWritePipeTransport(transports._FlowControlMixin,
             # write_eof is all what we needed to close the write pipe
             self.write_eof()
 
-    # 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 self._pipe is not None:
-                warnings.warn("unclosed transport %r" % self, ResourceWarning,
-                              source=self)
-                self._pipe.close()
+    def __del__(self):
+        if self._pipe is not None:
+            warnings.warn("unclosed transport %r" % self, ResourceWarning,
+                          source=self)
+            self._pipe.close()
 
     def abort(self):
         self._close(None)