aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/test_asyncio
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_asyncio')
-rw-r--r--Lib/test/test_asyncio/test_base_events.py149
-rw-r--r--Lib/test/test_asyncio/test_buffered_proto.py2
-rw-r--r--Lib/test/test_asyncio/test_context.py2
-rw-r--r--Lib/test/test_asyncio/test_eager_task_factory.py2
-rw-r--r--Lib/test/test_asyncio/test_events.py34
-rw-r--r--Lib/test/test_asyncio/test_free_threading.py2
-rw-r--r--Lib/test/test_asyncio/test_futures.py60
-rw-r--r--Lib/test/test_asyncio/test_futures2.py2
-rw-r--r--Lib/test/test_asyncio/test_graph.py2
-rw-r--r--Lib/test/test_asyncio/test_locks.py2
-rw-r--r--Lib/test/test_asyncio/test_pep492.py2
-rw-r--r--Lib/test/test_asyncio/test_proactor_events.py2
-rw-r--r--Lib/test/test_asyncio/test_protocols.py2
-rw-r--r--Lib/test/test_asyncio/test_queues.py2
-rw-r--r--Lib/test/test_asyncio/test_runners.py16
-rw-r--r--Lib/test/test_asyncio/test_selector_events.py18
-rw-r--r--Lib/test/test_asyncio/test_sendfile.py2
-rw-r--r--Lib/test/test_asyncio/test_server.py2
-rw-r--r--Lib/test/test_asyncio/test_sock_lowlevel.py2
-rw-r--r--Lib/test/test_asyncio/test_ssl.py2
-rw-r--r--Lib/test/test_asyncio/test_sslproto.py2
-rw-r--r--Lib/test/test_asyncio/test_staggered.py2
-rw-r--r--Lib/test/test_asyncio/test_streams.py2
-rw-r--r--Lib/test/test_asyncio/test_subprocess.py2
-rw-r--r--Lib/test/test_asyncio/test_taskgroups.py2
-rw-r--r--Lib/test/test_asyncio/test_tasks.py42
-rw-r--r--Lib/test/test_asyncio/test_threads.py2
-rw-r--r--Lib/test/test_asyncio/test_timeouts.py2
-rw-r--r--Lib/test/test_asyncio/test_tools.py1560
-rw-r--r--Lib/test/test_asyncio/test_transports.py2
-rw-r--r--Lib/test/test_asyncio/test_unix_events.py2
-rw-r--r--Lib/test/test_asyncio/test_waitfor.py2
-rw-r--r--Lib/test/test_asyncio/test_windows_events.py14
-rw-r--r--Lib/test/test_asyncio/test_windows_utils.py2
-rw-r--r--Lib/test/test_asyncio/utils.py6
35 files changed, 1533 insertions, 418 deletions
diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py
index 2ca5c4c6719..8c02de77c24 100644
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -24,8 +24,12 @@ import warnings
MOCK_ANY = mock.ANY
+class CustomError(Exception):
+ pass
+
+
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
def mock_socket_module():
@@ -146,6 +150,29 @@ class BaseEventTests(test_utils.TestCase):
socket.SOCK_STREAM,
socket.IPPROTO_TCP))
+ def test_interleave_addrinfos(self):
+ self.maxDiff = None
+ SIX_A = (socket.AF_INET6, 0, 0, '', ('2001:db8::1', 1))
+ SIX_B = (socket.AF_INET6, 0, 0, '', ('2001:db8::2', 2))
+ SIX_C = (socket.AF_INET6, 0, 0, '', ('2001:db8::3', 3))
+ SIX_D = (socket.AF_INET6, 0, 0, '', ('2001:db8::4', 4))
+ FOUR_A = (socket.AF_INET, 0, 0, '', ('192.0.2.1', 5))
+ FOUR_B = (socket.AF_INET, 0, 0, '', ('192.0.2.2', 6))
+ FOUR_C = (socket.AF_INET, 0, 0, '', ('192.0.2.3', 7))
+ FOUR_D = (socket.AF_INET, 0, 0, '', ('192.0.2.4', 8))
+
+ addrinfos = [SIX_A, SIX_B, SIX_C, FOUR_A, FOUR_B, FOUR_C, FOUR_D, SIX_D]
+ expected = [SIX_A, FOUR_A, SIX_B, FOUR_B, SIX_C, FOUR_C, SIX_D, FOUR_D]
+
+ self.assertEqual(expected, base_events._interleave_addrinfos(addrinfos))
+
+ expected_fafc_2 = [SIX_A, SIX_B, FOUR_A, SIX_C, FOUR_B, SIX_D, FOUR_C, FOUR_D]
+ self.assertEqual(
+ expected_fafc_2,
+ base_events._interleave_addrinfos(addrinfos, first_address_family_count=2),
+ )
+
+
class BaseEventLoopTests(test_utils.TestCase):
@@ -1049,6 +1076,71 @@ class BaseEventLoopTests(test_utils.TestCase):
test_utils.run_briefly(self.loop)
self.assertTrue(status['finalized'])
+ @unittest.skipUnless(socket_helper.IPV6_ENABLED, 'no IPv6 support')
+ @patch_socket
+ def test_create_connection_happy_eyeballs(self, m_socket):
+
+ class MyProto(asyncio.Protocol):
+ pass
+
+ async def getaddrinfo(*args, **kw):
+ return [(socket.AF_INET6, 0, 0, '', ('2001:db8::1', 1)),
+ (socket.AF_INET, 0, 0, '', ('192.0.2.1', 5))]
+
+ async def sock_connect(sock, address):
+ if address[0] == '2001:db8::1':
+ await asyncio.sleep(1)
+ sock.connect(address)
+
+ loop = asyncio.new_event_loop()
+ loop._add_writer = mock.Mock()
+ loop._add_writer = mock.Mock()
+ loop._add_reader = mock.Mock()
+ loop.getaddrinfo = getaddrinfo
+ loop.sock_connect = sock_connect
+
+ coro = loop.create_connection(MyProto, 'example.com', 80, happy_eyeballs_delay=0.3)
+ transport, protocol = loop.run_until_complete(coro)
+ try:
+ sock = transport._sock
+ sock.connect.assert_called_with(('192.0.2.1', 5))
+ finally:
+ transport.close()
+ test_utils.run_briefly(loop) # allow transport to close
+ loop.close()
+
+ @patch_socket
+ def test_create_connection_happy_eyeballs_ipv4_only(self, m_socket):
+
+ class MyProto(asyncio.Protocol):
+ pass
+
+ async def getaddrinfo(*args, **kw):
+ return [(socket.AF_INET, 0, 0, '', ('192.0.2.1', 5)),
+ (socket.AF_INET, 0, 0, '', ('192.0.2.2', 6))]
+
+ async def sock_connect(sock, address):
+ if address[0] == '192.0.2.1':
+ await asyncio.sleep(1)
+ sock.connect(address)
+
+ loop = asyncio.new_event_loop()
+ loop._add_writer = mock.Mock()
+ loop._add_writer = mock.Mock()
+ loop._add_reader = mock.Mock()
+ loop.getaddrinfo = getaddrinfo
+ loop.sock_connect = sock_connect
+
+ coro = loop.create_connection(MyProto, 'example.com', 80, happy_eyeballs_delay=0.3)
+ transport, protocol = loop.run_until_complete(coro)
+ try:
+ sock = transport._sock
+ sock.connect.assert_called_with(('192.0.2.2', 6))
+ finally:
+ transport.close()
+ test_utils.run_briefly(loop) # allow transport to close
+ loop.close()
+
class MyProto(asyncio.Protocol):
done = None
@@ -1190,6 +1282,36 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase):
self.loop.run_until_complete(coro)
self.assertTrue(sock.close.called)
+ @patch_socket
+ def test_create_connection_happy_eyeballs_empty_exceptions(self, m_socket):
+ # See gh-135836: Fix IndexError when Happy Eyeballs algorithm
+ # results in empty exceptions list
+
+ async def getaddrinfo(*args, **kw):
+ return [(socket.AF_INET, socket.SOCK_STREAM, 0, '', ('127.0.0.1', 80)),
+ (socket.AF_INET6, socket.SOCK_STREAM, 0, '', ('::1', 80))]
+
+ def getaddrinfo_task(*args, **kwds):
+ return self.loop.create_task(getaddrinfo(*args, **kwds))
+
+ self.loop.getaddrinfo = getaddrinfo_task
+
+ # Mock staggered_race to return empty exceptions list
+ # This simulates the scenario where Happy Eyeballs algorithm
+ # cancels all attempts but doesn't properly collect exceptions
+ with mock.patch('asyncio.staggered.staggered_race') as mock_staggered:
+ # Return (None, []) - no winner, empty exceptions list
+ async def mock_race(coro_fns, delay, loop):
+ return None, []
+ mock_staggered.side_effect = mock_race
+
+ coro = self.loop.create_connection(
+ MyProto, 'example.com', 80, happy_eyeballs_delay=0.1)
+
+ # Should raise TimeoutError instead of IndexError
+ with self.assertRaisesRegex(TimeoutError, "create_connection failed"):
+ self.loop.run_until_complete(coro)
+
def test_create_connection_host_port_sock(self):
coro = self.loop.create_connection(
MyProto, 'example.com', 80, sock=object())
@@ -1296,6 +1418,31 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase):
self.assertEqual(len(cm.exception.exceptions), 1)
self.assertIsInstance(cm.exception.exceptions[0], OSError)
+ @patch_socket
+ def test_create_connection_connect_non_os_err_close_err(self, m_socket):
+ # Test the case when sock_connect() raises non-OSError exception
+ # and sock.close() raises OSError.
+ async def getaddrinfo(*args, **kw):
+ return [(2, 1, 6, '', ('107.6.106.82', 80))]
+
+ def getaddrinfo_task(*args, **kwds):
+ return self.loop.create_task(getaddrinfo(*args, **kwds))
+
+ self.loop.getaddrinfo = getaddrinfo_task
+ self.loop.sock_connect = mock.Mock()
+ self.loop.sock_connect.side_effect = CustomError
+ sock = mock.Mock()
+ m_socket.socket.return_value = sock
+ sock.close.side_effect = OSError
+
+ coro = self.loop.create_connection(MyProto, 'example.com', 80)
+ self.assertRaises(
+ CustomError, self.loop.run_until_complete, coro)
+
+ coro = self.loop.create_connection(MyProto, 'example.com', 80, all_errors=True)
+ self.assertRaises(
+ CustomError, self.loop.run_until_complete, coro)
+
def test_create_connection_multiple(self):
async def getaddrinfo(*args, **kw):
return [(2, 1, 6, '', ('0.0.0.1', 80)),
diff --git a/Lib/test/test_asyncio/test_buffered_proto.py b/Lib/test/test_asyncio/test_buffered_proto.py
index 9c386dd2e63..6d3edcc36f5 100644
--- a/Lib/test/test_asyncio/test_buffered_proto.py
+++ b/Lib/test/test_asyncio/test_buffered_proto.py
@@ -5,7 +5,7 @@ from test.test_asyncio import functional as func_tests
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class ReceiveStuffProto(asyncio.BufferedProtocol):
diff --git a/Lib/test/test_asyncio/test_context.py b/Lib/test/test_asyncio/test_context.py
index ad394f44e7e..f85f39839cb 100644
--- a/Lib/test/test_asyncio/test_context.py
+++ b/Lib/test/test_asyncio/test_context.py
@@ -4,7 +4,7 @@ import unittest
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
@unittest.skipUnless(decimal.HAVE_CONTEXTVAR, "decimal is built with a thread-local context")
diff --git a/Lib/test/test_asyncio/test_eager_task_factory.py b/Lib/test/test_asyncio/test_eager_task_factory.py
index 9f3b6f9acef..da79ee9260a 100644
--- a/Lib/test/test_asyncio/test_eager_task_factory.py
+++ b/Lib/test/test_asyncio/test_eager_task_factory.py
@@ -13,7 +13,7 @@ MOCK_ANY = mock.ANY
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class EagerTaskFactoryLoopTests:
diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py
index 873c503fa02..919d543b032 100644
--- a/Lib/test/test_asyncio/test_events.py
+++ b/Lib/test/test_asyncio/test_events.py
@@ -38,7 +38,7 @@ from test.support import threading_helper
from test.support import ALWAYS_EQ, LARGEST, SMALLEST
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
def broken_unix_getsockname():
@@ -2843,13 +2843,13 @@ class PolicyTests(unittest.TestCase):
self.assertIsInstance(policy, asyncio.DefaultEventLoopPolicy)
def test_event_loop_policy(self):
- policy = asyncio._AbstractEventLoopPolicy()
+ policy = asyncio.events._AbstractEventLoopPolicy()
self.assertRaises(NotImplementedError, policy.get_event_loop)
self.assertRaises(NotImplementedError, policy.set_event_loop, object())
self.assertRaises(NotImplementedError, policy.new_event_loop)
def test_get_event_loop(self):
- policy = asyncio._DefaultEventLoopPolicy()
+ policy = test_utils.DefaultEventLoopPolicy()
self.assertIsNone(policy._local._loop)
with self.assertRaises(RuntimeError):
@@ -2857,7 +2857,7 @@ class PolicyTests(unittest.TestCase):
self.assertIsNone(policy._local._loop)
def test_get_event_loop_does_not_call_set_event_loop(self):
- policy = asyncio._DefaultEventLoopPolicy()
+ policy = test_utils.DefaultEventLoopPolicy()
with mock.patch.object(
policy, "set_event_loop",
@@ -2869,7 +2869,7 @@ class PolicyTests(unittest.TestCase):
m_set_event_loop.assert_not_called()
def test_get_event_loop_after_set_none(self):
- policy = asyncio._DefaultEventLoopPolicy()
+ policy = test_utils.DefaultEventLoopPolicy()
policy.set_event_loop(None)
self.assertRaises(RuntimeError, policy.get_event_loop)
@@ -2877,7 +2877,7 @@ class PolicyTests(unittest.TestCase):
def test_get_event_loop_thread(self, m_current_thread):
def f():
- policy = asyncio._DefaultEventLoopPolicy()
+ policy = test_utils.DefaultEventLoopPolicy()
self.assertRaises(RuntimeError, policy.get_event_loop)
th = threading.Thread(target=f)
@@ -2885,14 +2885,14 @@ class PolicyTests(unittest.TestCase):
th.join()
def test_new_event_loop(self):
- policy = asyncio._DefaultEventLoopPolicy()
+ policy = test_utils.DefaultEventLoopPolicy()
loop = policy.new_event_loop()
self.assertIsInstance(loop, asyncio.AbstractEventLoop)
loop.close()
def test_set_event_loop(self):
- policy = asyncio._DefaultEventLoopPolicy()
+ policy = test_utils.DefaultEventLoopPolicy()
old_loop = policy.new_event_loop()
policy.set_event_loop(old_loop)
@@ -2909,7 +2909,7 @@ class PolicyTests(unittest.TestCase):
with self.assertWarnsRegex(
DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"):
policy = asyncio.get_event_loop_policy()
- self.assertIsInstance(policy, asyncio._AbstractEventLoopPolicy)
+ self.assertIsInstance(policy, asyncio.events._AbstractEventLoopPolicy)
self.assertIs(policy, asyncio.get_event_loop_policy())
def test_set_event_loop_policy(self):
@@ -2922,7 +2922,7 @@ class PolicyTests(unittest.TestCase):
DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"):
old_policy = asyncio.get_event_loop_policy()
- policy = asyncio._DefaultEventLoopPolicy()
+ policy = test_utils.DefaultEventLoopPolicy()
with self.assertWarnsRegex(
DeprecationWarning, "'asyncio.set_event_loop_policy' is deprecated"):
asyncio.set_event_loop_policy(policy)
@@ -3034,13 +3034,13 @@ class GetEventLoopTestsMixin:
class TestError(Exception):
pass
- class Policy(asyncio._DefaultEventLoopPolicy):
+ class Policy(test_utils.DefaultEventLoopPolicy):
def get_event_loop(self):
raise TestError
- old_policy = asyncio._get_event_loop_policy()
+ old_policy = asyncio.events._get_event_loop_policy()
try:
- asyncio._set_event_loop_policy(Policy())
+ asyncio.events._set_event_loop_policy(Policy())
loop = asyncio.new_event_loop()
with self.assertRaises(TestError):
@@ -3068,7 +3068,7 @@ class GetEventLoopTestsMixin:
asyncio.get_event_loop()
finally:
- asyncio._set_event_loop_policy(old_policy)
+ asyncio.events._set_event_loop_policy(old_policy)
if loop is not None:
loop.close()
@@ -3078,9 +3078,9 @@ class GetEventLoopTestsMixin:
self.assertIs(asyncio._get_running_loop(), None)
def test_get_event_loop_returns_running_loop2(self):
- old_policy = asyncio._get_event_loop_policy()
+ old_policy = asyncio.events._get_event_loop_policy()
try:
- asyncio._set_event_loop_policy(asyncio._DefaultEventLoopPolicy())
+ asyncio.events._set_event_loop_policy(test_utils.DefaultEventLoopPolicy())
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)
@@ -3106,7 +3106,7 @@ class GetEventLoopTestsMixin:
asyncio.get_event_loop()
finally:
- asyncio._set_event_loop_policy(old_policy)
+ asyncio.events._set_event_loop_policy(old_policy)
if loop is not None:
loop.close()
diff --git a/Lib/test/test_asyncio/test_free_threading.py b/Lib/test/test_asyncio/test_free_threading.py
index 110996c3485..d874ed00bd7 100644
--- a/Lib/test/test_asyncio/test_free_threading.py
+++ b/Lib/test/test_asyncio/test_free_threading.py
@@ -15,7 +15,7 @@ class MyException(Exception):
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class TestFreeThreading:
diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py
index 8b51522278a..666f9c9ee18 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -17,7 +17,7 @@ from test import support
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
def _fakefunc(f):
@@ -413,7 +413,7 @@ class BaseFutureTests:
def test_copy_state(self):
from asyncio.futures import _copy_future_state
- f = self._new_future(loop=self.loop)
+ f = concurrent.futures.Future()
f.set_result(10)
newf = self._new_future(loop=self.loop)
@@ -421,7 +421,7 @@ class BaseFutureTests:
self.assertTrue(newf.done())
self.assertEqual(newf.result(), 10)
- f_exception = self._new_future(loop=self.loop)
+ f_exception = concurrent.futures.Future()
f_exception.set_exception(RuntimeError())
newf_exception = self._new_future(loop=self.loop)
@@ -429,7 +429,7 @@ class BaseFutureTests:
self.assertTrue(newf_exception.done())
self.assertRaises(RuntimeError, newf_exception.result)
- f_cancelled = self._new_future(loop=self.loop)
+ f_cancelled = concurrent.futures.Future()
f_cancelled.cancel()
newf_cancelled = self._new_future(loop=self.loop)
@@ -441,7 +441,7 @@ class BaseFutureTests:
except BaseException as e:
f_exc = e
- f_conexc = self._new_future(loop=self.loop)
+ f_conexc = concurrent.futures.Future()
f_conexc.set_exception(f_exc)
newf_conexc = self._new_future(loop=self.loop)
@@ -454,6 +454,56 @@ class BaseFutureTests:
newf_tb = ''.join(traceback.format_tb(newf_exc.__traceback__))
self.assertEqual(newf_tb.count('raise concurrent.futures.InvalidStateError'), 1)
+ def test_copy_state_from_concurrent_futures(self):
+ """Test _copy_future_state from concurrent.futures.Future.
+
+ This tests the optimized path using _get_snapshot when available.
+ """
+ from asyncio.futures import _copy_future_state
+
+ # Test with a result
+ f_concurrent = concurrent.futures.Future()
+ f_concurrent.set_result(42)
+ f_asyncio = self._new_future(loop=self.loop)
+ _copy_future_state(f_concurrent, f_asyncio)
+ self.assertTrue(f_asyncio.done())
+ self.assertEqual(f_asyncio.result(), 42)
+
+ # Test with an exception
+ f_concurrent_exc = concurrent.futures.Future()
+ f_concurrent_exc.set_exception(ValueError("test exception"))
+ f_asyncio_exc = self._new_future(loop=self.loop)
+ _copy_future_state(f_concurrent_exc, f_asyncio_exc)
+ self.assertTrue(f_asyncio_exc.done())
+ with self.assertRaises(ValueError) as cm:
+ f_asyncio_exc.result()
+ self.assertEqual(str(cm.exception), "test exception")
+
+ # Test with cancelled state
+ f_concurrent_cancelled = concurrent.futures.Future()
+ f_concurrent_cancelled.cancel()
+ f_asyncio_cancelled = self._new_future(loop=self.loop)
+ _copy_future_state(f_concurrent_cancelled, f_asyncio_cancelled)
+ self.assertTrue(f_asyncio_cancelled.cancelled())
+
+ # Test that destination already cancelled prevents copy
+ f_concurrent_result = concurrent.futures.Future()
+ f_concurrent_result.set_result(10)
+ f_asyncio_precancelled = self._new_future(loop=self.loop)
+ f_asyncio_precancelled.cancel()
+ _copy_future_state(f_concurrent_result, f_asyncio_precancelled)
+ self.assertTrue(f_asyncio_precancelled.cancelled())
+
+ # Test exception type conversion
+ f_concurrent_invalid = concurrent.futures.Future()
+ f_concurrent_invalid.set_exception(concurrent.futures.InvalidStateError("invalid"))
+ f_asyncio_invalid = self._new_future(loop=self.loop)
+ _copy_future_state(f_concurrent_invalid, f_asyncio_invalid)
+ self.assertTrue(f_asyncio_invalid.done())
+ with self.assertRaises(asyncio.exceptions.InvalidStateError) as cm:
+ f_asyncio_invalid.result()
+ self.assertEqual(str(cm.exception), "invalid")
+
def test_iter(self):
fut = self._new_future(loop=self.loop)
diff --git a/Lib/test/test_asyncio/test_futures2.py b/Lib/test/test_asyncio/test_futures2.py
index e2cddea01ec..c7c0ebdac1b 100644
--- a/Lib/test/test_asyncio/test_futures2.py
+++ b/Lib/test/test_asyncio/test_futures2.py
@@ -7,7 +7,7 @@ from asyncio import tasks
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class FutureTests:
diff --git a/Lib/test/test_asyncio/test_graph.py b/Lib/test/test_asyncio/test_graph.py
index 62f6593c31d..2f22fbccba4 100644
--- a/Lib/test/test_asyncio/test_graph.py
+++ b/Lib/test/test_asyncio/test_graph.py
@@ -5,7 +5,7 @@ import unittest
# To prevent a warning "test altered the execution environment"
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
def capture_test_stack(*, fut=None, depth=1):
diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py
index 047f03cbb14..e025d2990a3 100644
--- a/Lib/test/test_asyncio/test_locks.py
+++ b/Lib/test/test_asyncio/test_locks.py
@@ -20,7 +20,7 @@ RGX_REPR = re.compile(STR_RGX_REPR)
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class LockTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py
index 48f4a75e0fd..a0c8434c945 100644
--- a/Lib/test/test_asyncio/test_pep492.py
+++ b/Lib/test/test_asyncio/test_pep492.py
@@ -11,7 +11,7 @@ from test.test_asyncio import utils as test_utils
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
# Test that asyncio.iscoroutine() uses collections.abc.Coroutine
diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py
index 24c4e8546b1..b25daaface0 100644
--- a/Lib/test/test_asyncio/test_proactor_events.py
+++ b/Lib/test/test_asyncio/test_proactor_events.py
@@ -18,7 +18,7 @@ from test.test_asyncio import utils as test_utils
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
def close_transport(transport):
diff --git a/Lib/test/test_asyncio/test_protocols.py b/Lib/test/test_asyncio/test_protocols.py
index 4484a031988..29d3bd22705 100644
--- a/Lib/test/test_asyncio/test_protocols.py
+++ b/Lib/test/test_asyncio/test_protocols.py
@@ -7,7 +7,7 @@ import asyncio
def tearDownModule():
# not needed for the test file but added for uniformness with all other
# asyncio test files for the sake of unified cleanup
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class ProtocolsAbsTests(unittest.TestCase):
diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py
index 090b9774c22..54bbe79f81f 100644
--- a/Lib/test/test_asyncio/test_queues.py
+++ b/Lib/test/test_asyncio/test_queues.py
@@ -6,7 +6,7 @@ from types import GenericAlias
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class QueueBasicTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_runners.py b/Lib/test/test_asyncio/test_runners.py
index 21f277bc2d8..8a4d7f5c796 100644
--- a/Lib/test/test_asyncio/test_runners.py
+++ b/Lib/test/test_asyncio/test_runners.py
@@ -12,14 +12,14 @@ from unittest.mock import patch
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
def interrupt_self():
_thread.interrupt_main()
-class TestPolicy(asyncio._AbstractEventLoopPolicy):
+class TestPolicy(asyncio.events._AbstractEventLoopPolicy):
def __init__(self, loop_factory):
self.loop_factory = loop_factory
@@ -61,15 +61,15 @@ class BaseTest(unittest.TestCase):
super().setUp()
policy = TestPolicy(self.new_loop)
- asyncio._set_event_loop_policy(policy)
+ asyncio.events._set_event_loop_policy(policy)
def tearDown(self):
- policy = asyncio._get_event_loop_policy()
+ policy = asyncio.events._get_event_loop_policy()
if policy.loop is not None:
self.assertTrue(policy.loop.is_closed())
self.assertTrue(policy.loop.shutdown_ag_run)
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
super().tearDown()
@@ -208,7 +208,7 @@ class RunTests(BaseTest):
await asyncio.sleep(0)
return 42
- policy = asyncio._get_event_loop_policy()
+ policy = asyncio.events._get_event_loop_policy()
policy.set_event_loop = mock.Mock()
asyncio.run(main())
self.assertTrue(policy.set_event_loop.called)
@@ -259,7 +259,7 @@ class RunTests(BaseTest):
loop.set_task_factory(Task)
return loop
- asyncio._set_event_loop_policy(TestPolicy(new_event_loop))
+ asyncio.events._set_event_loop_policy(TestPolicy(new_event_loop))
with self.assertRaises(asyncio.CancelledError):
asyncio.run(main())
@@ -495,7 +495,7 @@ class RunnerTests(BaseTest):
async def coro():
pass
- policy = asyncio._get_event_loop_policy()
+ policy = asyncio.events._get_event_loop_policy()
policy.set_event_loop = mock.Mock()
runner = asyncio.Runner()
runner.run(coro())
diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py
index de81936b745..7b6d1bce5e4 100644
--- a/Lib/test/test_asyncio/test_selector_events.py
+++ b/Lib/test/test_asyncio/test_selector_events.py
@@ -24,7 +24,7 @@ MOCK_ANY = mock.ANY
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class TestBaseSelectorEventLoop(BaseSelectorEventLoop):
@@ -347,6 +347,18 @@ class BaseSelectorEventLoopTests(test_utils.TestCase):
selectors.EVENT_WRITE)])
self.loop._remove_writer.assert_called_with(1)
+ def test_accept_connection_zero_one(self):
+ for backlog in [0, 1]:
+ sock = mock.Mock()
+ sock.accept.return_value = (mock.Mock(), mock.Mock())
+ with self.subTest(backlog):
+ mock_obj = mock.patch.object
+ with mock_obj(self.loop, '_accept_connection2') as accept2_mock:
+ self.loop._accept_connection(
+ mock.Mock(), sock, backlog=backlog)
+ self.loop.run_until_complete(asyncio.sleep(0))
+ self.assertEqual(sock.accept.call_count, backlog + 1)
+
def test_accept_connection_multiple(self):
sock = mock.Mock()
sock.accept.return_value = (mock.Mock(), mock.Mock())
@@ -362,7 +374,7 @@ class BaseSelectorEventLoopTests(test_utils.TestCase):
self.loop._accept_connection(
mock.Mock(), sock, backlog=backlog)
self.loop.run_until_complete(asyncio.sleep(0))
- self.assertEqual(sock.accept.call_count, backlog)
+ self.assertEqual(sock.accept.call_count, backlog + 1)
def test_accept_connection_skip_connectionabortederror(self):
sock = mock.Mock()
@@ -388,7 +400,7 @@ class BaseSelectorEventLoopTests(test_utils.TestCase):
# as in test_accept_connection_multiple avoid task pending
# warnings by using asyncio.sleep(0)
self.loop.run_until_complete(asyncio.sleep(0))
- self.assertEqual(sock.accept.call_count, backlog)
+ self.assertEqual(sock.accept.call_count, backlog + 1)
class SelectorTransportTests(test_utils.TestCase):
diff --git a/Lib/test/test_asyncio/test_sendfile.py b/Lib/test/test_asyncio/test_sendfile.py
index e1b766d06cb..dcd963b3355 100644
--- a/Lib/test/test_asyncio/test_sendfile.py
+++ b/Lib/test/test_asyncio/test_sendfile.py
@@ -22,7 +22,7 @@ except ImportError:
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class MySendfileProto(asyncio.Protocol):
diff --git a/Lib/test/test_asyncio/test_server.py b/Lib/test/test_asyncio/test_server.py
index 32211f4cba3..5bd0f7e2af4 100644
--- a/Lib/test/test_asyncio/test_server.py
+++ b/Lib/test/test_asyncio/test_server.py
@@ -11,7 +11,7 @@ from test.test_asyncio import functional as func_tests
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class BaseStartServer(func_tests.FunctionalTestCaseMixin):
diff --git a/Lib/test/test_asyncio/test_sock_lowlevel.py b/Lib/test/test_asyncio/test_sock_lowlevel.py
index 4f7b9a1dda6..df4ec794897 100644
--- a/Lib/test/test_asyncio/test_sock_lowlevel.py
+++ b/Lib/test/test_asyncio/test_sock_lowlevel.py
@@ -15,7 +15,7 @@ if socket_helper.tcp_blackhole():
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class MyProto(asyncio.Protocol):
diff --git a/Lib/test/test_asyncio/test_ssl.py b/Lib/test/test_asyncio/test_ssl.py
index 3a7185cd897..06118f3a615 100644
--- a/Lib/test/test_asyncio/test_ssl.py
+++ b/Lib/test/test_asyncio/test_ssl.py
@@ -30,7 +30,7 @@ BUF_MULTIPLIER = 1024 if not MACOS else 64
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class MyBaseProto(asyncio.Protocol):
diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py
index aa248c5786f..3e304c16642 100644
--- a/Lib/test/test_asyncio/test_sslproto.py
+++ b/Lib/test/test_asyncio/test_sslproto.py
@@ -21,7 +21,7 @@ from test.test_asyncio import functional as func_tests
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
@unittest.skipIf(ssl is None, 'No ssl module')
diff --git a/Lib/test/test_asyncio/test_staggered.py b/Lib/test/test_asyncio/test_staggered.py
index ad34aa6da01..32e4817b70d 100644
--- a/Lib/test/test_asyncio/test_staggered.py
+++ b/Lib/test/test_asyncio/test_staggered.py
@@ -8,7 +8,7 @@ support.requires_working_socket(module=True)
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class StaggeredTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py
index 4fa4384346f..f93ee54abc6 100644
--- a/Lib/test/test_asyncio/test_streams.py
+++ b/Lib/test/test_asyncio/test_streams.py
@@ -18,7 +18,7 @@ from test.support import socket_helper
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class StreamTests(test_utils.TestCase):
diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py
index 341e3e979e0..3a17c169c34 100644
--- a/Lib/test/test_asyncio/test_subprocess.py
+++ b/Lib/test/test_asyncio/test_subprocess.py
@@ -37,7 +37,7 @@ PROGRAM_CAT = [
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class TestSubprocessTransport(base_subprocess.BaseSubprocessTransport):
diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py
index 0d69a436fdb..91f6b03b459 100644
--- a/Lib/test/test_asyncio/test_taskgroups.py
+++ b/Lib/test/test_asyncio/test_taskgroups.py
@@ -15,7 +15,7 @@ from test.test_asyncio.utils import await_without_task
# To prevent a warning "test altered the execution environment"
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class MyExc(Exception):
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index 44498ef790e..931a43816a2 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -24,7 +24,7 @@ from test.support.warnings_helper import ignore_warnings
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
async def coroutine_function():
@@ -2116,6 +2116,46 @@ class BaseTaskTests:
self.assertTrue(outer.cancelled())
self.assertEqual(0, 0 if outer._callbacks is None else len(outer._callbacks))
+ def test_shield_cancel_outer_result(self):
+ mock_handler = mock.Mock()
+ self.loop.set_exception_handler(mock_handler)
+ inner = self.new_future(self.loop)
+ outer = asyncio.shield(inner)
+ test_utils.run_briefly(self.loop)
+ outer.cancel()
+ test_utils.run_briefly(self.loop)
+ inner.set_result(1)
+ test_utils.run_briefly(self.loop)
+ mock_handler.assert_not_called()
+
+ def test_shield_cancel_outer_exception(self):
+ mock_handler = mock.Mock()
+ self.loop.set_exception_handler(mock_handler)
+ inner = self.new_future(self.loop)
+ outer = asyncio.shield(inner)
+ test_utils.run_briefly(self.loop)
+ outer.cancel()
+ test_utils.run_briefly(self.loop)
+ inner.set_exception(Exception('foo'))
+ test_utils.run_briefly(self.loop)
+ mock_handler.assert_called_once()
+
+ def test_shield_duplicate_log_once(self):
+ mock_handler = mock.Mock()
+ self.loop.set_exception_handler(mock_handler)
+ inner = self.new_future(self.loop)
+ outer = asyncio.shield(inner)
+ test_utils.run_briefly(self.loop)
+ outer.cancel()
+ test_utils.run_briefly(self.loop)
+ outer = asyncio.shield(inner)
+ test_utils.run_briefly(self.loop)
+ outer.cancel()
+ test_utils.run_briefly(self.loop)
+ inner.set_exception(Exception('foo'))
+ test_utils.run_briefly(self.loop)
+ mock_handler.assert_called_once()
+
def test_shield_shortcut(self):
fut = self.new_future(self.loop)
fut.set_result(42)
diff --git a/Lib/test/test_asyncio/test_threads.py b/Lib/test/test_asyncio/test_threads.py
index c98c9a9b395..8ad5f9b2c9e 100644
--- a/Lib/test/test_asyncio/test_threads.py
+++ b/Lib/test/test_asyncio/test_threads.py
@@ -8,7 +8,7 @@ from unittest import mock
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class ToThreadTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_timeouts.py b/Lib/test/test_asyncio/test_timeouts.py
index 3ba84d63b2c..f60722c48b7 100644
--- a/Lib/test/test_asyncio/test_timeouts.py
+++ b/Lib/test/test_asyncio/test_timeouts.py
@@ -9,7 +9,7 @@ from test.test_asyncio.utils import await_without_task
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class TimeoutTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_tools.py b/Lib/test/test_asyncio/test_tools.py
index ba36e759ccd..34e94830204 100644
--- a/Lib/test/test_asyncio/test_tools.py
+++ b/Lib/test/test_asyncio/test_tools.py
@@ -2,6 +2,13 @@ import unittest
from asyncio import tools
+from collections import namedtuple
+
+FrameInfo = namedtuple('FrameInfo', ['funcname', 'filename', 'lineno'])
+CoroInfo = namedtuple('CoroInfo', ['call_stack', 'task_name'])
+TaskInfo = namedtuple('TaskInfo', ['task_id', 'task_name', 'coroutine_stack', 'awaited_by'])
+AwaitedInfo = namedtuple('AwaitedInfo', ['thread_id', 'awaited_by'])
+
# mock output of get_all_awaited_by function.
TEST_INPUTS_TREE = [
@@ -10,81 +17,151 @@ TEST_INPUTS_TREE = [
# different subtasks part of a TaskGroup (root1 and root2) which call
# awaiter functions.
(
- (
- 1,
- [
- (2, "Task-1", []),
- (
- 3,
- "timer",
- [
- [[("awaiter3", "/path/to/app.py", 130),
- ("awaiter2", "/path/to/app.py", 120),
- ("awaiter", "/path/to/app.py", 110)], 4],
- [[("awaiterB3", "/path/to/app.py", 190),
- ("awaiterB2", "/path/to/app.py", 180),
- ("awaiterB", "/path/to/app.py", 170)], 5],
- [[("awaiterB3", "/path/to/app.py", 190),
- ("awaiterB2", "/path/to/app.py", 180),
- ("awaiterB", "/path/to/app.py", 170)], 6],
- [[("awaiter3", "/path/to/app.py", 130),
- ("awaiter2", "/path/to/app.py", 120),
- ("awaiter", "/path/to/app.py", 110)], 7],
- ],
- ),
- (
- 8,
- "root1",
- [[["_aexit", "__aexit__", "main"], 2]],
- ),
- (
- 9,
- "root2",
- [[["_aexit", "__aexit__", "main"], 2]],
- ),
- (
- 4,
- "child1_1",
- [
- [
- ["_aexit", "__aexit__", "blocho_caller", "bloch"],
- 8,
- ]
- ],
- ),
- (
- 6,
- "child2_1",
- [
- [
- ["_aexit", "__aexit__", "blocho_caller", "bloch"],
- 8,
- ]
- ],
- ),
- (
- 7,
- "child1_2",
- [
- [
- ["_aexit", "__aexit__", "blocho_caller", "bloch"],
- 9,
- ]
- ],
- ),
- (
- 5,
- "child2_2",
- [
- [
- ["_aexit", "__aexit__", "blocho_caller", "bloch"],
- 9,
- ]
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
),
- ],
+ TaskInfo(
+ task_id=3,
+ task_name="timer",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("awaiter3", "/path/to/app.py", 130),
+ FrameInfo("awaiter2", "/path/to/app.py", 120),
+ FrameInfo("awaiter", "/path/to/app.py", 110)
+ ],
+ task_name=4
+ ),
+ CoroInfo(
+ call_stack=[
+ FrameInfo("awaiterB3", "/path/to/app.py", 190),
+ FrameInfo("awaiterB2", "/path/to/app.py", 180),
+ FrameInfo("awaiterB", "/path/to/app.py", 170)
+ ],
+ task_name=5
+ ),
+ CoroInfo(
+ call_stack=[
+ FrameInfo("awaiterB3", "/path/to/app.py", 190),
+ FrameInfo("awaiterB2", "/path/to/app.py", 180),
+ FrameInfo("awaiterB", "/path/to/app.py", 170)
+ ],
+ task_name=6
+ ),
+ CoroInfo(
+ call_stack=[
+ FrameInfo("awaiter3", "/path/to/app.py", 130),
+ FrameInfo("awaiter2", "/path/to/app.py", 120),
+ FrameInfo("awaiter", "/path/to/app.py", 110)
+ ],
+ task_name=7
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=8,
+ task_name="root1",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("main", "", 0)
+ ],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=9,
+ task_name="root2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("main", "", 0)
+ ],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=4,
+ task_name="child1_1",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("blocho_caller", "", 0),
+ FrameInfo("bloch", "", 0)
+ ],
+ task_name=8
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=6,
+ task_name="child2_1",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("blocho_caller", "", 0),
+ FrameInfo("bloch", "", 0)
+ ],
+ task_name=8
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=7,
+ task_name="child1_2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("blocho_caller", "", 0),
+ FrameInfo("bloch", "", 0)
+ ],
+ task_name=9
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=5,
+ task_name="child2_2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("blocho_caller", "", 0),
+ FrameInfo("bloch", "", 0)
+ ],
+ task_name=9
+ )
+ ]
+ )
+ ]
),
- (0, []),
+ AwaitedInfo(thread_id=0, awaited_by=[])
),
(
[
@@ -130,26 +207,96 @@ TEST_INPUTS_TREE = [
[
# test case containing two roots
(
- (
- 9,
- [
- (5, "Task-5", []),
- (6, "Task-6", [[["main2"], 5]]),
- (7, "Task-7", [[["main2"], 5]]),
- (8, "Task-8", [[["main2"], 5]]),
- ],
+ AwaitedInfo(
+ thread_id=9,
+ awaited_by=[
+ TaskInfo(
+ task_id=5,
+ task_name="Task-5",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=6,
+ task_name="Task-6",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main2", "", 0)],
+ task_name=5
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=7,
+ task_name="Task-7",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main2", "", 0)],
+ task_name=5
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=8,
+ task_name="Task-8",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main2", "", 0)],
+ task_name=5
+ )
+ ]
+ )
+ ]
),
- (
- 10,
- [
- (1, "Task-1", []),
- (2, "Task-2", [[["main"], 1]]),
- (3, "Task-3", [[["main"], 1]]),
- (4, "Task-4", [[["main"], 1]]),
- ],
+ AwaitedInfo(
+ thread_id=10,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=2,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=1
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=3,
+ task_name="Task-3",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=1
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=4,
+ task_name="Task-4",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=1
+ )
+ ]
+ )
+ ]
),
- (11, []),
- (0, []),
+ AwaitedInfo(thread_id=11, awaited_by=[]),
+ AwaitedInfo(thread_id=0, awaited_by=[])
),
(
[
@@ -174,18 +321,63 @@ TEST_INPUTS_TREE = [
# test case containing two roots, one of them without subtasks
(
[
- (1, [(2, "Task-5", [])]),
- (
- 3,
- [
- (4, "Task-1", []),
- (5, "Task-2", [[["main"], 4]]),
- (6, "Task-3", [[["main"], 4]]),
- (7, "Task-4", [[["main"], 4]]),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-5",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
),
- (8, []),
- (0, []),
+ AwaitedInfo(
+ thread_id=3,
+ awaited_by=[
+ TaskInfo(
+ task_id=4,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=5,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=4
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=6,
+ task_name="Task-3",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=4
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=7,
+ task_name="Task-4",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=4
+ )
+ ]
+ )
+ ]
+ ),
+ AwaitedInfo(thread_id=8, awaited_by=[]),
+ AwaitedInfo(thread_id=0, awaited_by=[])
]
),
(
@@ -208,19 +400,44 @@ TEST_INPUTS_CYCLES_TREE = [
# this test case contains a cycle: two tasks awaiting each other.
(
[
- (
- 1,
- [
- (2, "Task-1", []),
- (
- 3,
- "a",
- [[["awaiter2"], 4], [["main"], 2]],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
),
- (4, "b", [[["awaiter"], 3]]),
- ],
+ TaskInfo(
+ task_id=3,
+ task_name="a",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("awaiter2", "", 0)],
+ task_name=4
+ ),
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=4,
+ task_name="b",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("awaiter", "", 0)],
+ task_name=3
+ )
+ ]
+ )
+ ]
),
- (0, []),
+ AwaitedInfo(thread_id=0, awaited_by=[])
]
),
([[4, 3, 4]]),
@@ -229,32 +446,85 @@ TEST_INPUTS_CYCLES_TREE = [
# this test case contains two cycles
(
[
- (
- 1,
- [
- (2, "Task-1", []),
- (
- 3,
- "A",
- [[["nested", "nested", "task_b"], 4]],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
),
- (
- 4,
- "B",
- [
- [["nested", "nested", "task_c"], 5],
- [["nested", "nested", "task_a"], 3],
- ],
+ TaskInfo(
+ task_id=3,
+ task_name="A",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0),
+ FrameInfo("task_b", "", 0)
+ ],
+ task_name=4
+ )
+ ]
),
- (5, "C", [[["nested", "nested"], 6]]),
- (
- 6,
- "Task-2",
- [[["nested", "nested", "task_b"], 4]],
+ TaskInfo(
+ task_id=4,
+ task_name="B",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0),
+ FrameInfo("task_c", "", 0)
+ ],
+ task_name=5
+ ),
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0),
+ FrameInfo("task_a", "", 0)
+ ],
+ task_name=3
+ )
+ ]
),
- ],
+ TaskInfo(
+ task_id=5,
+ task_name="C",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0)
+ ],
+ task_name=6
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=6,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0),
+ FrameInfo("task_b", "", 0)
+ ],
+ task_name=4
+ )
+ ]
+ )
+ ]
),
- (0, []),
+ AwaitedInfo(thread_id=0, awaited_by=[])
]
),
([[4, 3, 4], [4, 6, 5, 4]]),
@@ -267,81 +537,160 @@ TEST_INPUTS_TABLE = [
# different subtasks part of a TaskGroup (root1 and root2) which call
# awaiter functions.
(
- (
- 1,
- [
- (2, "Task-1", []),
- (
- 3,
- "timer",
- [
- [["awaiter3", "awaiter2", "awaiter"], 4],
- [["awaiter1_3", "awaiter1_2", "awaiter1"], 5],
- [["awaiter1_3", "awaiter1_2", "awaiter1"], 6],
- [["awaiter3", "awaiter2", "awaiter"], 7],
- ],
- ),
- (
- 8,
- "root1",
- [[["_aexit", "__aexit__", "main"], 2]],
- ),
- (
- 9,
- "root2",
- [[["_aexit", "__aexit__", "main"], 2]],
- ),
- (
- 4,
- "child1_1",
- [
- [
- ["_aexit", "__aexit__", "blocho_caller", "bloch"],
- 8,
- ]
- ],
- ),
- (
- 6,
- "child2_1",
- [
- [
- ["_aexit", "__aexit__", "blocho_caller", "bloch"],
- 8,
- ]
- ],
- ),
- (
- 7,
- "child1_2",
- [
- [
- ["_aexit", "__aexit__", "blocho_caller", "bloch"],
- 9,
- ]
- ],
- ),
- (
- 5,
- "child2_2",
- [
- [
- ["_aexit", "__aexit__", "blocho_caller", "bloch"],
- 9,
- ]
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
),
- ],
+ TaskInfo(
+ task_id=3,
+ task_name="timer",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("awaiter3", "", 0),
+ FrameInfo("awaiter2", "", 0),
+ FrameInfo("awaiter", "", 0)
+ ],
+ task_name=4
+ ),
+ CoroInfo(
+ call_stack=[
+ FrameInfo("awaiter1_3", "", 0),
+ FrameInfo("awaiter1_2", "", 0),
+ FrameInfo("awaiter1", "", 0)
+ ],
+ task_name=5
+ ),
+ CoroInfo(
+ call_stack=[
+ FrameInfo("awaiter1_3", "", 0),
+ FrameInfo("awaiter1_2", "", 0),
+ FrameInfo("awaiter1", "", 0)
+ ],
+ task_name=6
+ ),
+ CoroInfo(
+ call_stack=[
+ FrameInfo("awaiter3", "", 0),
+ FrameInfo("awaiter2", "", 0),
+ FrameInfo("awaiter", "", 0)
+ ],
+ task_name=7
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=8,
+ task_name="root1",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("main", "", 0)
+ ],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=9,
+ task_name="root2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("main", "", 0)
+ ],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=4,
+ task_name="child1_1",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("blocho_caller", "", 0),
+ FrameInfo("bloch", "", 0)
+ ],
+ task_name=8
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=6,
+ task_name="child2_1",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("blocho_caller", "", 0),
+ FrameInfo("bloch", "", 0)
+ ],
+ task_name=8
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=7,
+ task_name="child1_2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("blocho_caller", "", 0),
+ FrameInfo("bloch", "", 0)
+ ],
+ task_name=9
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=5,
+ task_name="child2_2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("_aexit", "", 0),
+ FrameInfo("__aexit__", "", 0),
+ FrameInfo("blocho_caller", "", 0),
+ FrameInfo("bloch", "", 0)
+ ],
+ task_name=9
+ )
+ ]
+ )
+ ]
),
- (0, []),
+ AwaitedInfo(thread_id=0, awaited_by=[])
),
(
[
- [1, "0x2", "Task-1", "", "", "0x0"],
+ [1, "0x2", "Task-1", "", "", "", "0x0"],
[
1,
"0x3",
"timer",
+ "",
"awaiter3 -> awaiter2 -> awaiter",
"child1_1",
"0x4",
@@ -350,6 +699,7 @@ TEST_INPUTS_TABLE = [
1,
"0x3",
"timer",
+ "",
"awaiter1_3 -> awaiter1_2 -> awaiter1",
"child2_2",
"0x5",
@@ -358,6 +708,7 @@ TEST_INPUTS_TABLE = [
1,
"0x3",
"timer",
+ "",
"awaiter1_3 -> awaiter1_2 -> awaiter1",
"child2_1",
"0x6",
@@ -366,6 +717,7 @@ TEST_INPUTS_TABLE = [
1,
"0x3",
"timer",
+ "",
"awaiter3 -> awaiter2 -> awaiter",
"child1_2",
"0x7",
@@ -374,6 +726,7 @@ TEST_INPUTS_TABLE = [
1,
"0x8",
"root1",
+ "",
"_aexit -> __aexit__ -> main",
"Task-1",
"0x2",
@@ -382,6 +735,7 @@ TEST_INPUTS_TABLE = [
1,
"0x9",
"root2",
+ "",
"_aexit -> __aexit__ -> main",
"Task-1",
"0x2",
@@ -390,6 +744,7 @@ TEST_INPUTS_TABLE = [
1,
"0x4",
"child1_1",
+ "",
"_aexit -> __aexit__ -> blocho_caller -> bloch",
"root1",
"0x8",
@@ -398,6 +753,7 @@ TEST_INPUTS_TABLE = [
1,
"0x6",
"child2_1",
+ "",
"_aexit -> __aexit__ -> blocho_caller -> bloch",
"root1",
"0x8",
@@ -406,6 +762,7 @@ TEST_INPUTS_TABLE = [
1,
"0x7",
"child1_2",
+ "",
"_aexit -> __aexit__ -> blocho_caller -> bloch",
"root2",
"0x9",
@@ -414,6 +771,7 @@ TEST_INPUTS_TABLE = [
1,
"0x5",
"child2_2",
+ "",
"_aexit -> __aexit__ -> blocho_caller -> bloch",
"root2",
"0x9",
@@ -424,37 +782,107 @@ TEST_INPUTS_TABLE = [
[
# test case containing two roots
(
- (
- 9,
- [
- (5, "Task-5", []),
- (6, "Task-6", [[["main2"], 5]]),
- (7, "Task-7", [[["main2"], 5]]),
- (8, "Task-8", [[["main2"], 5]]),
- ],
+ AwaitedInfo(
+ thread_id=9,
+ awaited_by=[
+ TaskInfo(
+ task_id=5,
+ task_name="Task-5",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=6,
+ task_name="Task-6",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main2", "", 0)],
+ task_name=5
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=7,
+ task_name="Task-7",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main2", "", 0)],
+ task_name=5
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=8,
+ task_name="Task-8",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main2", "", 0)],
+ task_name=5
+ )
+ ]
+ )
+ ]
),
- (
- 10,
- [
- (1, "Task-1", []),
- (2, "Task-2", [[["main"], 1]]),
- (3, "Task-3", [[["main"], 1]]),
- (4, "Task-4", [[["main"], 1]]),
- ],
+ AwaitedInfo(
+ thread_id=10,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=2,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=1
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=3,
+ task_name="Task-3",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=1
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=4,
+ task_name="Task-4",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=1
+ )
+ ]
+ )
+ ]
),
- (11, []),
- (0, []),
+ AwaitedInfo(thread_id=11, awaited_by=[]),
+ AwaitedInfo(thread_id=0, awaited_by=[])
),
(
[
- [9, "0x5", "Task-5", "", "", "0x0"],
- [9, "0x6", "Task-6", "main2", "Task-5", "0x5"],
- [9, "0x7", "Task-7", "main2", "Task-5", "0x5"],
- [9, "0x8", "Task-8", "main2", "Task-5", "0x5"],
- [10, "0x1", "Task-1", "", "", "0x0"],
- [10, "0x2", "Task-2", "main", "Task-1", "0x1"],
- [10, "0x3", "Task-3", "main", "Task-1", "0x1"],
- [10, "0x4", "Task-4", "main", "Task-1", "0x1"],
+ [9, "0x5", "Task-5", "", "", "", "0x0"],
+ [9, "0x6", "Task-6", "", "main2", "Task-5", "0x5"],
+ [9, "0x7", "Task-7", "", "main2", "Task-5", "0x5"],
+ [9, "0x8", "Task-8", "", "main2", "Task-5", "0x5"],
+ [10, "0x1", "Task-1", "", "", "", "0x0"],
+ [10, "0x2", "Task-2", "", "main", "Task-1", "0x1"],
+ [10, "0x3", "Task-3", "", "main", "Task-1", "0x1"],
+ [10, "0x4", "Task-4", "", "main", "Task-1", "0x1"],
]
),
],
@@ -462,27 +890,72 @@ TEST_INPUTS_TABLE = [
# test case containing two roots, one of them without subtasks
(
[
- (1, [(2, "Task-5", [])]),
- (
- 3,
- [
- (4, "Task-1", []),
- (5, "Task-2", [[["main"], 4]]),
- (6, "Task-3", [[["main"], 4]]),
- (7, "Task-4", [[["main"], 4]]),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-5",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
),
- (8, []),
- (0, []),
+ AwaitedInfo(
+ thread_id=3,
+ awaited_by=[
+ TaskInfo(
+ task_id=4,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=5,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=4
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=6,
+ task_name="Task-3",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=4
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=7,
+ task_name="Task-4",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=4
+ )
+ ]
+ )
+ ]
+ ),
+ AwaitedInfo(thread_id=8, awaited_by=[]),
+ AwaitedInfo(thread_id=0, awaited_by=[])
]
),
(
[
- [1, "0x2", "Task-5", "", "", "0x0"],
- [3, "0x4", "Task-1", "", "", "0x0"],
- [3, "0x5", "Task-2", "main", "Task-1", "0x4"],
- [3, "0x6", "Task-3", "main", "Task-1", "0x4"],
- [3, "0x7", "Task-4", "main", "Task-1", "0x4"],
+ [1, "0x2", "Task-5", "", "", "", "0x0"],
+ [3, "0x4", "Task-1", "", "", "", "0x0"],
+ [3, "0x5", "Task-2", "", "main", "Task-1", "0x4"],
+ [3, "0x6", "Task-3", "", "main", "Task-1", "0x4"],
+ [3, "0x7", "Task-4", "", "main", "Task-1", "0x4"],
]
),
],
@@ -491,27 +964,52 @@ TEST_INPUTS_TABLE = [
# this test case contains a cycle: two tasks awaiting each other.
(
[
- (
- 1,
- [
- (2, "Task-1", []),
- (
- 3,
- "a",
- [[["awaiter2"], 4], [["main"], 2]],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
),
- (4, "b", [[["awaiter"], 3]]),
- ],
+ TaskInfo(
+ task_id=3,
+ task_name="a",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("awaiter2", "", 0)],
+ task_name=4
+ ),
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=4,
+ task_name="b",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("awaiter", "", 0)],
+ task_name=3
+ )
+ ]
+ )
+ ]
),
- (0, []),
+ AwaitedInfo(thread_id=0, awaited_by=[])
]
),
(
[
- [1, "0x2", "Task-1", "", "", "0x0"],
- [1, "0x3", "a", "awaiter2", "b", "0x4"],
- [1, "0x3", "a", "main", "Task-1", "0x2"],
- [1, "0x4", "b", "awaiter", "a", "0x3"],
+ [1, "0x2", "Task-1", "", "", "", "0x0"],
+ [1, "0x3", "a", "", "awaiter2", "b", "0x4"],
+ [1, "0x3", "a", "", "main", "Task-1", "0x2"],
+ [1, "0x4", "b", "", "awaiter", "a", "0x3"],
]
),
],
@@ -519,41 +1017,95 @@ TEST_INPUTS_TABLE = [
# this test case contains two cycles
(
[
- (
- 1,
- [
- (2, "Task-1", []),
- (
- 3,
- "A",
- [[["nested", "nested", "task_b"], 4]],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=3,
+ task_name="A",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0),
+ FrameInfo("task_b", "", 0)
+ ],
+ task_name=4
+ )
+ ]
),
- (
- 4,
- "B",
- [
- [["nested", "nested", "task_c"], 5],
- [["nested", "nested", "task_a"], 3],
- ],
+ TaskInfo(
+ task_id=4,
+ task_name="B",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0),
+ FrameInfo("task_c", "", 0)
+ ],
+ task_name=5
+ ),
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0),
+ FrameInfo("task_a", "", 0)
+ ],
+ task_name=3
+ )
+ ]
),
- (5, "C", [[["nested", "nested"], 6]]),
- (
- 6,
- "Task-2",
- [[["nested", "nested", "task_b"], 4]],
+ TaskInfo(
+ task_id=5,
+ task_name="C",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0)
+ ],
+ task_name=6
+ )
+ ]
),
- ],
+ TaskInfo(
+ task_id=6,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("nested", "", 0),
+ FrameInfo("nested", "", 0),
+ FrameInfo("task_b", "", 0)
+ ],
+ task_name=4
+ )
+ ]
+ )
+ ]
),
- (0, []),
+ AwaitedInfo(thread_id=0, awaited_by=[])
]
),
(
[
- [1, "0x2", "Task-1", "", "", "0x0"],
+ [1, "0x2", "Task-1", "", "", "", "0x0"],
[
1,
"0x3",
"A",
+ "",
"nested -> nested -> task_b",
"B",
"0x4",
@@ -562,6 +1114,7 @@ TEST_INPUTS_TABLE = [
1,
"0x4",
"B",
+ "",
"nested -> nested -> task_c",
"C",
"0x5",
@@ -570,6 +1123,7 @@ TEST_INPUTS_TABLE = [
1,
"0x4",
"B",
+ "",
"nested -> nested -> task_a",
"A",
"0x3",
@@ -578,6 +1132,7 @@ TEST_INPUTS_TABLE = [
1,
"0x5",
"C",
+ "",
"nested -> nested",
"Task-2",
"0x6",
@@ -586,6 +1141,7 @@ TEST_INPUTS_TABLE = [
1,
"0x6",
"Task-2",
+ "",
"nested -> nested -> task_b",
"B",
"0x4",
@@ -600,7 +1156,8 @@ class TestAsyncioToolsTree(unittest.TestCase):
def test_asyncio_utils(self):
for input_, tree in TEST_INPUTS_TREE:
with self.subTest(input_):
- self.assertEqual(tools.build_async_tree(input_), tree)
+ result = tools.build_async_tree(input_)
+ self.assertEqual(result, tree)
def test_asyncio_utils_cycles(self):
for input_, cycles in TEST_INPUTS_CYCLES_TREE:
@@ -615,7 +1172,8 @@ class TestAsyncioToolsTable(unittest.TestCase):
def test_asyncio_utils(self):
for input_, table in TEST_INPUTS_TABLE:
with self.subTest(input_):
- self.assertEqual(tools.build_task_table(input_), table)
+ result = tools.build_task_table(input_)
+ self.assertEqual(result, table)
class TestAsyncioToolsBasic(unittest.TestCase):
@@ -632,26 +1190,67 @@ class TestAsyncioToolsBasic(unittest.TestCase):
self.assertEqual(tools.build_task_table(result), expected_output)
def test_only_independent_tasks_tree(self):
- input_ = [(1, [(10, "taskA", []), (11, "taskB", [])])]
+ input_ = [
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=10,
+ task_name="taskA",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=11,
+ task_name="taskB",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
+ )
+ ]
expected = [["└── (T) taskA"], ["└── (T) taskB"]]
result = tools.build_async_tree(input_)
self.assertEqual(sorted(result), sorted(expected))
def test_only_independent_tasks_table(self):
- input_ = [(1, [(10, "taskA", []), (11, "taskB", [])])]
+ input_ = [
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=10,
+ task_name="taskA",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=11,
+ task_name="taskB",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
+ )
+ ]
self.assertEqual(
tools.build_task_table(input_),
- [[1, "0xa", "taskA", "", "", "0x0"], [1, "0xb", "taskB", "", "", "0x0"]],
+ [[1, '0xa', 'taskA', '', '', '', '0x0'], [1, '0xb', 'taskB', '', '', '', '0x0']]
)
def test_single_task_tree(self):
"""Test build_async_tree with a single task and no awaits."""
result = [
- (
- 1,
- [
- (2, "Task-1", []),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
)
]
expected_output = [
@@ -664,25 +1263,50 @@ class TestAsyncioToolsBasic(unittest.TestCase):
def test_single_task_table(self):
"""Test build_task_table with a single task and no awaits."""
result = [
- (
- 1,
- [
- (2, "Task-1", []),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
)
]
- expected_output = [[1, "0x2", "Task-1", "", "", "0x0"]]
+ expected_output = [[1, '0x2', 'Task-1', '', '', '', '0x0']]
self.assertEqual(tools.build_task_table(result), expected_output)
def test_cycle_detection(self):
"""Test build_async_tree raises CycleFoundException for cyclic input."""
result = [
- (
- 1,
- [
- (2, "Task-1", [[["main"], 3]]),
- (3, "Task-2", [[["main"], 2]]),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=3
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=3,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=2
+ )
+ ]
+ )
+ ]
)
]
with self.assertRaises(tools.CycleFoundException) as context:
@@ -692,13 +1316,38 @@ class TestAsyncioToolsBasic(unittest.TestCase):
def test_complex_tree(self):
"""Test build_async_tree with a more complex tree structure."""
result = [
- (
- 1,
- [
- (2, "Task-1", []),
- (3, "Task-2", [[["main"], 2]]),
- (4, "Task-3", [[["main"], 3]]),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=3,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=4,
+ task_name="Task-3",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=3
+ )
+ ]
+ )
+ ]
)
]
expected_output = [
@@ -715,30 +1364,76 @@ class TestAsyncioToolsBasic(unittest.TestCase):
def test_complex_table(self):
"""Test build_task_table with a more complex tree structure."""
result = [
- (
- 1,
- [
- (2, "Task-1", []),
- (3, "Task-2", [[["main"], 2]]),
- (4, "Task-3", [[["main"], 3]]),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=2,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=3,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=4,
+ task_name="Task-3",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("main", "", 0)],
+ task_name=3
+ )
+ ]
+ )
+ ]
)
]
expected_output = [
- [1, "0x2", "Task-1", "", "", "0x0"],
- [1, "0x3", "Task-2", "main", "Task-1", "0x2"],
- [1, "0x4", "Task-3", "main", "Task-2", "0x3"],
+ [1, '0x2', 'Task-1', '', '', '', '0x0'],
+ [1, '0x3', 'Task-2', '', 'main', 'Task-1', '0x2'],
+ [1, '0x4', 'Task-3', '', 'main', 'Task-2', '0x3']
]
self.assertEqual(tools.build_task_table(result), expected_output)
def test_deep_coroutine_chain(self):
input_ = [
- (
- 1,
- [
- (10, "leaf", [[["c1", "c2", "c3", "c4", "c5"], 11]]),
- (11, "root", []),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=10,
+ task_name="leaf",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("c1", "", 0),
+ FrameInfo("c2", "", 0),
+ FrameInfo("c3", "", 0),
+ FrameInfo("c4", "", 0),
+ FrameInfo("c5", "", 0)
+ ],
+ task_name=11
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=11,
+ task_name="root",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
)
]
expected = [
@@ -757,13 +1452,47 @@ class TestAsyncioToolsBasic(unittest.TestCase):
def test_multiple_cycles_same_node(self):
input_ = [
- (
- 1,
- [
- (1, "Task-A", [[["call1"], 2]]),
- (2, "Task-B", [[["call2"], 3]]),
- (3, "Task-C", [[["call3"], 1], [["call4"], 2]]),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="Task-A",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("call1", "", 0)],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=2,
+ task_name="Task-B",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("call2", "", 0)],
+ task_name=3
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=3,
+ task_name="Task-C",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("call3", "", 0)],
+ task_name=1
+ ),
+ CoroInfo(
+ call_stack=[FrameInfo("call4", "", 0)],
+ task_name=2
+ )
+ ]
+ )
+ ]
)
]
with self.assertRaises(tools.CycleFoundException) as ctx:
@@ -772,19 +1501,43 @@ class TestAsyncioToolsBasic(unittest.TestCase):
self.assertTrue(any(set(c) == {1, 2, 3} for c in cycles))
def test_table_output_format(self):
- input_ = [(1, [(1, "Task-A", [[["foo"], 2]]), (2, "Task-B", [])])]
+ input_ = [
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="Task-A",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("foo", "", 0)],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=2,
+ task_name="Task-B",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
+ )
+ ]
table = tools.build_task_table(input_)
for row in table:
- self.assertEqual(len(row), 6)
+ self.assertEqual(len(row), 7)
self.assertIsInstance(row[0], int) # thread ID
self.assertTrue(
isinstance(row[1], str) and row[1].startswith("0x")
) # hex task ID
self.assertIsInstance(row[2], str) # task name
- self.assertIsInstance(row[3], str) # coroutine chain
- self.assertIsInstance(row[4], str) # awaiter name
+ self.assertIsInstance(row[3], str) # coroutine stack
+ self.assertIsInstance(row[4], str) # coroutine chain
+ self.assertIsInstance(row[5], str) # awaiter name
self.assertTrue(
- isinstance(row[5], str) and row[5].startswith("0x")
+ isinstance(row[6], str) and row[6].startswith("0x")
) # hex awaiter ID
@@ -792,28 +1545,86 @@ class TestAsyncioToolsEdgeCases(unittest.TestCase):
def test_task_awaits_self(self):
"""A task directly awaits itself - should raise a cycle."""
- input_ = [(1, [(1, "Self-Awaiter", [[["loopback"], 1]])])]
+ input_ = [
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="Self-Awaiter",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("loopback", "", 0)],
+ task_name=1
+ )
+ ]
+ )
+ ]
+ )
+ ]
with self.assertRaises(tools.CycleFoundException) as ctx:
tools.build_async_tree(input_)
self.assertIn([1, 1], ctx.exception.cycles)
def test_task_with_missing_awaiter_id(self):
"""Awaiter ID not in task list - should not crash, just show 'Unknown'."""
- input_ = [(1, [(1, "Task-A", [[["coro"], 999]])])] # 999 not defined
+ input_ = [
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="Task-A",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("coro", "", 0)],
+ task_name=999
+ )
+ ]
+ )
+ ]
+ )
+ ]
table = tools.build_task_table(input_)
self.assertEqual(len(table), 1)
- self.assertEqual(table[0][4], "Unknown")
+ self.assertEqual(table[0][5], "Unknown")
def test_duplicate_coroutine_frames(self):
"""Same coroutine frame repeated under a parent - should deduplicate."""
input_ = [
- (
- 1,
- [
- (1, "Task-1", [[["frameA"], 2], [["frameA"], 3]]),
- (2, "Task-2", []),
- (3, "Task-3", []),
- ],
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="Task-1",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("frameA", "", 0)],
+ task_name=2
+ ),
+ CoroInfo(
+ call_stack=[FrameInfo("frameA", "", 0)],
+ task_name=3
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=2,
+ task_name="Task-2",
+ coroutine_stack=[],
+ awaited_by=[]
+ ),
+ TaskInfo(
+ task_id=3,
+ task_name="Task-3",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
)
]
tree = tools.build_async_tree(input_)
@@ -830,14 +1641,63 @@ class TestAsyncioToolsEdgeCases(unittest.TestCase):
def test_task_with_no_name(self):
"""Task with no name in id2name - should still render with fallback."""
- input_ = [(1, [(1, "root", [[["f1"], 2]]), (2, None, [])])]
+ input_ = [
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="root",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[FrameInfo("f1", "", 0)],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=2,
+ task_name=None,
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
+ )
+ ]
# If name is None, fallback to string should not crash
tree = tools.build_async_tree(input_)
self.assertIn("(T) None", "\n".join(tree[0]))
def test_tree_rendering_with_custom_emojis(self):
"""Pass custom emojis to the tree renderer."""
- input_ = [(1, [(1, "MainTask", [[["f1", "f2"], 2]]), (2, "SubTask", [])])]
+ input_ = [
+ AwaitedInfo(
+ thread_id=1,
+ awaited_by=[
+ TaskInfo(
+ task_id=1,
+ task_name="MainTask",
+ coroutine_stack=[],
+ awaited_by=[
+ CoroInfo(
+ call_stack=[
+ FrameInfo("f1", "", 0),
+ FrameInfo("f2", "", 0)
+ ],
+ task_name=2
+ )
+ ]
+ ),
+ TaskInfo(
+ task_id=2,
+ task_name="SubTask",
+ coroutine_stack=[],
+ awaited_by=[]
+ )
+ ]
+ )
+ ]
tree = tools.build_async_tree(input_, task_emoji="🧵", cor_emoji="🔁")
flat = "\n".join(tree[0])
self.assertIn("🧵 MainTask", flat)
diff --git a/Lib/test/test_asyncio/test_transports.py b/Lib/test/test_asyncio/test_transports.py
index af10d3dc2a8..dbb572e2e15 100644
--- a/Lib/test/test_asyncio/test_transports.py
+++ b/Lib/test/test_asyncio/test_transports.py
@@ -10,7 +10,7 @@ from asyncio import transports
def tearDownModule():
# not needed for the test file but added for uniformness with all other
# asyncio test files for the sake of unified cleanup
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class TransportTests(unittest.TestCase):
diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py
index e020c1f3e4f..22982dc9d8a 100644
--- a/Lib/test/test_asyncio/test_unix_events.py
+++ b/Lib/test/test_asyncio/test_unix_events.py
@@ -30,7 +30,7 @@ from test.test_asyncio import utils as test_utils
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
MOCK_ANY = mock.ANY
diff --git a/Lib/test/test_asyncio/test_waitfor.py b/Lib/test/test_asyncio/test_waitfor.py
index d083f6b4d2a..dedc6bf69d7 100644
--- a/Lib/test/test_asyncio/test_waitfor.py
+++ b/Lib/test/test_asyncio/test_waitfor.py
@@ -5,7 +5,7 @@ from test import support
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
# The following value can be used as a very small timeout:
diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py
index 69e9905205e..0af3368627a 100644
--- a/Lib/test/test_asyncio/test_windows_events.py
+++ b/Lib/test/test_asyncio/test_windows_events.py
@@ -19,7 +19,7 @@ from test.test_asyncio import utils as test_utils
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class UpperProto(asyncio.Protocol):
@@ -330,16 +330,16 @@ class WinPolicyTests(WindowsEventsTestCase):
async def main():
self.assertIsInstance(asyncio.get_running_loop(), asyncio.SelectorEventLoop)
- old_policy = asyncio._get_event_loop_policy()
+ old_policy = asyncio.events._get_event_loop_policy()
try:
with self.assertWarnsRegex(
DeprecationWarning,
"'asyncio.WindowsSelectorEventLoopPolicy' is deprecated",
):
- asyncio._set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
+ asyncio.events._set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(main())
finally:
- asyncio._set_event_loop_policy(old_policy)
+ asyncio.events._set_event_loop_policy(old_policy)
def test_proactor_win_policy(self):
async def main():
@@ -347,16 +347,16 @@ class WinPolicyTests(WindowsEventsTestCase):
asyncio.get_running_loop(),
asyncio.ProactorEventLoop)
- old_policy = asyncio._get_event_loop_policy()
+ old_policy = asyncio.events._get_event_loop_policy()
try:
with self.assertWarnsRegex(
DeprecationWarning,
"'asyncio.WindowsProactorEventLoopPolicy' is deprecated",
):
- asyncio._set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
+ asyncio.events._set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
asyncio.run(main())
finally:
- asyncio._set_event_loop_policy(old_policy)
+ asyncio.events._set_event_loop_policy(old_policy)
if __name__ == '__main__':
diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py
index a6b207567c4..97f078ff911 100644
--- a/Lib/test/test_asyncio/test_windows_utils.py
+++ b/Lib/test/test_asyncio/test_windows_utils.py
@@ -16,7 +16,7 @@ from test import support
def tearDownModule():
- asyncio._set_event_loop_policy(None)
+ asyncio.events._set_event_loop_policy(None)
class PipeTests(unittest.TestCase):
diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py
index 0a96573a81c..a480e16e81b 100644
--- a/Lib/test/test_asyncio/utils.py
+++ b/Lib/test/test_asyncio/utils.py
@@ -601,3 +601,9 @@ async def await_without_task(coro):
await asyncio.sleep(0)
if exc is not None:
raise exc
+
+
+if sys.platform == 'win32':
+ DefaultEventLoopPolicy = asyncio.windows_events._DefaultEventLoopPolicy
+else:
+ DefaultEventLoopPolicy = asyncio.unix_events._DefaultEventLoopPolicy