Change the PEP to add that __exit__() must re-raise the exception

passed in, if any.  This appears to be a deviation from how the PEP
was originally seen; but I think it's essential to be able to make a
simple promise about generators decorated with @contextmanager.

Added the transition plan and the __future__ statement.

Also some miscellaneous cleanup.
This commit is contained in:
Guido van Rossum 2006-02-28 20:58:17 +00:00
parent 12bffcefbe
commit f038797f00
1 changed files with 45 additions and 16 deletions

View File

@ -227,21 +227,22 @@ Specification: The 'with' Statement
ctx = (EXPR).__context__()
exit = exc.__exit__ # Not calling it yet
res = ctx.__enter__()
value = ctx.__enter__()
exc = (None, None, None)
try:
try:
VAR = res # Only if "as VAR" is present
VAR = value # Only if "as VAR" is present
BLOCK
except:
exc = sys.exc_info()
raise
exc = None
exit(*sys.exc_info())
finally:
if exc:
exit(*exc)
Here, the lowercase variables are internal variables and not
accessible to the user; they will most likely be implemented as
special registers or stack positions.
Here, the lowercase variables (ctx, exit, value, exc) are internal
variables and not accessible to the user; they will most likely be
implemented as special registers or stack positions.
The details of the above translation are intended to prescribe the
exact semantics. If any of the relevant methods are not found as
@ -264,20 +265,32 @@ Specification: The 'with' Statement
object each time its __context__ method is invoked.
If the "as VAR" part of the syntax is omitted, the "VAR =" part of
the translation is omitted (but abc.__enter__() is still called).
the translation is omitted (but ctx.__enter__() is still called).
The calling convention for abc.__exit__() is as follows. If the
The calling convention for ctx.__exit__() is as follows. If the
finally-suite was reached through normal completion of BLOCK or
through a non-local goto (a break, continue or return statement in
BLOCK), abc.__exit__() is called with three None arguments. If
BLOCK), ctx.__exit__() is called with three None arguments. If
the finally-suite was reached through an exception raised in
BLOCK, abc.__exit__() is called with three arguments representing
BLOCK, ctx.__exit__() is called with three arguments representing
the exception type, value, and traceback.
The motivation for this API to __exit__(), as opposed to the
argument-less __exit__() from PEP 310, was given by the
transactional() use case, example 3 below. The template in that
example must commit or roll back the transaction depending on
IMPORTANT: if ctx.__exit__() is passed exception information, it
*must* re-raise the exception, unless it wants the exception to be
ignored. That is, if ctx.__exit__() returns normally, execution
continues at the next statement after the with-statement, even if
an exception happened inside the with-statement. However, if the
with-statement was left via a non-local goto (break, continue or
return), this non-local return is resumed when ctx.__exit__()
returns normally. The motivation for this detail is to make a
try/except block in a context manager created from a decorated
generator behave exactly as if the body of the generator were
expanded in-line at the place of the with-statement.
The motivation for passing the exception details to __exit__(), as
opposed to the argument-less __exit__() from PEP 310, was given by
the transactional() use case, example 3 below. The template in
that example must commit or roll back the transaction depending on
whether an exception occurred or not. Rather than just having a
boolean flag indicating whether an exception occurred, we pass the
complete exception information, for the benefit of an
@ -291,6 +304,20 @@ Specification: The 'with' Statement
non-local goto should be considered unexceptional for the purposes
of a database transaction roll-back decision.
Transition Plan
In Python 2.5, the new syntax will only be recognized if a future
statement is present:
from __future__ import with_statement
This will make both 'with' and 'as' keywords. Without the future
statement, using 'with' or 'as' as an identifier will cause a
DeprecationWarning to be issued.
In Python 2.6, the new syntax will always be recognized; 'with'
and 'as' are always keywords.
Generator Decorator
With PEP 342 accepted, it is possible to write a decorator
@ -394,6 +421,7 @@ Optional Extensions
- threading.Lock
- threading.RLock
- threading.Condition
- threading.Semaphore and threading.BoundedSemaphore
Standard Terminology
@ -598,6 +626,7 @@ Examples
yield None
except:
db.rollback()
raise
else:
db.commit()