Hi all - after some earlier discussion, I’m pleased to present PEP 806 for discussion. It’s in most ways a very small proposal, which would make a surprisingly large difference for my code!
Feedback, questions, requests for clarification, etc. all most welcome.
Abstract
Python allows the with and async with statements to handle multiple context managers in a single statement, so long as they are all respectively synchronous or asynchronous. When mixing synchronous and asynchronous context managers, developers must use deeply nested statements or use risky workarounds such as overuse of AsyncExitStack.
We therefore propose to allow with statements to accept both synchronous and asynchronous context managers in a single statement by prefixing individual async context managers with the async keyword.
This change eliminates unnecessary nesting, improves code readability, and improves ergonomics without making async code any less explicit.
For example:
async def process_data():
async with acquire_lock() as lock:
with temp_directory() as tmpdir:
async with connect_to_db(cache=tmpdir) as db:
with open('config.json', encoding='utf-8') as f:
# We're now 16 spaces deep before any actual logic
config = json.load(f)
await db.execute(config['query'])
# ... more processing
becomes
async def process_data():
with (
async acquire_lock() as lock,
temp_directory() as tmpdir,
async connect_to_db(cache=tmpdir) as db,
open('config.json', encoding='utf-8') as f,
):
config = json.load(f)
await db.execute(config['query'])
# ... more processing