Skip to content

Commit 226e6e7

Browse files
geryogamncoghlan
authored andcommitted
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.
1 parent 32a12ae commit 226e6e7

File tree

1 file changed

+49
-18
lines changed

1 file changed

+49
-18
lines changed

Doc/reference/compound_stmts.rst

+49-18
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follo
399399
#. The context expression (the expression given in the :token:`with_item`) is
400400
evaluated to obtain a context manager.
401401

402+
#. The context manager's :meth:`__enter__` is loaded for later use.
403+
402404
#. The context manager's :meth:`__exit__` is loaded for later use.
403405

404406
#. 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
430432
value from :meth:`__exit__` is ignored, and execution proceeds at the normal
431433
location for the kind of exit that was taken.
432434

435+
The following code::
436+
437+
with EXPRESSION as TARGET:
438+
SUITE
439+
440+
is semantically equivalent to::
441+
442+
manager = (EXPRESSION)
443+
enter = type(manager).__enter__
444+
exit = type(manager).__exit__
445+
value = enter(manager)
446+
hit_except = False
447+
448+
try:
449+
TARGET = value
450+
SUITE
451+
except:
452+
hit_except = True
453+
if not exit(manager, *sys.exc_info()):
454+
raise
455+
finally:
456+
if not hit_except:
457+
exit(manager, None, None, None)
458+
433459
With more than one item, the context managers are processed as if multiple
434460
:keyword:`with` statements were nested::
435461

436462
with A() as a, B() as b:
437-
suite
463+
SUITE
438464

439-
is equivalent to ::
465+
is semantically equivalent to::
440466

441467
with A() as a:
442468
with B() as b:
443-
suite
469+
SUITE
444470

445471
.. versionchanged:: 3.1
446472
Support for multiple context expressions.
@@ -772,24 +798,25 @@ iterators.
772798
The following code::
773799

774800
async for TARGET in ITER:
775-
BLOCK
801+
SUITE
776802
else:
777-
BLOCK2
803+
SUITE2
778804

779805
Is semantically equivalent to::
780806

781807
iter = (ITER)
782808
iter = type(iter).__aiter__(iter)
783809
running = True
810+
784811
while running:
785812
try:
786813
TARGET = await type(iter).__anext__(iter)
787814
except StopAsyncIteration:
788815
running = False
789816
else:
790-
BLOCK
817+
SUITE
791818
else:
792-
BLOCK2
819+
SUITE2
793820

794821
See also :meth:`__aiter__` and :meth:`__anext__` for details.
795822

@@ -811,23 +838,27 @@ able to suspend execution in its *enter* and *exit* methods.
811838

812839
The following code::
813840

814-
async with EXPR as VAR:
815-
BLOCK
841+
async with EXPRESSION as TARGET:
842+
SUITE
816843

817-
Is semantically equivalent to::
844+
is semantically equivalent to::
818845

819-
mgr = (EXPR)
820-
aexit = type(mgr).__aexit__
821-
aenter = type(mgr).__aenter__(mgr)
846+
manager = (EXPRESSION)
847+
aexit = type(manager).__aexit__
848+
aenter = type(manager).__aenter__
849+
value = await aenter(manager)
850+
hit_except = False
822851

823-
VAR = await aenter
824852
try:
825-
BLOCK
853+
TARGET = value
854+
SUITE
826855
except:
827-
if not await aexit(mgr, *sys.exc_info()):
856+
hit_except = True
857+
if not await aexit(manager, *sys.exc_info()):
828858
raise
829-
else:
830-
await aexit(mgr, None, None, None)
859+
finally:
860+
if not hit_except:
861+
await aexit(manager, None, None, None)
831862

832863
See also :meth:`__aenter__` and :meth:`__aexit__` for details.
833864

0 commit comments

Comments
 (0)