From 6cb145d23f5cf69b6d7414877d142747cd3d134c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 29 Jun 2021 11:28:15 +0300 Subject: bpo-44471: Change error type for bad objects in ExitStack.enter_context() (GH-26820) A TypeError is now raised instead of an AttributeError in ExitStack.enter_context() and AsyncExitStack.enter_async_context() for objects which do not support the context manager or asynchronous context manager protocols correspondingly. --- Lib/test/test_contextlib_async.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'Lib/test/test_contextlib_async.py') diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index cbc82dfd8f..7904abff7d 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -483,7 +483,7 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase): 1/0 @_async_test - async def test_async_enter_context(self): + async def test_enter_async_context(self): class TestCM(object): async def __aenter__(self): result.append(1) @@ -504,6 +504,26 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase): self.assertEqual(result, [1, 2, 3, 4]) + @_async_test + async def test_enter_async_context_errors(self): + class LacksEnterAndExit: + pass + class LacksEnter: + async def __aexit__(self, *exc_info): + pass + class LacksExit: + async def __aenter__(self): + pass + + async with self.exit_stack() as stack: + with self.assertRaisesRegex(TypeError, 'asynchronous context manager'): + await stack.enter_async_context(LacksEnterAndExit()) + with self.assertRaisesRegex(TypeError, 'asynchronous context manager'): + await stack.enter_async_context(LacksEnter()) + with self.assertRaisesRegex(TypeError, 'asynchronous context manager'): + 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 @@ -536,6 +556,18 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase): self.assertIsInstance(inner_exc, ValueError) self.assertIsInstance(inner_exc.__context__, ZeroDivisionError) + @_async_test + async def test_instance_bypass_async(self): + class Example(object): pass + cm = Example() + cm.__aenter__ = object() + cm.__aexit__ = object() + stack = self.exit_stack() + with self.assertRaisesRegex(TypeError, 'asynchronous context manager'): + await stack.enter_async_context(cm) + stack.push_async_exit(cm) + self.assertIs(stack._exit_callbacks[-1][1], cm) + class TestAsyncNullcontext(unittest.TestCase): @_async_test -- cgit v1.2.1