async with asynchronously acquires a resource, runs a block, and releases the resource when the block ends. It provides the coroutine-aware counterpart to synchronous with, including cleanup when an exception occurs. section 4.2
Use async with when a resource must be acquired or released through awaitable operations:
- Implement asynchronous context managers with async __aenter__ and async __aexit__ methods. section 4.2
- Bind the value returned by __aenter__ with as, then use it inside the indented block. section 4.2
- Let __aexit__ perform cleanup automatically after normal completion or an exception. section 4.2
- Prefer async with for asyncio locks; leaving the block releases the lock and lets another coroutine enter the critical section. section 11.2
HTTP sessions and database connections
aiohttp uses async with for both a ClientSession and an individual request: a session can be reused for connection pooling, while the response is processed inside the request block. asyncpg uses the same pattern for connection pools and pool.acquire(); the acquired connection returns to the pool when the block ends. Without that release, concurrent queries can exhaust the pool and wait indefinitely. section 4.2.1 section 5.5.2
Listing 4.1 An asynchronous context manager to wait for a client connection
import asyncio
import socket
from types import TracebackType
from typing import Optional, Type
class ConnectedSocket:
def __init__(self, server_socket):
self._connection = None
self._server_socket = server_socket
async def __aenter__(self): #1
print('Entering context manager, waiting for connection')
loop = asyncio.get_event_loop()
connection, address = await loop.sock_accept(self._server_socket)
self._connection = connection
print('Accepted a connection')
return self._connection
async def __aexit__(self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType]): #2
print('Exiting context manager')
self._connection.close()
print('Closed connection')
async def main():
loop = asyncio.get_event_loop()
server_socket = socket.socket()
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_address = ('127.0.0.1', 8000)
server_socket.setblocking(False)
server_socket.bind(server_address)
server_socket.listen()
async with ConnectedSocket(server_socket) as connection: #3
data = await loop.sock_recv(connection, 1024)
print(data) #4
asyncio.run(main())
Shows a complete asynchronous context manager that awaits a socket connection in __aenter__, exposes it inside async with, and closes it in __aexit__. section 4.2
