mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
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/
* ef7bf0cea7/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.
This commit is contained in:
parent
e9208f0e74
commit
6e8dcdaaa4
5 changed files with 133 additions and 4 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue