Skip to content

Commit 47e064f

Browse files
author
Yury Selivanov
committed
bpo-32528: Make asyncio.CancelledError a BaseException.
This will address the common mistake many asyncio users make: an "except Exception" clause breaking Tasks cancellation. In addition to this change, we stop inheriting asyncio.TimeoutError and asyncio.InvalidStateError from their concurrent.futures.* counterparts. There's no point for these exceptions to share the inheritance chain.
1 parent 16cefb0 commit 47e064f

15 files changed

Lines changed: 144 additions & 62 deletions

Lib/asyncio/base_events.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def _interleave_addrinfos(addrinfos, first_address_family_count=1):
186186
def _run_until_complete_cb(fut):
187187
if not fut.cancelled():
188188
exc = fut.exception()
189-
if isinstance(exc, BaseException) and not isinstance(exc, Exception):
189+
if isinstance(exc, (SystemExit, KeyboardInterrupt)):
190190
# Issue #22429: run_forever() already finished, no need to
191191
# stop it.
192192
return
@@ -1196,7 +1196,7 @@ async def start_tls(self, transport, protocol, sslcontext, *,
11961196

11971197
try:
11981198
await waiter
1199-
except Exception:
1199+
except BaseException:
12001200
transport.close()
12011201
conmade_cb.cancel()
12021202
resume_cb.cancel()
@@ -1710,7 +1710,9 @@ def call_exception_handler(self, context):
17101710
if self._exception_handler is None:
17111711
try:
17121712
self.default_exception_handler(context)
1713-
except Exception:
1713+
except (SystemExit, KeyboardInterrupt):
1714+
raise
1715+
except BaseException:
17141716
# Second protection layer for unexpected errors
17151717
# in the default implementation, as well as for subclassed
17161718
# event loops with overloaded "default_exception_handler".
@@ -1719,7 +1721,9 @@ def call_exception_handler(self, context):
17191721
else:
17201722
try:
17211723
self._exception_handler(self, context)
1722-
except Exception as exc:
1724+
except (SystemExit, KeyboardInterrupt):
1725+
raise
1726+
except BaseException as exc:
17231727
# Exception in the user set custom exception handler.
17241728
try:
17251729
# Let's try default handler.
@@ -1728,7 +1732,9 @@ def call_exception_handler(self, context):
17281732
'exception': exc,
17291733
'context': context,
17301734
})
1731-
except Exception:
1735+
except (SystemExit, KeyboardInterrupt):
1736+
raise
1737+
except BaseException:
17321738
# Guard 'default_exception_handler' in case it is
17331739
# overloaded.
17341740
logger.error('Exception in default exception handler '

Lib/asyncio/base_subprocess.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,9 @@ async def _connect_pipes(self, waiter):
182182
for callback, data in self._pending_calls:
183183
loop.call_soon(callback, *data)
184184
self._pending_calls = None
185-
except Exception as exc:
185+
except (SystemExit, KeyboardInterrupt):
186+
raise
187+
except BaseException as exc:
186188
if waiter is not None and not waiter.cancelled():
187189
waiter.set_exception(exc)
188190
else:

Lib/asyncio/events.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ def cancelled(self):
7979
def _run(self):
8080
try:
8181
self._context.run(self._callback, *self._args)
82-
except Exception as exc:
82+
except (SystemExit, KeyboardInterrupt):
83+
raise
84+
except BaseException as exc:
8385
cb = format_helpers._format_callback_source(
8486
self._callback, self._args)
8587
msg = f'Exception in callback {cb}'

Lib/asyncio/exceptions.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,16 @@
55
'IncompleteReadError', 'LimitOverrunError',
66
'SendfileNotAvailableError')
77

8-
import concurrent.futures
9-
from . import base_futures
108

11-
12-
class CancelledError(concurrent.futures.CancelledError):
9+
class CancelledError(BaseException):
1310
"""The Future or Task was cancelled."""
1411

1512

