bpo-39037: Fix lookup order of magic methods in with statement documentation (GH-17608)

* __enter__ is now looked up before __exit__ to give a more intuitive error message
* add pseudo-code equivalent for the with statement
* fix pseudo-code for the async with statement to use a finally clause
* use SUITE rather than BLOCK for consistency with the language grammar

Patch by Géry Ogam.
This commit is contained in:
Géry Ogam 2019-12-30 05:24:51 +00:00 committed by Nick Coghlan
parent 32a12aed6d
commit 226e6e7d43

View file

@ -399,6 +399,8 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follo
#. The context expression (the expression given in the :token:`with_item`) is
evaluated to obtain a context manager.
#. The context manager's :meth:`__enter__` is loaded for later use.
#. The context manager's :meth:`__exit__` is loaded for later use.
#. The context manager's :meth:`__enter__` method is invoked.
@ -430,17 +432,41 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follo
value from :meth:`__exit__` is ignored, and execution proceeds at the normal
location for the kind of exit that was taken.
The following code::
with EXPRESSION as TARGET:
SUITE
is semantically equivalent to::
manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not exit(manager, *sys.exc_info()):
raise
finally:
if not hit_except:
exit(manager, None, None, None)
With more than one item, the context managers are processed as if multiple
:keyword:`with` statements were nested::
with A() as a, B() as b:
suite
SUITE
is equivalent to ::
is semantically equivalent to::
with A() as a:
with B() as b:
suite
SUITE
.. versionchanged:: 3.1
Support for multiple context expressions.
@ -772,24 +798,25 @@ iterators.
The following code::
async for TARGET in ITER:
BLOCK
SUITE
else:
BLOCK2
SUITE2
Is semantically equivalent to::
iter = (ITER)
iter = type(iter).__aiter__(iter)
running = True
while running:
try:
TARGET = await type(iter).__anext__(iter)
except StopAsyncIteration:
running = False
else:
BLOCK
SUITE
else:
BLOCK2
SUITE2
See also :meth:`__aiter__` and :meth:`__anext__` for details.
@ -811,23 +838,27 @@ able to suspend execution in its *enter* and *exit* methods.
The following code::
async with EXPR as VAR:
BLOCK
async with EXPRESSION as TARGET:
SUITE
Is semantically equivalent to::
is semantically equivalent to::
mgr = (EXPR)
aexit = type(mgr).__aexit__
aenter = type(mgr).__aenter__(mgr)
manager = (EXPRESSION)
aexit = type(manager).__aexit__
aenter = type(manager).__aenter__
value = await aenter(manager)
hit_except = False
VAR = await aenter
try:
BLOCK
TARGET = value
SUITE
except:
if not await aexit(mgr, *sys.exc_info()):
hit_except = True
if not await aexit(manager, *sys.exc_info()):
raise
else:
await aexit(mgr, None, None, None)
finally:
if not hit_except:
await aexit(manager, None, None, None)
See also :meth:`__aenter__` and :meth:`__aexit__` for details.