On behalf of the Python Steering Council: We’ve chosen to reject PEP 806, “Mixed sync/async context managers with precise async marking”. We thank Zac for the careful survey and write-up, and we acknowledge that the nesting pattern the PEP describes is a real annoyance for code that mixes sync and async context managers. However, the cost of adding new syntax to Python is very high: a permanent addition to the grammar is paid for by every parser, AST consumer, linter, formatter, type checker, and teaching resource in the ecosystem, and it is carried forward by every Python programmer from here on. The problem this PEP addresses, while genuine, does not in our view meet that bar. The PEP’s own survey suggests adoption would be limited to a specific subset of async code that also uses synchronous context managers, and that narrow payoff is not enough on its own to justify new syntax.
In addition to that background concern, we have several more specific reservations:
-
The proposal introduces two valid spellings with different rules. Under this PEP, async with ctx(): and with async ctx(): would both be valid spellings of an async context manager entry, with different compositional rules: the former disallows mixing, the latter permits it. The PEP explicitly defers style enforcement to linters and formatters, but we are reluctant to ship syntax whose usability depends on external tooling to decide which of two legal forms is idiomatic.
-
We have some concerns about how with async interacts with the existing async marking convention. Today every line performing asynchronous work begins with async or await as its first non-whitespace token, which is a property readers and static analysis tools often rely on. Placing async as an item-level prefix inside a parenthesised with block changes that, and the PEP’s own recommendation to keep async with for single-line statements suggests the authors share some of this concern. On balance we would prefer to preserve the existing convention rather than loosen it for a syntactic convenience.
-
Existing workarounds are adequate for the cases the PEP addresses. contextlib.AsyncExitStack and the as_acm() helper pattern already cover the mixed sync/async case. The PEP’s objection to as_acm() rests on the await sleep(0) cancellation checkpoint breaking the property that syntactic await/async for/async with always yields to the scheduler, but the PEP itself acknowledges that few codebases enforce this property today. We would be receptive to a proposal for a stdlib version of as_acm(), with or without the checkpoint, as a smaller and more targeted change.
None of this is to dismiss the underlying pain point. We would encourage interested parties to continue exploring library-level solutions, and we think the survey data gathered for this PEP would be valuable input into such a discussion. Thanks to Zac for the work, and to everyone who contributed to the discussion.