16-
class TimeoutError(concurrent.futures.TimeoutError):
13+
class TimeoutError(Exception):
1714
"""The operation exceeded the given deadline."""
1815

1916

20-
class InvalidStateError(concurrent.futures.InvalidStateError):
17+
class InvalidStateError(Exception):
2118
"""The operation is not allowed in this state."""
2219

2320

Lib/asyncio/proactor_events.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,9 @@ def _eof_received(self):
212212

213213
try:
214214
keep_open = self._protocol.eof_received()
215-
except Exception as exc:
215+
except (SystemExit, KeyboardInterrupt):
216+
raise
217+
except BaseException as exc:
216218
self._fatal_error(
217219
exc, 'Fatal error: protocol.eof_received() call failed.')
218220
return
@@ -235,7 +237,9 @@ def _data_received(self, data):
235237
if isinstance(self._protocol, protocols.BufferedProtocol):
236238
try:
237239
protocols._feed_data_to_buffered_proto(self._protocol, data)
238-
except Exception as exc:
240+
except (SystemExit, KeyboardInterrupt):
241+
raise
242+
except BaseException as exc:
239243
self._fatal_error(exc,
240244
'Fatal error: protocol.buffer_updated() '
241245
'call failed.')
@@ -625,7 +629,9 @@ def _loop_self_reading(self, f=None):
625629
except exceptions.CancelledError:
626630
# _close_self_pipe() has been called, stop waiting for data
627631
return
628-
except Exception as exc:
632+
except (SystemExit, KeyboardInterrupt):
633+
raise
634+
except BaseException as exc:
629635
self.call_exception_handler({
630636
'message': 'Error on reading from the event loop self pipe',
631637
'exception': exc,

Lib/asyncio/selector_events.py

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,14 @@ async def _accept_connection2(
208208

209209
try:
210210
await waiter
211-
except:
211+
except BaseException:
212212
transport.close()
213213
raise
214+
# It's now up to the protocol to handle the connection.
214215

215-
# It's now up to the protocol to handle the connection.
216-
except Exception as exc:
216+
except (SystemExit, KeyboardInterrupt):
217+
raise
218+
except BaseException as exc:
217219
if self._debug:
218220
context = {
219221
'message':
@@ -370,7 +372,9 @@ def _sock_recv(self, fut, sock, n):
370372
data = sock.recv(n)
371373
except (BlockingIOError, InterruptedError):
372374
return # try again next time
373-
except Exception as exc:
375+
except (SystemExit, KeyboardInterrupt):
376+
raise
377+
except BaseException as exc:
374378
fut.set_exception(exc)
375379
else:
376380
fut.set_result(data)
@@ -404,7 +408,9 @@ def _sock_recv_into(self, fut, sock, buf):
404408
nbytes = sock.recv_into(buf)
405409
except (BlockingIOError, InterruptedError):
406410
return # try again next time
407-
except Exception as exc:
411+
except (SystemExit, KeyboardInterrupt):
412+
raise
413+
except BaseException as exc:
408414
fut.set_exception(exc)
409415
else:
410416
fut.set_result(nbytes)
@@ -447,7 +453,9 @@ def _sock_sendall(self, fut, sock, view, pos):
447453
n = sock.send(view[start:])
448454
except (BlockingIOError, InterruptedError):
449455
return
450-
except Exception as exc:
456+
except (SystemExit, KeyboardInterrupt):
457+
raise
458+
except BaseException as exc:
451459
fut.set_exception(exc)
452460
return
453461

@@ -487,7 +495,9 @@ def _sock_connect(self, fut, sock, address):
487495
fut.add_done_callback(
488496
functools.partial(self._sock_write_done, fd))
489497
self.add_writer(fd, self._sock_connect_cb, fut, sock, address)
490-
except Exception as exc:
498+
except (SystemExit, KeyboardInterrupt):
499+
raise
500+
except BaseException as exc:
491501
fut.set_exception(exc)
492502
else:
493503
fut.set_result(None)
@@ -507,7 +517,9 @@ def _sock_connect_cb(self, fut, sock, address):
507517
except (BlockingIOError, InterruptedError):
508518
# socket is still registered, the callback will be retried later
509519
pass
510-
except Exception as exc:
520+
except (SystemExit, KeyboardInterrupt):
521+
raise
522+
except BaseException as exc:
511523
fut.set_exception(exc)
512524
else:
513525
fut.set_result(None)
@@ -537,7 +549,9 @@ def _sock_accept(self, fut, registered, sock):
537549
conn.setblocking(False)
538550
except (BlockingIOError, InterruptedError):
539551
self.add_reader(fd, self._sock_accept, fut, True, sock)
540-
except Exception as exc:
552+
except (SystemExit, KeyboardInterrupt):
553+
raise
554+
except BaseException as exc:
541555
fut.set_exception(exc)
542556
else:
543557
fut.set_result((conn, address))
@@ -785,7 +799,9 @@ def _read_ready__get_buffer(self):
785799
buf = self._protocol.get_buffer(-1)
786800
if not len(buf):
787801
raise RuntimeError('get_buffer() returned an empty buffer')
788-
except Exception as exc:
802+
except (SystemExit, KeyboardInterrupt):
803+
raise
804+
except BaseException as exc:
789805
self._fatal_error(
790806
exc, 'Fatal error: protocol.get_buffer() call failed.')
791807
return
@@ -794,7 +810,9 @@ def _read_ready__get_buffer(self):
794810
nbytes = self._sock.recv_into(buf)
795811
except (BlockingIOError, InterruptedError):
796812
return
797-
except Exception as exc:
813+
except (SystemExit, KeyboardInterrupt):
814+
raise
815+
except BaseException as exc:
798816
self._fatal_error(exc, 'Fatal read error on socket transport')
799817
return
800818

@@ -804,7 +822,9 @@ def _read_ready__get_buffer(self):
804822

805823
try:
806824
self._protocol.buffer_updated(nbytes)
807-
except Exception as exc:
825+
except (SystemExit, KeyboardInterrupt):
826+
raise
827+
except BaseException as exc:
808828
self._fatal_error(
809829
exc, 'Fatal error: protocol.buffer_updated() call failed.')
810830

@@ -815,7 +835,9 @@ def _read_ready__data_received(self):
815835
data = self._sock.recv(self.max_size)
816836
except (BlockingIOError, InterruptedError):
817837
return
818-
except Exception as exc:
838+
except (SystemExit, KeyboardInterrupt):
839+
raise
840+
except BaseException as exc:
819841
self._fatal_error(exc, 'Fatal read error on socket transport')
820842
return
821843

@@ -825,7 +847,9 @@ def _read_ready__data_received(self):
825847

826848
try:
827849
self._protocol.data_received(data)
828-
except Exception as exc:
850+
except (SystemExit, KeyboardInterrupt):
851+
raise
852+
except BaseException as exc:
829853
self._fatal_error(
830854
exc, 'Fatal error: protocol.data_received() call failed.')
831855

@@ -835,7 +859,9 @@ def _read_ready__on_eof(self):
835859

836860
try:
837861
keep_open = self._protocol.eof_received()
838-
except Exception as exc:
862+
except (SystemExit, KeyboardInterrupt):
863+
raise
864+
except BaseException as exc:
839865
self._fatal_error(
840866
exc, 'Fatal error: protocol.eof_received() call failed.')
841867
return
@@ -871,7 +897,9 @@ def write(self, data):
871897
n = self._sock.send(data)
872898
except (BlockingIOError, InterruptedError):
873899
pass
874-
except Exception as exc:
900+
except (SystemExit, KeyboardInterrupt):
901+
raise
902+
except BaseException as exc:
875903
self._fatal_error(exc, 'Fatal write error on socket transport')
876904
return
877905
else:
@@ -894,7 +922,9 @@ def _write_ready(self):
894922
n = self._sock.send(self._buffer)
895923
except (BlockingIOError, InterruptedError):
896924
pass
897-
except Exception as exc:
925+
except (SystemExit, KeyboardInterrupt):
926+
raise
927+
except BaseException as exc:
898928
self._loop._remove_writer(self._sock_fd)
899929
self._buffer.clear()
900930
self._fatal_error(exc, 'Fatal write error on socket transport')
@@ -970,7 +1000,9 @@ def _read_ready(self):
9701000
pass
9711001
except OSError as exc:
9721002
self._protocol.error_received(exc)
973-
except Exception as exc:
1003+
except (SystemExit, KeyboardInterrupt):
1004+
raise
1005+
except BaseException as exc:
9741006
self._fatal_error(exc, 'Fatal read error on datagram transport')
9751007
else:
9761008
self._protocol.datagram_received(data, addr)
@@ -1007,7 +1039,9 @@ def sendto(self, data, addr=None):
10071039
except OSError as exc:
10081040
self._protocol.error_received(exc)
10091041
return
1010-
except Exception as exc:
1042+
except (SystemExit, KeyboardInterrupt):
1043+
raise
1044+
except BaseException as exc:
10111045
self._fatal_error(
10121046
exc, 'Fatal write error on datagram transport')
10131047
return
@@ -1030,7 +1064,9 @@ def _sendto_ready(self):
10301064
except OSError as exc:
10311065
self._protocol.error_received(exc)
10321066
return
1033-
except Exception as exc:
1067+
except (SystemExit, KeyboardInterrupt):
1068+
raise
1069+
except BaseException as exc:
10341070
self._fatal_error(
10351071
exc, 'Fatal write error on datagram transport')
10361072
return

Lib/asyncio/sslproto.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,9 @@ def data_received(self, data):
527527

528528
try:
529529
ssldata, appdata = self._sslpipe.feed_ssldata(data)
530-
except Exception as e:
530+
except (SystemExit, KeyboardInterrupt):
531+
raise
532+
except BaseException as e:
531533
self._fatal_error(e, 'SSL error in data received')
532534
return
533535

@@ -542,7 +544,9 @@ def data_received(self, data):
542544
self._app_protocol, chunk)
543545
else:
544546
self._app_protocol.data_received(chunk)
545-
except Exception as ex:
547+
except (SystemExit, KeyboardInterrupt):
548+
raise
549+
except BaseException as ex:
546550
self._fatal_error(
547551
ex, 'application protocol failed to receive SSL data')
548552
return
@@ -628,7 +632,9 @@ def _on_handshake_complete(self, handshake_exc):
628632
raise handshake_exc
629633

630634
peercert = sslobj.getpeercert()
631-
except Exception as exc:
635+
except (SystemExit, KeyboardInterrupt):
636+
raise
637+
except BaseException as exc:
632638
if isinstance(exc, ssl.CertificateError):
633639
msg = 'SSL handshake failed on verifying the certificate'
634640
else:
@@ -691,7 +697,9 @@ def _process_write_backlog(self):
691697
# delete it and reduce the outstanding buffer size.
692698
del self._write_backlog[0]
693699
self._write_buffer_size -= len(data)
694-
except Exception as exc:
700+
except (SystemExit, KeyboardInterrupt):
701+
raise
702+
except BaseException as exc:
695703
if self._in_handshake:
696704
# Exceptions will be re-raised in _on_handshake_complete.
697705
self._on_handshake_complete(exc)

Lib/asyncio/staggered.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ async def run_one_coro(
105105

106106
try:
107107
result = await coro_fn()
108-
except Exception as e:
108+
except (SystemExit, KeyboardInterrupt):
109+
raise
110+
except BaseException as e:
109111
exceptions[this_index] = e
110112
this_failed.set() # Kickstart the next coroutine
111113
else:

0 commit comments

Comments
 (0)