From a117167d8dc8fa673a4646f509551c7950f824e5 Mon Sep 17 00:00:00 2001 From: Tom Gringauz Date: Mon, 9 Nov 2020 14:34:07 +0200 Subject: [PATCH] bpo-41543: contextlib.nullcontext can fill in for an async context manager (GH-21870) Co-authored-by: Andrew Svetlov --- Doc/library/contextlib.rst | 18 ++++++++++++++++++ Lib/contextlib.py | 8 +++++++- Lib/test/test_contextlib_async.py | 14 +++++++++++++- .../2020-08-14-00-39-04.bpo-41543.RpcRjb.rst | 1 + 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-08-14-00-39-04.bpo-41543.RpcRjb.rst diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index d5a1068a734..91edbba7283 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -243,8 +243,26 @@ Functions and classes provided: with cm as file: # Perform processing on the file + It can also be used as a stand-in for + :ref:`asynchronous context managers `:: + + async def send_http(session=None): + if not session: + # If no http session, create it with aiohttp + cm = aiohttp.ClientSession() + else: + # Caller is responsible for closing the session + cm = nullcontext(session) + + async with cm as session: + # Send http requests with session + .. versionadded:: 3.7 + .. versionchanged:: 3.10 + :term:`asynchronous context manager` support was added. + + .. function:: suppress(*exceptions) diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 56b4968118b..a0b523c96fb 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -704,7 +704,7 @@ class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager): return received_exc and suppressed_exc -class nullcontext(AbstractContextManager): +class nullcontext(AbstractContextManager, AbstractAsyncContextManager): """Context manager that does no additional processing. Used as a stand-in for a normal context manager, when a particular @@ -723,3 +723,9 @@ class nullcontext(AbstractContextManager): def __exit__(self, *excinfo): pass + + async def __aenter__(self): + return self.enter_result + + async def __aexit__(self, *excinfo): + pass diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index 109807d633d..290ef05b82a 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -1,5 +1,7 @@ import asyncio -from contextlib import aclosing, asynccontextmanager, AbstractAsyncContextManager, AsyncExitStack +from contextlib import ( + asynccontextmanager, AbstractAsyncContextManager, + AsyncExitStack, nullcontext, aclosing) import functools from test import support import unittest @@ -537,5 +539,15 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase): self.assertIsInstance(inner_exc.__context__, ZeroDivisionError) +class TestAsyncNullcontext(unittest.TestCase): + @_async_test + async def test_async_nullcontext(self): + class C: + pass + c = C() + async with nullcontext(c) as c_in: + self.assertIs(c_in, c) + + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-08-14-00-39-04.bpo-41543.RpcRjb.rst b/Misc/NEWS.d/next/Library/2020-08-14-00-39-04.bpo-41543.RpcRjb.rst new file mode 100644 index 00000000000..753dc763f21 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-14-00-39-04.bpo-41543.RpcRjb.rst @@ -0,0 +1 @@ +Add async context manager support for contextlib.nullcontext.