From 6e8dcdaaa49d4313bf9fab9f9923ca5828fbb10e Mon Sep 17 00:00:00 2001 From: Joongi Kim Date: Mon, 2 Nov 2020 17:02:48 +0900 Subject: 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. --- Lib/contextlib.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'Lib/contextlib.py') diff --git a/Lib/contextlib.py b/Lib/contextlib.py index ff92d9f913..82ddc1497d 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -303,6 +303,32 @@ class closing(AbstractContextManager): self.thing.close() +class aclosing(AbstractAsyncContextManager): + """Async context manager for safely finalizing an asynchronously cleaned-up + resource such as an async generator, calling its ``aclose()`` method. + + Code like this: + + async with aclosing(.fetch()) as agen: + + + is equivalent to this: + + agen = .fetch() + try: + + finally: + await agen.aclose() + + """ + def __init__(self, thing): + self.thing = thing + async def __aenter__(self): + return self.thing + async def __aexit__(self, *exc_info): + await self.thing.aclose() + + class _RedirectStream(AbstractContextManager): _stream = None -- cgit v1.2.1