Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c066bf5

Browse files
authored
gh-124858: fix happy eyeballs refcyles (#124859)
1 parent 6810928 commit c066bf5

File tree

4 files changed

+32
-6
lines changed

4 files changed

+32
-6
lines changed

Lib/asyncio/base_events.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import collections.abc
1818
import concurrent.futures
1919
import errno
20-
import functools
2120
import heapq
2221
import itertools
2322
import os
@@ -1140,11 +1139,18 @@ async def create_connection(
11401139
except OSError:
11411140
continue
11421141
else: # using happy eyeballs
1143-
sock, _, _ = await staggered.staggered_race(
1144-
(functools.partial(self._connect_sock,
1145-
exceptions, addrinfo, laddr_infos)
1146-
for addrinfo in infos),
1147-
happy_eyeballs_delay, loop=self)
1142+
sock = (await staggered.staggered_race(
1143+
(
1144+
# can't use functools.partial as it keeps a reference
1145+
# to exceptions
1146+
lambda addrinfo=addrinfo: self._connect_sock(
1147+
exceptions, addrinfo, laddr_infos
1148+
)
1149+
for addrinfo in infos
1150+
),
1151+
happy_eyeballs_delay,
1152+
loop=self,
1153+
))[0] # can't use sock, _, _ as it keeks a reference to exceptions
11481154

11491155
if sock is None:
11501156
exceptions = [exc for sub in exceptions for exc in sub]

Lib/asyncio/staggered.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ async def run_one_coro(previous_failed) -> None:
133133
raise d.exception()
134134
return winner_result, winner_index, exceptions
135135
finally:
136+
del exceptions
136137
# Make sure no tasks are left running if we leave this function
137138
for t in running_tasks:
138139
t.cancel()

Lib/test/test_asyncio/test_streams.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,24 @@ async def handle_echo(reader, writer):
12001200
messages = self._basetest_unhandled_exceptions(handle_echo)
12011201
self.assertEqual(messages, [])
12021202

1203+
def test_open_connection_happy_eyeball_refcycles(self):
1204+
port = socket_helper.find_unused_port()
1205+
async def main():
1206+
exc = None
1207+
try:
1208+
await asyncio.open_connection(
1209+
host="localhost",
1210+
port=port,
1211+
happy_eyeballs_delay=0.25,
1212+
)
1213+
except* OSError as excs:
1214+
# can't use assertRaises because that clears frames
1215+
exc = excs.exceptions[0]
1216+
self.assertIsNotNone(exc)
1217+
self.assertListEqual(gc.get_referrers(exc), [])
1218+
1219+
asyncio.run(main())
1220+
12031221

12041222
if __name__ == '__main__':
12051223
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix reference cycles left in tracebacks in :func:`asyncio.open_connection` when used with ``happy_eyeballs_delay``

0 commit comments

Comments
 (0)