Skip to content

Commit 39d6608

Browse files
committed
bpo-29617: Remove Python 3.3 support from asyncio
1 parent 3e8d6cb commit 39d6608

File tree

12 files changed

+64
-212
lines changed

12 files changed

+64
-212
lines changed

Lib/asyncio/base_events.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -498,16 +498,12 @@ def is_closed(self):
498498
"""Returns True if the event loop was closed."""
499499
return self._closed
500500

501-
# On Python 3.3 and older, objects with a destructor part of a reference
502-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
503-
# to the PEP 442.
504-
if compat.PY34:
505-
def __del__(self):
506-
if not self.is_closed():
507-
warnings.warn("unclosed event loop %r" % self, ResourceWarning,
508-
source=self)
509-
if not self.is_running():
510-
self.close()
501+
def __del__(self):
502+
if not self.is_closed():
503+
warnings.warn("unclosed event loop %r" % self, ResourceWarning,
504+
source=self)
505+
if not self.is_running():
506+
self.close()
511507

512508
def is_running(self):
513509
"""Returns True if the event loop is running."""

Lib/asyncio/base_subprocess.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,11 @@ def close(self):
121121

122122
# Don't clear the _proc reference yet: _post_init() may still run
123123

124-
# On Python 3.3 and older, objects with a destructor part of a reference
125-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
126-
# to the PEP 442.
127-
if compat.PY34:
128-
def __del__(self):
129-
if not self._closed:
130-
warnings.warn("unclosed transport %r" % self, ResourceWarning,
131-
source=self)
132-
self.close()
124+
def __del__(self):
125+
if not self._closed:
126+
warnings.warn("unclosed transport %r" % self, ResourceWarning,
127+
source=self)
128+
self.close()
133129

134130
def get_pid(self):
135131
return self._pid

Lib/asyncio/compat.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,5 @@
22

33
import sys
44

5-
PY34 = sys.version_info >= (3, 4)
65
PY35 = sys.version_info >= (3, 5)
76
PY352 = sys.version_info >= (3, 5, 2)
8-
9-
10-
def flatten_list_bytes(list_of_data):
11-
"""Concatenate a sequence of bytes-like objects."""
12-
if not PY34:
13-
# On Python 3.3 and older, bytes.join() doesn't handle
14-
# memoryview.
15-
list_of_data = (
16-
bytes(data) if isinstance(data, memoryview) else data
17-
for data in list_of_data)
18-
return b''.join(list_of_data)

Lib/asyncio/events.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,13 @@
2222

2323

2424
def _get_function_source(func):
25-
if compat.PY34:
26-
func = inspect.unwrap(func)
27-
elif hasattr(func, '__wrapped__'):
25+
if hasattr(func, '__wrapped__'):
2826
func = func.__wrapped__
2927
if inspect.isfunction(func):
3028
code = func.__code__
3129
return (code.co_filename, code.co_firstlineno)
3230
if isinstance(func, functools.partial):
3331
return _get_function_source(func.func)
34-
if compat.PY34 and isinstance(func, functools.partialmethod):
35-
return _get_function_source(func.func)
3632
return None
3733

3834

Lib/asyncio/futures.py

Lines changed: 16 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -27,86 +27,6 @@
2727
STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
2828

2929

