summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Grainger <tagrain@gmail.com>2024-10-02 23:32:31 (GMT)
committerGitHub <noreply@github.com>2024-10-02 23:32:31 (GMT)
commitc066bf553577d1000e208eb078d9e758c3e41186 (patch)
tree0d754f19f47cf0fb5c5a126c6d6b6450b273ef49
parent6810928927e4d12d9a5dd90e672afb096882b730 (diff)
downloadcpython-c066bf553577d1000e208eb078d9e758c3e41186.zip
cpython-c066bf553577d1000e208eb078d9e758c3e41186.tar.gz
cpython-c066bf553577d1000e208eb078d9e758c3e41186.tar.bz2
gh-124858: fix happy eyeballs refcyles (#124859)
-rw-r--r--Lib/asyncio/base_events.py18
-rw-r--r--Lib/asyncio/staggered.py1
-rw-r--r--Lib/test/test_asyncio/test_streams.py18
-rw-r--r--Misc/NEWS.d/next/Library/2024-10-01-17-12-20.gh-issue-124858.Zy0tvT.rst1
4 files changed, 32 insertions, 6 deletions
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 000647f..5dbe4b2 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -17,7 +17,6 @@ import collections
import collections.abc
import concurrent.futures
import errno
-import functools
import heapq
import itertools
import os
@@ -1140,11 +1139,18 @@ class BaseEventLoop(events.AbstractEventLoop):
except OSError:
continue
else: # using happy eyeballs
- sock, _, _ = await staggered.staggered_race(
- (functools.partial(self._connect_sock,
- exceptions, addrinfo, laddr_infos)
- for addrinfo in infos),
- happy_eyeballs_delay, loop=self)
+ sock = (await staggered.staggered_race(
+ (
+ # can't use functools.partial as it keeps a reference
+ # to exceptions
+ lambda addrinfo=addrinfo: self._connect_sock(
+ exceptions, addrinfo, laddr_infos
+ )
+ for addrinfo in infos
+ ),
+ happy_eyeballs_delay,
+ loop=self,
+ ))[0] # can't use sock, _, _ as it keeks a reference to exceptions
if sock is None:
exceptions = [exc for sub in exceptions for exc in sub]
diff --git a/Lib/asyncio/staggered.py b/Lib/asyncio/staggered.py
index c3a7441..326c6f7 100644
--- a/Lib/asyncio/staggered.py
+++ b/Lib/asyncio/staggered.py
@@ -133,6 +133,7 @@ async def staggered_race(coro_fns, delay, *, loop=None):
raise d.exception()
return winner_result, winner_index, exceptions
finally:
+ del exceptions
# Make sure no tasks are left running if we leave this function
for t in running_tasks:
t.cancel()
diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py
index d32b7ff..0688299 100644
--- a/Lib/test/test_asyncio/test_streams.py
+++ b/Lib/test/test_asyncio/test_streams.py
@@ -1200,6 +1200,24 @@ class StreamTests(test_utils.TestCase):
messages = self._basetest_unhandled_exceptions(handle_echo)
self.assertEqual(messages, [])
+ def test_open_connection_happy_eyeball_refcycles(self):
+ port = socket_helper.find_unused_port()
+ async def main():
+ exc = None
+ try:
+ await asyncio.open_connection(
+ host="localhost",
+ port=port,
+ happy_eyeballs_delay=0.25,
+ )
+ except* OSError as excs:
+ # can't use assertRaises because that clears frames
+ exc = excs.exceptions[0]
+ self.assertIsNotNone(exc)
+ self.assertListEqual(gc.get_referrers(exc), [])
+
+ asyncio.run(main())
+
if __name__ == '__main__':
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2024-10-01-17-12-20.gh-issue-124858.Zy0tvT.rst b/Misc/NEWS.d/next/Library/2024-10-01-17-12-20.gh-issue-124858.Zy0tvT.rst
new file mode 100644
index 0000000..c05d24a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-10-01-17-12-20.gh-issue-124858.Zy0tvT.rst
@@ -0,0 +1 @@
+Fix reference cycles left in tracebacks in :func:`asyncio.open_connection` when used with ``happy_eyeballs_delay``