diff options
author | Joongi Kim <joongi@lablup.com> | 2020-11-02 08:02:48 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-02 08:02:48 (GMT) |
commit | 6e8dcdaaa49d4313bf9fab9f9923ca5828fbb10e (patch) | |
tree | fc23f29d61f7fa5d98c51f5adce53c9f32a726e4 /Lib/test/test_contextlib_async.py | |
parent | e9208f0e74d8c37542a750476ff272143fa8f67c (diff) | |
download | cpython-6e8dcdaaa49d4313bf9fab9f9923ca5828fbb10e.zip cpython-6e8dcdaaa49d4313bf9fab9f9923ca5828fbb10e.tar.gz cpython-6e8dcdaaa49d4313bf9fab9f9923ca5828fbb10e.tar.bz2 |
bpo-41229: Update docs for explicit aclose()-required cases and add contextlib.aclosing() method (GH-21545)
This is a PR to:
* Add `contextlib.aclosing` which ia analogous to `contextlib.closing` but for async-generators with an explicit test case for [bpo-41229]()
* Update the docs to describe when we need explicit `aclose()` invocation.
which are motivated by the following issues, articles, and examples:
* [bpo-41229]()
* https://github.com/njsmith/async_generator
* https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#cleanup-in-generators-and-async-generators
* https://www.python.org/dev/peps/pep-0533/
* https://github.com/achimnol/aiotools/blob/ef7bf0cea7af/src/aiotools/context.py#L152
Particuarly regarding [PEP-533](https://www.python.org/dev/peps/pep-0533/), its acceptance (`__aiterclose__()`) would make this little addition of `contextlib.aclosing()` unnecessary for most use cases, but until then this could serve as a good counterpart and analogy to `contextlib.closing()`. The same applies for `contextlib.closing` with `__iterclose__()`.
Also, still there are other use cases, e.g., when working with non-generator objects with `aclose()` methods.
Diffstat (limited to 'Lib/test/test_contextlib_async.py')
-rw-r--r-- | Lib/test/test_contextlib_async.py | 59 |
1 files changed, 58 insertions, 1 deletions
diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index 43fb7fc..3765f6c 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -1,5 +1,5 @@ import asyncio -from contextlib import asynccontextmanager, AbstractAsyncContextManager, AsyncExitStack +from contextlib import aclosing, asynccontextmanager, AbstractAsyncContextManager, AsyncExitStack import functools from test import support import unittest @@ -279,6 +279,63 @@ class AsyncContextManagerTestCase(unittest.TestCase): self.assertEqual(target, (11, 22, 33, 44)) +class AclosingTestCase(unittest.TestCase): + + @support.requires_docstrings + def test_instance_docs(self): + cm_docstring = aclosing.__doc__ + obj = aclosing(None) + self.assertEqual(obj.__doc__, cm_docstring) + + @_async_test + async def test_aclosing(self): + state = [] + class C: + async def aclose(self): + state.append(1) + x = C() + self.assertEqual(state, []) + async with aclosing(x) as y: + self.assertEqual(x, y) + self.assertEqual(state, [1]) + + @_async_test + async def test_aclosing_error(self): + state = [] + class C: + async def aclose(self): + state.append(1) + x = C() + self.assertEqual(state, []) + with self.assertRaises(ZeroDivisionError): + async with aclosing(x) as y: + self.assertEqual(x, y) + 1 / 0 + self.assertEqual(state, [1]) + + @_async_test + async def test_aclosing_bpo41229(self): + state = [] + + class Resource: + def __del__(self): + state.append(1) + + async def agenfunc(): + r = Resource() + yield -1 + yield -2 + + x = agenfunc() + self.assertEqual(state, []) + with self.assertRaises(ZeroDivisionError): + async with aclosing(x) as y: + self.assertEqual(x, y) + self.assertEqual(-1, await x.__anext__()) + 1 / 0 + self.assertEqual(state, [1]) + + class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase): class SyncAsyncExitStack(AsyncExitStack): @staticmethod |