30-
class _TracebackLogger:
31-
"""Helper to log a traceback upon destruction if not cleared.
32-
33-
This solves a nasty problem with Futures and Tasks that have an
34-
exception set: if nobody asks for the exception, the exception is
35-
never logged. This violates the Zen of Python: 'Errors should
36-
never pass silently. Unless explicitly silenced.'
37-
38-
However, we don't want to log the exception as soon as
39-
set_exception() is called: if the calling code is written
40-
properly, it will get the exception and handle it properly. But
41-
we *do* want to log it if result() or exception() was never called
42-
-- otherwise developers waste a lot of time wondering why their
43-
buggy code fails silently.
44-
45-
An earlier attempt added a __del__() method to the Future class
46-
itself, but this backfired because the presence of __del__()
47-
prevents garbage collection from breaking cycles. A way out of
48-
this catch-22 is to avoid having a __del__() method on the Future
49-
class itself, but instead to have a reference to a helper object
50-
with a __del__() method that logs the traceback, where we ensure
51-
that the helper object doesn't participate in cycles, and only the
52-
Future has a reference to it.
53-
54-
The helper object is added when set_exception() is called. When
55-
the Future is collected, and the helper is present, the helper
56-
object is also collected, and its __del__() method will log the
57-
traceback. When the Future's result() or exception() method is
58-
called (and a helper object is present), it removes the helper
59-
object, after calling its clear() method to prevent it from
60-
logging.
61-
62-
One downside is that we do a fair amount of work to extract the
63-
traceback from the exception, even when it is never logged. It
64-
would seem cheaper to just store the exception object, but that
65-
references the traceback, which references stack frames, which may
66-
reference the Future, which references the _TracebackLogger, and
67-
then the _TracebackLogger would be included in a cycle, which is
68-
what we're trying to avoid! As an optimization, we don't
69-
immediately format the exception; we only do the work when
70-
activate() is called, which call is delayed until after all the
71-
Future's callbacks have run. Since usually a Future has at least
72-
one callback (typically set by 'yield from') and usually that
73-
callback extracts the callback, thereby removing the need to
74-
format the exception.
75-
76-
PS. I don't claim credit for this solution. I first heard of it
77-
in a discussion about closing files when they are collected.
78-
"""
79-
80-
__slots__ = ('loop', 'source_traceback', 'exc', 'tb')
81-
82-
def __init__(self, future, exc):
83-
self.loop = future._loop
84-
self.source_traceback = future._source_traceback
85-
self.exc = exc
86-
self.tb = None
87-
88-
def activate(self):
89-
exc = self.exc
90-
if exc is not None:
91-
self.exc = None
92-
self.tb = traceback.format_exception(exc.__class__, exc,
93-
exc.__traceback__)
94-
95-
def clear(self):
96-
self.exc = None
97-
self.tb = None
98-
99-
def __del__(self):
100-
if self.tb:
101-
msg = 'Future/Task exception was never retrieved\n'
102-
if self.source_traceback:
103-
src = ''.join(traceback.format_list(self.source_traceback))
104-
msg += 'Future/Task created at (most recent call last):\n'
105-
msg += '%s\n' % src.rstrip()
106-
msg += ''.join(self.tb).rstrip()
107-
self.loop.call_exception_handler({'message': msg})
108-
109-
11030
class Future:
11131
"""This class is *almost* compatible with concurrent.futures.Future.
11232
@@ -164,25 +84,21 @@ def __init__(self, *, loop=None):
16484
def __repr__(self):
16585
return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info()))
16686

167-
# On Python 3.3 and older, objects with a destructor part of a reference
168-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
169-
# to the PEP 442.
170-
if compat.PY34:
171-
def __del__(self):
172-
if not self._log_traceback:
173-
# set_exception() was not called, or result() or exception()
174-
# has consumed the exception
175-
return
176-
exc = self._exception
177-
context = {
178-
'message': ('%s exception was never retrieved'
179-
% self.__class__.__name__),
180-
'exception': exc,
181-
'future': self,
182-
}
183-
if self._source_traceback:
184-
context['source_traceback'] = self._source_traceback
185-
self._loop.call_exception_handler(context)
87+
def __del__(self):
88+
if not self._log_traceback:
89+
# set_exception() was not called, or result() or exception()
90+
# has consumed the exception
91+
return
92+
exc = self._exception
93+
context = {
94+
'message': ('%s exception was never retrieved'
95+
% self.__class__.__name__),
96+
'exception': exc,
97+
'future': self,
98+
}
99+
if self._source_traceback:
100+
context['source_traceback'] = self._source_traceback
101+
self._loop.call_exception_handler(context)
186102

187103
def cancel(self):
188104
"""Cancel the future and schedule callbacks.
@@ -317,13 +233,7 @@ def set_exception(self, exception):
317233
self._exception = exception
318234
self._state = _FINISHED
319235
self._schedule_callbacks()
320-
if compat.PY34:
321-
self._log_traceback = True
322-
else:
323-
self._tb_logger = _TracebackLogger(self, exception)
324-
# Arrange for the logger to be activated after all callbacks
325-
# have had a chance to call result() or exception().
326-
self._loop.call_soon(self._tb_logger.activate)
236+
self._log_traceback = True
327237

328238
def __iter__(self):
329239
if not self.done():

Lib/asyncio/proactor_events.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,11 @@ def close(self):
8686
self._read_fut.cancel()
8787
self._read_fut = None
8888

89-
# On Python 3.3 and older, objects with a destructor part of a reference
90-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
91-
# to the PEP 442.
92-
if compat.PY34:
93-
def __del__(self):
94-
if self._sock is not None:
95-
warnings.warn("unclosed transport %r" % self, ResourceWarning,
96-
source=self)
97-
self.close()
89+
def __del__(self):
90+
if self._sock is not None:
91+
warnings.warn("unclosed transport %r" % self, ResourceWarning,
92+
source=self)
93+
self.close()
9894

9995
def _fatal_error(self, exc, message='Fatal error on pipe transport'):
10096
if isinstance(exc, base_events._FATAL_ERROR_IGNORE):

Lib/asyncio/selector_events.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -621,15 +621,11 @@ def close(self):
621621
self._loop._remove_writer(self._sock_fd)
622622
self._loop.call_soon(self._call_connection_lost, None)
623623

