From c5a5fbc9a4e5b9835ed54371d5f1a612f5e7331c Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Fri, 17 Apr 2026 23:08:04 +0300 Subject: [PATCH 1/3] gh-148701: Add missing test coverage for asyncio.graph --- Lib/test/test_asyncio/test_graph.py | 134 ++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/Lib/test/test_asyncio/test_graph.py b/Lib/test/test_asyncio/test_graph.py index 2f22fbccba42bc..cffb2b30ad9cff 100644 --- a/Lib/test/test_asyncio/test_graph.py +++ b/Lib/test/test_asyncio/test_graph.py @@ -1,6 +1,7 @@ import asyncio import io import unittest +from unittest import mock # To prevent a warning "test altered the execution environment" @@ -345,6 +346,139 @@ async def main(): self.assertTrue(stack_for_fut[1].startswith('* Future(id=')) + async def test_capture_call_graph_positive_limit(self): + captured = None + + async def c3(): + nonlocal captured + captured = asyncio.capture_call_graph(limit=2) + + async def c2(): + await c3() + + async def c1(): + await c2() + + await c1() + self.assertEqual(len(captured.call_stack), 2) + + async def test_capture_call_graph_negative_limit(self): + captured = None + + async def c3(): + nonlocal captured + captured = asyncio.capture_call_graph(limit=-2) + + async def c2(): + await c3() + + async def c1(): + await c2() + + await c1() + self.assertEqual(len(captured.call_stack), 2) + + async def test_capture_call_graph_zero_limit(self): + captured = None + + async def inner(): + nonlocal captured + captured = asyncio.capture_call_graph(limit=0) + + await inner() + self.assertEqual(captured.call_stack, ()) + + def test_capture_call_graph_outside_loop(self): + with self.assertRaises(RuntimeError): + asyncio.capture_call_graph() + + def test_capture_call_graph_non_future(self): + with self.assertRaises(TypeError): + asyncio.capture_call_graph("not a future") + + async def test_capture_call_graph_no_current_task(self): + results = [] + + def cb(): + results.append(asyncio.capture_call_graph()) + results.append(asyncio.format_call_graph()) + + loop = asyncio.get_running_loop() + loop.call_soon(cb) + await asyncio.sleep(0) + + self.assertEqual(results, [None, ""]) + + async def test_capture_call_graph_current_task_not_future(self): + sentinel = object() + with mock.patch('asyncio.tasks.current_task', return_value=sentinel): + with self.assertRaises(TypeError): + asyncio.capture_call_graph(sentinel) + + async def test_build_graph_for_future_positive_limit(self): + fut = asyncio.Future() + captured = None + + async def deep(): + await fut + + async def mid(): + await deep() + + async def runner(): + await mid() + + async def main(): + nonlocal captured + async with asyncio.TaskGroup() as g: + t = g.create_task(runner(), name='runner') + for _ in range(5): + await asyncio.sleep(0) + captured = asyncio.capture_call_graph(t, limit=2) + fut.set_result(None) + + await main() + self.assertEqual(len(captured.call_stack), 2) + + async def test_build_graph_for_future_negative_limit(self): + fut = asyncio.Future() + captured = None + + async def deep(): + await fut + + async def mid(): + await deep() + + async def runner(): + await mid() + + async def main(): + nonlocal captured + async with asyncio.TaskGroup() as g: + t = g.create_task(runner(), name='runner') + for _ in range(5): + await asyncio.sleep(0) + captured = asyncio.capture_call_graph(t, limit=-2) + fut.set_result(None) + + await main() + self.assertEqual(len(captured.call_stack), 2) + + async def test_format_call_graph_regular_generator(self): + output = [] + + def gen(): + output.append(asyncio.format_call_graph()) + yield + + async def main(): + for _ in gen(): + pass + + await main() + self.assertRegex(output[0], r'in generator [\w.<>]+\.gen\(\)') + @unittest.skipIf( not hasattr(asyncio.futures, "_c_future_add_to_awaited_by"), From 27a5e0902177f92535537162b1a106c197be488c Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Sat, 18 Apr 2026 00:11:44 +0300 Subject: [PATCH 2/3] add blurb --- .../next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst diff --git a/Misc/NEWS.d/next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst b/Misc/NEWS.d/next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst new file mode 100644 index 00000000000000..30d05154807b7b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst @@ -0,0 +1,3 @@ +Add missing test coverage for `asyncio.graph`, including error handling, +`limit` parameter edge cases, and generator introspection. Patched by Shamil +Abdulaev. From cacbf931983ba4d02cad9a760643aa1abf382eb0 Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Sat, 18 Apr 2026 00:32:24 +0300 Subject: [PATCH 3/3] add blurb --- .../next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst b/Misc/NEWS.d/next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst index 30d05154807b7b..9e5ad5655a785a 100644 --- a/Misc/NEWS.d/next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst +++ b/Misc/NEWS.d/next/Tests/2026-04-17-23-50-50.gh-issue-148701.GVW-Lz.rst @@ -1,3 +1,3 @@ -Add missing test coverage for `asyncio.graph`, including error handling, -`limit` parameter edge cases, and generator introspection. Patched by Shamil +Add missing test coverage for ``asyncio.graph``, including error handling, +``limit`` parameter edge cases, and generator introspection. Patched by Shamil Abdulaev.