Elaborate transaction() manager example (Chris A).

This commit is contained in:
Guido van Rossum 2014-11-28 17:26:33 -08:00
parent a6b3c8b1f7
commit 90addf57a2
1 changed files with 22 additions and 9 deletions

View File

@ -44,26 +44,39 @@ limitation, but notes that "use cases for these [are] rare to non-
existent". Unfortunately while intentional use is rare, it is easy to
stumble on these cases by accident::
import contextlib
@contextlib.contextmanager
def transaction():
begin()
print('begin')
try:
yield from do_it()
except:
rollback()
print('rollback')
raise
else:
commit()
print('commit')
def do_it():
initial_preparations()
yield
finishing_touches()
print('Refactored preparations')
yield # Body of with-statement is executed here
print('Refactored finalization')
def gene():
for i in range(2):
with transaction():
yield i
# return
raise StopIteration # This is wrong
print('Should not be reached')
for i in gene():
print('main: i =', i)
Here factoring out ``do_it`` into a subgenerator has introduced a
subtle bug: if the wrapped block raises ``StopIteration``, under the
current behavior ``do_it`` will fail but report success by returning
normally, causing the failed transaction to be committed! Similarly
current behavior this exception will be swallowed by the context
manager; and, worse, the finalization is silently skipped! Similarly
problematic behavior occurs when an ``asyncio`` coroutine raises
``StopIteration``, causing it to terminate silently.