Skip to content

Commit f7ffe4a

Browse files
miss-islingtonkumaraditya303vstinner
authored
[3.11] [3.12] GH-110894: Call loop exception handler for exceptions in client_connected_cb (GH-111601) (GH-111632) (#111634)
* [3.12] GH-110894: Call loop exception handler for exceptions in client_connected_cb (GH-111601) (GH-111632) (cherry picked from commit 9aa8829) Co-authored-by: Kumar Aditya <[email protected]> Call loop exception handler for exceptions in `client_connected_cb` of `asyncio.start_server` so that applications can handle it.. (cherry picked from commit 229f44d) * gh-111644: Fix asyncio test_unhandled_exceptions() (#111713) Fix test_unhandled_exceptions() of test_asyncio.test_streams: break explicitly a reference cycle. Fix also StreamTests.tearDown(): the loop must not be closed explicitly, but using set_event_loop() which takes care of shutting down the executor with executor.shutdown(wait=True). BaseEventLoop.close() calls executor.shutdown(wait=False). (cherry picked from commit ac01e22) --------- Co-authored-by: Kumar Aditya <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent 89264a3 commit f7ffe4a

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

Lib/asyncio/streams.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,19 @@ def connection_made(self, transport):
245245
res = self._client_connected_cb(reader,
246246
self._stream_writer)
247247
if coroutines.iscoroutine(res):
248+
def callback(task):
249+
exc = task.exception()
250+
if exc is not None:
251+
self._loop.call_exception_handler({
252+
'message': 'Unhandled exception in client_connected_cb',
253+
'exception': exc,
254+
'transport': transport,
255+
})
256+
transport.close()
257+
248258
self._task = self._loop.create_task(res)
259+
self._task.add_done_callback(callback)
260+
249261
self._strong_reader = None
250262

251263
def connection_lost(self, exc):

Lib/test/test_asyncio/test_streams.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ def tearDown(self):
3636
# just in case if we have transport close callbacks
3737
test_utils.run_briefly(self.loop)
3838

39-
self.loop.close()
40-
gc.collect()
39+
# set_event_loop() takes care of closing self.loop in a safe way
4140
super().tearDown()
4241

4342
def _basetest_open_connection(self, open_connection_fut):
@@ -1068,6 +1067,37 @@ def test_eof_feed_when_closing_writer(self):
10681067

10691068
self.assertEqual(messages, [])
10701069

1070+
def test_unhandled_exceptions(self) -> None:
1071+
port = socket_helper.find_unused_port()
1072+
1073+
messages = []
1074+
self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx))
1075+
1076+
async def client():
1077+
rd, wr = await asyncio.open_connection('localhost', port)
1078+
wr.write(b'test msg')
1079+
await wr.drain()
1080+
wr.close()
1081+
await wr.wait_closed()
1082+
1083+
async def main():
1084+
async def handle_echo(reader, writer):
1085+
raise Exception('test')
1086+
1087+
server = await asyncio.start_server(
1088+
handle_echo, 'localhost', port)
1089+
await server.start_serving()
1090+
await client()
1091+
server.close()
1092+
await server.wait_closed()
1093+
1094+
self.loop.run_until_complete(main())
1095+
1096+
self.assertEqual(messages[0]['message'],
1097+
'Unhandled exception in client_connected_cb')
1098+
# Break explicitly reference cycle
1099+
messages = None
1100+
10711101

10721102
if __name__ == '__main__':
10731103
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Call loop exception handler for exceptions in ``client_connected_cb`` of :func:`asyncio.start_server` so that applications can handle it. Patch by Kumar Aditya.

0 commit comments

Comments
 (0)