diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2021-06-29 08:28:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-29 08:28:15 (GMT) |
commit | 6cb145d23f5cf69b6d7414877d142747cd3d134c (patch) | |
tree | 0617efcd6911c14a1b8003bb9bee5fc68e521103 /Lib/test | |
parent | 20a88004bae8ead66a205a125e1fe979376fc3ea (diff) | |
download | cpython-6cb145d23f5cf69b6d7414877d142747cd3d134c.zip cpython-6cb145d23f5cf69b6d7414877d142747cd3d134c.tar.gz cpython-6cb145d23f5cf69b6d7414877d142747cd3d134c.tar.bz2 |
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.
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/test_contextlib.py | 23 | ||||
-rw-r--r-- | Lib/test/test_contextlib_async.py | 34 |
2 files changed, 55 insertions, 2 deletions
diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index dbc3f5f..9c27866 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -661,6 +661,25 @@ class TestBaseExitStack: result.append(2) self.assertEqual(result, [1, 2, 3, 4]) + def test_enter_context_errors(self): + class LacksEnterAndExit: + pass + class LacksEnter: + def __exit__(self, *exc_info): + pass + class LacksExit: + def __enter__(self): + pass + + with self.exit_stack() as stack: + with self.assertRaisesRegex(TypeError, 'the context manager'): + stack.enter_context(LacksEnterAndExit()) + with self.assertRaisesRegex(TypeError, 'the context manager'): + stack.enter_context(LacksEnter()) + with self.assertRaisesRegex(TypeError, 'the context manager'): + stack.enter_context(LacksExit()) + self.assertFalse(stack._exit_callbacks) + def test_close(self): result = [] with self.exit_stack() as stack: @@ -886,9 +905,11 @@ class TestBaseExitStack: def test_instance_bypass(self): class Example(object): pass cm = Example() + cm.__enter__ = object() cm.__exit__ = object() stack = self.exit_stack() - self.assertRaises(AttributeError, stack.enter_context, cm) + with self.assertRaisesRegex(TypeError, 'the context manager'): + stack.enter_context(cm) stack.push(cm) self.assertIs(stack._exit_callbacks[-1][1], cm) diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index cbc82df..7904abf 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) @@ -505,6 +505,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 async def raise_exc(exc): @@ -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 |