From cb0cffbd7cab72e3a7ecefd63d179bdddf960797 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Mon, 11 May 2026 18:41:05 +0300 Subject: [PATCH] Update `test_contextlib_async.py` to 3.14.4 (#7816) --- Lib/test/test_contextlib_async.py | 94 ++++++++++++++++++------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index 95d0bd7f9..6541043bd 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -1,21 +1,27 @@ -import asyncio +import functools from contextlib import ( asynccontextmanager, AbstractAsyncContextManager, AsyncExitStack, nullcontext, aclosing, contextmanager) from test import support +from test.support import run_no_yield_async_fn as _run_async_fn import unittest import traceback from test.test_contextlib import TestBaseExitStack -support.requires_working_socket(module=True) -def tearDownModule(): - asyncio.set_event_loop_policy(None) +def _async_test(async_fn): + """Decorator to turn an async function into a synchronous function""" + @functools.wraps(async_fn) + def wrapper(*args, **kwargs): + return _run_async_fn(async_fn, *args, **kwargs) + + return wrapper -class TestAbstractAsyncContextManager(unittest.IsolatedAsyncioTestCase): +class TestAbstractAsyncContextManager(unittest.TestCase): + @_async_test async def test_enter(self): class DefaultEnter(AbstractAsyncContextManager): async def __aexit__(self, *args): @@ -27,6 +33,7 @@ class TestAbstractAsyncContextManager(unittest.IsolatedAsyncioTestCase): async with manager as context: self.assertIs(manager, context) + @_async_test async def test_slots(self): class DefaultAsyncContextManager(AbstractAsyncContextManager): __slots__ = () @@ -38,6 +45,7 @@ class TestAbstractAsyncContextManager(unittest.IsolatedAsyncioTestCase): manager = DefaultAsyncContextManager() manager.var = 42 + @_async_test async def test_async_gen_propagates_generator_exit(self): # A regression test for https://bugs.python.org/issue33786. @@ -69,27 +77,28 @@ class TestAbstractAsyncContextManager(unittest.IsolatedAsyncioTestCase): async def __aexit__(self, exc_type, exc_value, traceback): return None - self.assertTrue(issubclass(ManagerFromScratch, AbstractAsyncContextManager)) + self.assertIsSubclass(ManagerFromScratch, AbstractAsyncContextManager) class DefaultEnter(AbstractAsyncContextManager): async def __aexit__(self, *args): await super().__aexit__(*args) - self.assertTrue(issubclass(DefaultEnter, AbstractAsyncContextManager)) + self.assertIsSubclass(DefaultEnter, AbstractAsyncContextManager) class NoneAenter(ManagerFromScratch): __aenter__ = None - self.assertFalse(issubclass(NoneAenter, AbstractAsyncContextManager)) + self.assertNotIsSubclass(NoneAenter, AbstractAsyncContextManager) class NoneAexit(ManagerFromScratch): __aexit__ = None - self.assertFalse(issubclass(NoneAexit, AbstractAsyncContextManager)) + self.assertNotIsSubclass(NoneAexit, AbstractAsyncContextManager) -class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): +class AsyncContextManagerTestCase(unittest.TestCase): + @_async_test async def test_contextmanager_plain(self): state = [] @asynccontextmanager @@ -103,6 +112,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): state.append(x) self.assertEqual(state, [1, 42, 999]) + @_async_test async def test_contextmanager_finally(self): state = [] @asynccontextmanager @@ -120,7 +130,8 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): raise ZeroDivisionError() self.assertEqual(state, [1, 42, 999]) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON + @_async_test async def test_contextmanager_traceback(self): @asynccontextmanager async def f(): @@ -176,6 +187,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): self.assertEqual(frames[0].name, 'test_contextmanager_traceback') self.assertEqual(frames[0].line, 'raise stop_exc') + @_async_test async def test_contextmanager_no_reraise(self): @asynccontextmanager async def whee(): @@ -185,6 +197,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): # Calling __aexit__ should not result in an exception self.assertFalse(await ctx.__aexit__(TypeError, TypeError("foo"), None)) + @_async_test async def test_contextmanager_trap_yield_after_throw(self): @asynccontextmanager async def whoo(): @@ -200,6 +213,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): # The "gen" attribute is an implementation detail. self.assertFalse(ctx.gen.ag_suspended) + @_async_test async def test_contextmanager_trap_no_yield(self): @asynccontextmanager async def whoo(): @@ -209,6 +223,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): with self.assertRaises(RuntimeError): await ctx.__aenter__() + @_async_test async def test_contextmanager_trap_second_yield(self): @asynccontextmanager async def whoo(): @@ -222,6 +237,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): # The "gen" attribute is an implementation detail. self.assertFalse(ctx.gen.ag_suspended) + @_async_test async def test_contextmanager_non_normalised(self): @asynccontextmanager async def whoo(): @@ -235,6 +251,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): with self.assertRaises(SyntaxError): await ctx.__aexit__(RuntimeError, None, None) + @_async_test async def test_contextmanager_except(self): state = [] @asynccontextmanager @@ -252,6 +269,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): raise ZeroDivisionError(999) self.assertEqual(state, [1, 42, 999]) + @_async_test async def test_contextmanager_except_stopiter(self): @asynccontextmanager async def woohoo(): @@ -278,6 +296,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): else: self.fail(f'{stop_exc} was suppressed') + @_async_test async def test_contextmanager_wrap_runtimeerror(self): @asynccontextmanager async def woohoo(): @@ -322,12 +341,14 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): self.assertEqual(baz.__doc__, "Whee!") @support.requires_docstrings + @_async_test async def test_instance_docstring_given_cm_docstring(self): baz = self._create_contextmanager_attribs()(None) self.assertEqual(baz.__doc__, "Whee!") async with baz: pass # suppress warning + @_async_test async def test_keywords(self): # Ensure no keyword arguments are inhibited @asynccontextmanager @@ -336,6 +357,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): async with woohoo(self=11, func=22, args=33, kwds=44) as target: self.assertEqual(target, (11, 22, 33, 44)) + @_async_test async def test_recursive(self): depth = 0 ncols = 0 @@ -362,6 +384,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): self.assertEqual(ncols, 10) self.assertEqual(depth, 0) + @_async_test async def test_decorator(self): entered = False @@ -380,6 +403,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): await test() self.assertFalse(entered) + @_async_test async def test_decorator_with_exception(self): entered = False @@ -402,6 +426,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): await test() self.assertFalse(entered) + @_async_test async def test_decorating_method(self): @asynccontextmanager @@ -436,7 +461,7 @@ class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): self.assertEqual(test.b, 2) -class AclosingTestCase(unittest.IsolatedAsyncioTestCase): +class AclosingTestCase(unittest.TestCase): @support.requires_docstrings def test_instance_docs(self): @@ -444,6 +469,7 @@ class AclosingTestCase(unittest.IsolatedAsyncioTestCase): obj = aclosing(None) self.assertEqual(obj.__doc__, cm_docstring) + @_async_test async def test_aclosing(self): state = [] class C: @@ -455,6 +481,7 @@ class AclosingTestCase(unittest.IsolatedAsyncioTestCase): self.assertEqual(x, y) self.assertEqual(state, [1]) + @_async_test async def test_aclosing_error(self): state = [] class C: @@ -468,6 +495,7 @@ class AclosingTestCase(unittest.IsolatedAsyncioTestCase): 1 / 0 self.assertEqual(state, [1]) + @_async_test async def test_aclosing_bpo41229(self): state = [] @@ -493,45 +521,27 @@ class AclosingTestCase(unittest.IsolatedAsyncioTestCase): self.assertEqual(state, [1]) -class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): +class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase): class SyncAsyncExitStack(AsyncExitStack): - @staticmethod - def run_coroutine(coro): - loop = asyncio.get_event_loop_policy().get_event_loop() - t = loop.create_task(coro) - t.add_done_callback(lambda f: loop.stop()) - loop.run_forever() - - exc = t.exception() - if not exc: - return t.result() - else: - context = exc.__context__ - - try: - raise exc - except: - exc.__context__ = context - raise exc def close(self): - return self.run_coroutine(self.aclose()) + return _run_async_fn(self.aclose) def __enter__(self): - return self.run_coroutine(self.__aenter__()) + return _run_async_fn(self.__aenter__) def __exit__(self, *exc_details): - return self.run_coroutine(self.__aexit__(*exc_details)) + return _run_async_fn(self.__aexit__, *exc_details) exit_stack = SyncAsyncExitStack callback_error_internal_frames = [ - ('__exit__', 'return self.run_coroutine(self.__aexit__(*exc_details))'), - ('run_coroutine', 'raise exc'), - ('run_coroutine', 'raise exc'), + ('__exit__', 'return _run_async_fn(self.__aexit__, *exc_details)'), + ('run_no_yield_async_fn', 'coro.send(None)'), ('__aexit__', 'raise exc'), ('__aexit__', 'cb_suppress = cb(*exc_details)'), ] + @_async_test async def test_async_callback(self): expected = [ ((), {}), @@ -574,6 +584,7 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): stack.push_async_callback(callback=_exit, arg=3) self.assertEqual(result, []) + @_async_test async def test_async_push(self): exc_raised = ZeroDivisionError async def _expect_exc(exc_type, exc, exc_tb): @@ -609,6 +620,7 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): self.assertIs(stack._exit_callbacks[-1][1], _expect_exc) 1/0 + @_async_test async def test_enter_async_context(self): class TestCM(object): async def __aenter__(self): @@ -630,6 +642,7 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): self.assertEqual(result, [1, 2, 3, 4]) + @_async_test async def test_enter_async_context_errors(self): class LacksEnterAndExit: pass @@ -649,6 +662,7 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): await stack.enter_async_context(LacksExit()) self.assertFalse(stack._exit_callbacks) + @_async_test async def test_async_exit_exception_chaining(self): # Ensure exception chaining matches the reference behaviour async def raise_exc(exc): @@ -680,6 +694,7 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): self.assertIsInstance(inner_exc, ValueError) self.assertIsInstance(inner_exc.__context__, ZeroDivisionError) + @_async_test async def test_async_exit_exception_explicit_none_context(self): # Ensure AsyncExitStack chaining matches actual nested `with` statements # regarding explicit __context__ = None. @@ -714,6 +729,7 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): else: self.fail("Expected IndexError, but no exception was raised") + @_async_test async def test_instance_bypass_async(self): class Example(object): pass cm = Example() @@ -726,8 +742,8 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): self.assertIs(stack._exit_callbacks[-1][1], cm) - -class TestAsyncNullcontext(unittest.IsolatedAsyncioTestCase): +class TestAsyncNullcontext(unittest.TestCase): + @_async_test async def test_async_nullcontext(self): class C: pass