diff options
author | Pablo Galindo Salgado <Pablogsal@gmail.com> | 2025-05-04 00:51:57 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-04 00:51:57 (GMT) |
commit | 2bc836523105a2197a1f987cc03911bece74b35e (patch) | |
tree | 762f45a2793d0cf56aabd402c63883a1906eb561 /Lib/test | |
parent | 7363e8d24d14abf651633865ea959702ebac73d3 (diff) | |
download | cpython-2bc836523105a2197a1f987cc03911bece74b35e.zip cpython-2bc836523105a2197a1f987cc03911bece74b35e.tar.gz cpython-2bc836523105a2197a1f987cc03911bece74b35e.tar.bz2 |
GH-91048: Add utils for printing the call stack for asyncio tasks (#133284)
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/test_asyncio/test_tools.py | 839 | ||||
-rw-r--r-- | Lib/test/test_external_inspection.py | 45 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 2 |
3 files changed, 864 insertions, 22 deletions
diff --git a/Lib/test/test_asyncio/test_tools.py b/Lib/test/test_asyncio/test_tools.py new file mode 100644 index 0000000..2caf561 --- /dev/null +++ b/Lib/test/test_asyncio/test_tools.py @@ -0,0 +1,839 @@ +import unittest + +from asyncio import tools + + +# mock output of get_all_awaited_by function. +TEST_INPUTS_TREE = [ + [ + # test case containing a task called timer being awaited in two + # 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, + ] + ], + ), + ], + ), + (0, []), + ), + ( + [ + [ + "└── (T) Task-1", + " └── main", + " └── __aexit__", + " └── _aexit", + " ├── (T) root1", + " │ └── bloch", + " │ └── blocho_caller", + " │ └── __aexit__", + " │ └── _aexit", + " │ ├── (T) child1_1", + " │ │ └── awaiter", + " │ │ └── awaiter2", + " │ │ └── awaiter3", + " │ │ └── (T) timer", + " │ └── (T) child2_1", + " │ └── awaiter1", + " │ └── awaiter1_2", + " │ └── awaiter1_3", + " │ └── (T) timer", + " └── (T) root2", + " └── bloch", + " └── blocho_caller", + " └── __aexit__", + " └── _aexit", + " ├── (T) child1_2", + " │ └── awaiter", + " │ └── awaiter2", + " │ └── awaiter3", + " │ └── (T) timer", + " └── (T) child2_2", + " └── awaiter1", + " └── awaiter1_2", + " └── awaiter1_3", + " └── (T) timer", + ] + ] + ), + ], + [ + # test case containing two roots + ( + ( + 9, + [ + (5, "Task-5", []), + (6, "Task-6", [[["main2"], 5]]), + (7, "Task-7", [[["main2"], 5]]), + (8, "Task-8", [[["main2"], 5]]), + ], + ), + ( + 10, + [ + (1, "Task-1", []), + (2, "Task-2", [[["main"], 1]]), + (3, "Task-3", [[["main"], 1]]), + (4, "Task-4", [[["main"], 1]]), + ], + ), + (11, []), + (0, []), + ), + ( + [ + [ + "└── (T) Task-5", + " └── main2", + " ├── (T) Task-6", + " ├── (T) Task-7", + " └── (T) Task-8", + ], + [ + "└── (T) Task-1", + " └── main", + " ├── (T) Task-2", + " ├── (T) Task-3", + " └── (T) Task-4", + ], + ] + ), + ], + [ + # 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]]), + ], + ), + (8, []), + (0, []), + ] + ), + ( + [ + ["└── (T) Task-5"], + [ + "└── (T) Task-1", + " └── main", + " ├── (T) Task-2", + " ├── (T) Task-3", + " └── (T) Task-4", + ], + ] + ), + ], +] + +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]], + ), + (4, "b", [[["awaiter"], 3]]), + ], + ), + (0, []), + ] + ), + ([[4, 3, 4]]), + ], + [ + # this test case contains two cycles + ( + [ + ( + 1, + [ + (2, "Task-1", []), + ( + 3, + "A", + [[["nested", "nested", "task_b"], 4]], + ), + ( + 4, + "B", + [ + [["nested", "nested", "task_c"], 5], + [["nested", "nested", "task_a"], 3], + ], + ), + (5, "C", [[["nested", "nested"], 6]]), + ( + 6, + "Task-2", + [[["nested", "nested", "task_b"], 4]], + ), + ], + ), + (0, []), + ] + ), + ([[4, 3, 4], [4, 6, 5, 4]]), + ], +] + +TEST_INPUTS_TABLE = [ + [ + # test case containing a task called timer being awaited in two + # 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, + ] + ], + ), + ], + ), + (0, []), + ), + ( + [ + [1, "0x2", "Task-1", "", "", "0x0"], + [ + 1, + "0x3", + "timer", + "awaiter3 -> awaiter2 -> awaiter", + "child1_1", + "0x4", + ], + [ + 1, + "0x3", + "timer", + "awaiter1_3 -> awaiter1_2 -> awaiter1", + "child2_2", + "0x5", + ], + [ + 1, + "0x3", + "timer", + "awaiter1_3 -> awaiter1_2 -> awaiter1", + "child2_1", + "0x6", + ], + [ + 1, + "0x3", + "timer", + "awaiter3 -> awaiter2 -> awaiter", + "child1_2", + "0x7", + ], + [ + 1, + "0x8", + "root1", + "_aexit -> __aexit__ -> main", + "Task-1", + "0x2", + ], + [ + 1, + "0x9", + "root2", + "_aexit -> __aexit__ -> main", + "Task-1", + "0x2", + ], + [ + 1, + "0x4", + "child1_1", + "_aexit -> __aexit__ -> blocho_caller -> bloch", + "root1", + "0x8", + ], + [ + 1, + "0x6", + "child2_1", + "_aexit -> __aexit__ -> blocho_caller -> bloch", + "root1", + "0x8", + ], + [ + 1, + "0x7", + "child1_2", + "_aexit -> __aexit__ -> blocho_caller -> bloch", + "root2", + "0x9", + ], + [ + 1, + "0x5", + "child2_2", + "_aexit -> __aexit__ -> blocho_caller -> bloch", + "root2", + "0x9", + ], + ] + ), + ], + [ + # test case containing two roots + ( + ( + 9, + [ + (5, "Task-5", []), + (6, "Task-6", [[["main2"], 5]]), + (7, "Task-7", [[["main2"], 5]]), + (8, "Task-8", [[["main2"], 5]]), + ], + ), + ( + 10, + [ + (1, "Task-1", []), + (2, "Task-2", [[["main"], 1]]), + (3, "Task-3", [[["main"], 1]]), + (4, "Task-4", [[["main"], 1]]), + ], + ), + (11, []), + (0, []), + ), + ( + [ + [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"], + ] + ), + ], + [ + # 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]]), + ], + ), + (8, []), + (0, []), + ] + ), + ( + [ + [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"], + ] + ), + ], + # CASES WITH CYCLES + [ + # this test case contains a cycle: two tasks awaiting each other. + ( + [ + ( + 1, + [ + (2, "Task-1", []), + ( + 3, + "a", + [[["awaiter2"], 4], [["main"], 2]], + ), + (4, "b", [[["awaiter"], 3]]), + ], + ), + (0, []), + ] + ), + ( + [ + [1, "0x2", "Task-1", "", "", "0x0"], + [1, "0x3", "a", "awaiter2", "b", "0x4"], + [1, "0x3", "a", "main", "Task-1", "0x2"], + [1, "0x4", "b", "awaiter", "a", "0x3"], + ] + ), + ], + [ + # this test case contains two cycles + ( + [ + ( + 1, + [ + (2, "Task-1", []), + ( + 3, + "A", + [[["nested", "nested", "task_b"], 4]], + ), + ( + 4, + "B", + [ + [["nested", "nested", "task_c"], 5], + [["nested", "nested", "task_a"], 3], + ], + ), + (5, "C", [[["nested", "nested"], 6]]), + ( + 6, + "Task-2", + [[["nested", "nested", "task_b"], 4]], + ), + ], + ), + (0, []), + ] + ), + ( + [ + [1, "0x2", "Task-1", "", "", "0x0"], + [ + 1, + "0x3", + "A", + "nested -> nested -> task_b", + "B", + "0x4", + ], + [ + 1, + "0x4", + "B", + "nested -> nested -> task_c", + "C", + "0x5", + ], + [ + 1, + "0x4", + "B", + "nested -> nested -> task_a", + "A", + "0x3", + ], + [ + 1, + "0x5", + "C", + "nested -> nested", + "Task-2", + "0x6", + ], + [ + 1, + "0x6", + "Task-2", + "nested -> nested -> task_b", + "B", + "0x4", + ], + ] + ), + ], +] + + +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) + + def test_asyncio_utils_cycles(self): + for input_, cycles in TEST_INPUTS_CYCLES_TREE: + with self.subTest(input_): + try: + tools.build_async_tree(input_) + except tools.CycleFoundException as e: + self.assertEqual(e.cycles, cycles) + + +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) + + +class TestAsyncioToolsBasic(unittest.TestCase): + def test_empty_input_tree(self): + """Test build_async_tree with empty input.""" + result = [] + expected_output = [] + self.assertEqual(tools.build_async_tree(result), expected_output) + + def test_empty_input_table(self): + """Test build_task_table with empty input.""" + result = [] + expected_output = [] + self.assertEqual(tools.build_task_table(result), expected_output) + + def test_only_independent_tasks_tree(self): + input_ = [(1, [(10, "taskA", []), (11, "taskB", [])])] + 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", [])])] + self.assertEqual( + tools.build_task_table(input_), + [[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", []), + ], + ) + ] + expected_output = [ + [ + "└── (T) Task-1", + ] + ] + self.assertEqual(tools.build_async_tree(result), expected_output) + + def test_single_task_table(self): + """Test build_task_table with a single task and no awaits.""" + result = [ + ( + 1, + [ + (2, "Task-1", []), + ], + ) + ] + 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]]), + ], + ) + ] + with self.assertRaises(tools.CycleFoundException) as context: + tools.build_async_tree(result) + self.assertEqual(context.exception.cycles, [[3, 2, 3]]) + + 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]]), + ], + ) + ] + expected_output = [ + [ + "└── (T) Task-1", + " └── main", + " └── (T) Task-2", + " └── main", + " └── (T) Task-3", + ] + ] + self.assertEqual(tools.build_async_tree(result), expected_output) + + 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]]), + ], + ) + ] + expected_output = [ + [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", []), + ], + ) + ] + expected = [ + [ + "└── (T) root", + " └── c5", + " └── c4", + " └── c3", + " └── c2", + " └── c1", + " └── (T) leaf", + ] + ] + result = tools.build_async_tree(input_) + self.assertEqual(result, expected) + + 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]]), + ], + ) + ] + with self.assertRaises(tools.CycleFoundException) as ctx: + tools.build_async_tree(input_) + cycles = ctx.exception.cycles + 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", [])])] + table = tools.build_task_table(input_) + for row in table: + self.assertEqual(len(row), 6) + 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.assertTrue( + isinstance(row[5], str) and row[5].startswith("0x") + ) # hex awaiter ID + + +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]])])] + 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 + table = tools.build_task_table(input_) + self.assertEqual(len(table), 1) + self.assertEqual(table[0][4], "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", []), + ], + ) + ] + tree = tools.build_async_tree(input_) + # Both children should be under the same coroutine node + flat = "\n".join(tree[0]) + self.assertIn("frameA", flat) + self.assertIn("Task-2", flat) + self.assertIn("Task-1", flat) + + flat = "\n".join(tree[1]) + self.assertIn("frameA", flat) + self.assertIn("Task-3", flat) + self.assertIn("Task-1", flat) + + 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, [])])] + # 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", [])])] + tree = tools.build_async_tree(input_, task_emoji="🧵", cor_emoji="🔁") + flat = "\n".join(tree[0]) + self.assertIn("🧵 MainTask", flat) + self.assertIn("🔁 f1", flat) + self.assertIn("🔁 f2", flat) + self.assertIn("🧵 SubTask", flat) diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py index aa05db9..4e82f56 100644 --- a/Lib/test/test_external_inspection.py +++ b/Lib/test/test_external_inspection.py @@ -4,7 +4,8 @@ import textwrap import importlib import sys import socket -from test.support import os_helper, SHORT_TIMEOUT, busy_retry +from unittest.mock import ANY +from test.support import os_helper, SHORT_TIMEOUT, busy_retry, requires_gil_enabled from test.support.script_helper import make_script from test.support.socket_helper import find_unused_port @@ -13,13 +14,13 @@ import subprocess PROCESS_VM_READV_SUPPORTED = False try: - from _testexternalinspection import PROCESS_VM_READV_SUPPORTED - from _testexternalinspection import get_stack_trace - from _testexternalinspection import get_async_stack_trace - from _testexternalinspection import get_all_awaited_by + from _remotedebugging import PROCESS_VM_READV_SUPPORTED + from _remotedebugging import get_stack_trace + from _remotedebugging import get_async_stack_trace + from _remotedebugging import get_all_awaited_by except ImportError: raise unittest.SkipTest( - "Test only runs when _testexternalinspection is available") + "Test only runs when _remotedebuggingmodule is available") def _make_test_script(script_dir, script_basename, source): to_return = make_script(script_dir, script_basename, source) @@ -184,13 +185,13 @@ class TestGetStackTrace(unittest.TestCase): root_task = "Task-1" expected_stack_trace = [ - ["c5", "c4", "c3", "c2"], - "c2_root", + ['c5', 'c4', 'c3', 'c2'], + 'c2_root', [ - [["main"], root_task, []], - [["c1"], "sub_main_1", [[["main"], root_task, []]]], - [["c1"], "sub_main_2", [[["main"], root_task, []]]], - ], + [['_aexit', '__aexit__', 'main'], root_task, []], + [['c1'], 'sub_main_1', [[['_aexit', '__aexit__', 'main'], root_task, []]]], + [['c1'], 'sub_main_2', [[['_aexit', '__aexit__', 'main'], root_task, []]]], + ] ] self.assertEqual(stack_trace, expected_stack_trace) @@ -397,12 +398,15 @@ class TestGetStackTrace(unittest.TestCase): # sets are unordered, so we want to sort "awaited_by"s stack_trace[2].sort(key=lambda x: x[1]) - expected_stack_trace = [ - ['deep', 'c1', 'run_one_coro'], 'Task-2', [[['main'], 'Task-1', []]] + expected_stack_trace = [ + ['deep', 'c1', 'run_one_coro'], + 'Task-2', + [[['staggered_race', 'main'], 'Task-1', []]] ] self.assertEqual(stack_trace, expected_stack_trace) @skip_if_not_supported + @requires_gil_enabled("gh-133359: occasionally flaky on AMD64") @unittest.skipIf(sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, "Test only runs on Linux with process_vm_readv support") def test_async_global_awaited_by(self): @@ -516,19 +520,19 @@ class TestGetStackTrace(unittest.TestCase): # expected: at least 1000 pending tasks self.assertGreaterEqual(len(entries), 1000) # the first three tasks stem from the code structure - self.assertIn(('Task-1', []), entries) - self.assertIn(('server task', [[['main'], 'Task-1', []]]), entries) - self.assertIn(('echo client spam', [[['main'], 'Task-1', []]]), entries) + self.assertIn((ANY, 'Task-1', []), entries) + self.assertIn((ANY, 'server task', [[['_aexit', '__aexit__', 'main'], ANY]]), entries) + self.assertIn((ANY, 'echo client spam', [[['_aexit', '__aexit__', 'main'], ANY]]), entries) - expected_stack = [[['echo_client_spam'], 'echo client spam', [[['main'], 'Task-1', []]]]] - tasks_with_stack = [task for task in entries if task[1] == expected_stack] + expected_stack = [[['_aexit', '__aexit__', 'echo_client_spam'], ANY]] + tasks_with_stack = [task for task in entries if task[2] == expected_stack] self.assertGreaterEqual(len(tasks_with_stack), 1000) # the final task will have some random number, but it should for # sure be one of the echo client spam horde (In windows this is not true # for some reason) if sys.platform != "win32": - self.assertEqual([[['echo_client_spam'], 'echo client spam', [[['main'], 'Task-1', []]]]], entries[-1][1]) + self.assertEqual([[['_aexit', '__aexit__', 'echo_client_spam'], ANY]], entries[-1][2]) except PermissionError: self.skipTest( "Insufficient permissions to read the stack trace") @@ -544,7 +548,6 @@ class TestGetStackTrace(unittest.TestCase): "Test only runs on Linux with process_vm_readv support") def test_self_trace(self): stack_trace = get_stack_trace(os.getpid()) - print(stack_trace) self.assertEqual(stack_trace[0], "test_self_trace") if __name__ == "__main__": diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 56413d0..10c3e0e 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1960,7 +1960,7 @@ def _supports_remote_attaching(): PROCESS_VM_READV_SUPPORTED = False try: - from _testexternalinspection import PROCESS_VM_READV_SUPPORTED + from _remotedebuggingmodule import PROCESS_VM_READV_SUPPORTED except ImportError: pass |