1- PEP: 9999
1+ PEP: 806
22Title: Mixed sync/async context managers with precise async marking
33Author: Zac Hatfield-Dodds <zac@zhd.dev>
44Sponsor: Jelle Zijlstra <jelle.zijlstra@gmail.com>
@@ -123,7 +123,7 @@ managers in the same way as current multi-context ``with`` statements,
123123except that those prefixed by the ``async `` keyword use the ``__aenter__ `` /
124124``__aexit__ `` protocol.
125125
126- Only the ``with `` statement is modified; ``async with async cm (): `` is a
126+ Only the ``with `` statement is modified; ``async with async ctx (): `` is a
127127syntax error.
128128
129129
@@ -158,7 +158,7 @@ Workaround: an ``as_acm()`` wrapper
158158-----------------------------------
159159
160160It is easy to implement a helper function which wraps a synchronous context
161- manager in an async cm . For example:
161+ manager in an async context manager . For example:
162162
163163.. code-block :: python
164164
@@ -195,8 +195,8 @@ which allows for explicit entry of sync and/or async context managers.
195195 f = stack.enter_context(open (' file' ))
196196 ...
197197
198- However, :class: `~contextlib.AsyncExitStack ` introduces significant complexity
199- and potential for errors - it's easy to violate properties that syntactic use
198+ However, :class: `~contextlib.AsyncExitStack ` introduces significant complexity
199+ and potential for errors - it's easy to violate properties that syntactic use
200200of context managers would guarantee, such as 'last-in, first-out' order.
201201
202202
@@ -220,27 +220,32 @@ It also requires either distinguishing sync from async context managers using
220220something like a tagged union - perhaps overload an operator so that, e.g.,
221221``async_ @ acquire_lock() `` works - or else guessing what to do with objects
222222that implement both sync and async context-manager protocols.
223+ Finally, it has the error-prone semantics around exception handling which led
224+ `contextlib.nested() `__ to be deprecated in favor of the multi-argument
225+ ``with `` statement.
226+
227+ __ https://docs.python.org/2.7/library/contextlib.html#contextlib.nested
223228
224229
225230Syntax: allow ``async with sync_cm, async_cm: ``
226231-----------------------------------------------
227232
228233An early draft of this proposal used ``async with `` for the entire statement
229- when mixing context managers, *if * there is at least one async cm :
234+ when mixing context managers, *if * there is at least one async context manager :
230235
231236.. code-block :: python
232237
233238 # Rejected approach
234239 async with (
235240 acquire_lock(),
236- open (' config.json' ) as f, # actually a sync cm , surprise!
241+ open (' config.json' ) as f, # actually sync, surprise!
237242 ):
238243 ...
239244
240- Requiring an async cm maintains the syntax/scheduler link, but at the cost
241- of setting invisible constraints on future code changes. Removing one of
242- several context managers could cause runtime errors, if that happened to be
243- the last async cm !
245+ Requiring an async context manager maintains the syntax/scheduler link, but at
246+ the cost of setting invisible constraints on future code changes. Removing
247+ one of several context managers could cause runtime errors, if that happened
248+ to be the last async context manager !
244249
245250Explicit is better than implicit.
246251
@@ -267,7 +272,7 @@ Thanks to Rob Rolls for `proposing`__ ``with async``. Thanks also to the many
267272other people with whom we discussed this problem and possible solutions at the
268273PyCon 2025 sprints, on Discuss, and at work.
269274
270- __: https://discuss.python.org/t/allow-mixed-sync-async-context-managers-in-async-with-statements/92939/10
275+ __ https://discuss.python.org/t/allow-mixed-sync-async-context-managers-in-async-with-statements/92939/10
271276
272277
273278Copyright
0 commit comments