624-
# On Python 3.3 and older, objects with a destructor part of a reference
625-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
626-
# to the PEP 442.
627-
if compat.PY34:
628-
def __del__(self):
629-
if self._sock is not None:
630-
warnings.warn("unclosed transport %r" % self, ResourceWarning,
631-
source=self)
632-
self._sock.close()
624+
def __del__(self):
625+
if self._sock is not None:
626+
warnings.warn("unclosed transport %r" % self, ResourceWarning,
627+
source=self)
628+
self._sock.close()
633629

634630
def _fatal_error(self, exc, message='Fatal error on transport'):
635631
# Should be called from exception handler only.

Lib/asyncio/sslproto.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -325,15 +325,11 @@ def close(self):
325325
self._closed = True
326326
self._ssl_protocol._start_shutdown()
327327

328-
# On Python 3.3 and older, objects with a destructor part of a reference
329-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
330-
# to the PEP 442.
331-
if compat.PY34:
332-
def __del__(self):
333-
if not self._closed:
334-
warnings.warn("unclosed transport %r" % self, ResourceWarning,
335-
source=self)
336-
self.close()
328+
def __del__(self):
329+
if not self._closed:
330+
warnings.warn("unclosed transport %r" % self, ResourceWarning,
331+
source=self)
332+
self.close()
337333

338334
def pause_reading(self):
339335
"""Pause the receiving end.

Lib/asyncio/tasks.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,16 @@ def __init__(self, coro, *, loop=None):
7676
self._loop.call_soon(self._step)
7777
self.__class__._all_tasks.add(self)
7878

79-
# On Python 3.3 or older, objects with a destructor that are part of a
80-
# reference cycle are never destroyed. That's not the case any more on
81-
# Python 3.4 thanks to the PEP 442.
82-
if compat.PY34:
83-
def __del__(self):
84-
if self._state == futures._PENDING and self._log_destroy_pending:
85-
context = {
86-
'task': self,
87-
'message': 'Task was destroyed but it is pending!',
88-
}
89-
if self._source_traceback:
90-
context['source_traceback'] = self._source_traceback
91-
self._loop.call_exception_handler(context)
92-
futures.Future.__del__(self)
79+
def __del__(self):
80+
if self._state == futures._PENDING and self._log_destroy_pending:
81+
context = {
82+
'task': self,
83+
'message': 'Task was destroyed but it is pending!',
84+
}
85+
if self._source_traceback:
86+
context['source_traceback'] = self._source_traceback
87+
self._loop.call_exception_handler(context)
88+
futures.Future.__del__(self)
9389

9490
def _repr_info(self):
9591
return base_tasks._task_repr_info(self)

Lib/asyncio/test_utils.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -462,16 +462,6 @@ def tearDown(self):
462462
# in an except block of a generator
463463
self.assertEqual(sys.exc_info(), (None, None, None))
464464

465-
if not compat.PY34:
466-
# Python 3.3 compatibility
467-
def subTest(self, *args, **kwargs):
468-
class EmptyCM:
469-
def __enter__(self):
470-
pass
471-
def __exit__(self, *exc):
472-
pass
473-
return EmptyCM()
474-
475465

476466
@contextlib.contextmanager
477467
def disable_logger():

Lib/asyncio/transports.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def writelines(self, list_of_data):
104104
The default implementation concatenates the arguments and
105105
calls write() on the result.
106106
"""
107-
data = compat.flatten_list_bytes(list_of_data)
107+
data = b''.join(list_of_data)
108108
self.write(data)
109109

110110
def write_eof(self):

Lib/asyncio/unix_events.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -413,15 +413,11 @@ def close(self):
413413
if not self._closing:
414414
self._close(None)
415415

416-
# On Python 3.3 and older, objects with a destructor part of a reference
417-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
418-
# to the PEP 442.
419-
if compat.PY34:
420-
def __del__(self):
421-
if self._pipe is not None:
422-
warnings.warn("unclosed transport %r" % self, ResourceWarning,
423-
source=self)
424-
self._pipe.close()
416+
def __del__(self):
417+
if self._pipe is not None:
418+
warnings.warn("unclosed transport %r" % self, ResourceWarning,
419+
source=self)
420+
self._pipe.close()
425421

426422
def _fatal_error(self, exc, message='Fatal error on pipe transport'):
427423
# should be called by exception handler only
@@ -614,15 +610,11 @@ def close(self):
614610
# write_eof is all what we needed to close the write pipe
615611
self.write_eof()
616612

617-
# On Python 3.3 and older, objects with a destructor part of a reference
618-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
619-
# to the PEP 442.
620-
if compat.PY34:
621-
def __del__(self):
622-
if self._pipe is not None:
623-
warnings.warn("unclosed transport %r" % self, ResourceWarning,
624-
source=self)
625-
self._pipe.close()
613+
def __del__(self):
614+
if self._pipe is not None:
615+
warnings.warn("unclosed transport %r" % self, ResourceWarning,
616+
source=self)
617+
self._pipe.close()
626618

627619
def abort(self):
628620
self._close(None)

0 commit comments

Comments
 (0)