After reading the with
statement section of the language documentation of Python, I was wondering if it is correct to state that this Python code:
with EXPRESSION as TARGET:
SUITE
is equivalent to this one:
try:
manager = (EXPRESSION)
value = manager.__enter__()
TARGET = value # only if `as TARGET` is present in the with statement
SUITE
except:
import sys
if not manager.__exit__(*sys.exc_info()):
raise
else:
manager.__exit__(None, None, None)
The correct equivalent Python code (the real CPython implementation is in C) was actually given by Guido van Rossum himself in PEP 343:
mgr = (EXPR)
exit = type(mgr).__exit__ # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
VAR = value # Only if "as VAR" is present
BLOCK
except:
# The exceptional case is handled here
exc = False
if not exit(mgr, *sys.exc_info()):
raise
# The exception is swallowed if exit() returns true
finally:
# The normal and non-local-goto cases are handled here
if exc:
exit(mgr, None, None, None)
Since Python 3.6, this has changed a little: now the __enter__
function is loaded before the __exit__
function (cf. https://bugs.python.org/issue27100).
So my equivalent Python code had three flaws:
__enter__
and __exit__
functions should be loaded before calling the __enter__
function.__enter__
function should be called outside the try
statement (cf. the note of point 4 in the language documentation).else
clause should instead be a finally
clause, to handle the case when there is a non-local goto statement (break
, continue
, return
) in suite
.However I don’t understand why the equivalent Python code in PEP 343 puts the finally
clause in an outer try
statement instead of in the inner try
statement?
Nick Coghlan, the other author of PEP 343, answered on the Python bug tracker:
It's a matter of historical timing: PEP 343 was written before try/except/finally was allowed, when try/finally and try/except were still distinct statements.
However, PEP 341 was also accepted and implemented for Python 2.5, allowing for the modern try/except/finally form: https://docs.python.org/dev/whatsnew/2.5.html#pep-341-unified-try-except-finally
So the modern try
statement equivalent Python code of the with
statement is this one:
manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
hit_except = False
try:
TARGET = value # only if `as TARGET` is present in the with statement
SUITE
except:
import sys
hit_except = True
if not exit(manager, *sys.exc_info()):
raise
finally:
if not hit_except:
exit(manager, None, None, None)