Don't be wishy-washy about the call to __exit__().
Fix the redirecting_stdout() example (remove the try/finally).
This commit is contained in:
parent
3e348d5237
commit
e9086b2485
115
pep-0343.txt
115
pep-0343.txt
|
@ -46,23 +46,28 @@ Specification
|
||||||
The translation of the above statement is:
|
The translation of the above statement is:
|
||||||
|
|
||||||
abc = EXPR
|
abc = EXPR
|
||||||
|
exc = () # Or (None, None, None) ?
|
||||||
try:
|
try:
|
||||||
VAR = abc.__enter__()
|
try:
|
||||||
BLOCK
|
VAR = abc.__enter__()
|
||||||
|
BLOCK
|
||||||
|
except:
|
||||||
|
exc = sys.exc_info()
|
||||||
|
raise
|
||||||
finally:
|
finally:
|
||||||
abc.__exit__(*sys.exc_info()) # XXX See below
|
abc.__exit__(exc)
|
||||||
|
|
||||||
If the "as VAR" part of the syntax is omitted, the "VAR =" part of
|
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 abc.__enter__() is still called).
|
||||||
|
|
||||||
The call to abc.__exit__() is only approximated as written. The
|
The calling convention for abc.__exit__() is: as follows. If the
|
||||||
actual calling convention is: If the finally-suite was reached
|
finally-suite was reached through normal completion of BLOCK or
|
||||||
through normal completion of BLOCK or through a "non-local goto"
|
through a "non-local goto" (a break, continue or return statement
|
||||||
(a break, continue or return statement in BLOCK), abc.__exit__()
|
in BLOCK), abc.__exit__() is called without arguments (or perhaps
|
||||||
is called without arguments (or perhaps with three None
|
with three None arguments?). If the finally-suite was reached
|
||||||
arguments). If the finally-suite was reached through an exception
|
through an exception raised in BLOCK, abc.__exit__() is called
|
||||||
raised in BLOCK, abc.__exit__() is called with three arguments
|
with three arguments representing the exception type, value, and
|
||||||
representing the exception type, value, and traceback.
|
traceback.
|
||||||
|
|
||||||
Optional Generator Decorator
|
Optional Generator Decorator
|
||||||
|
|
||||||
|
@ -70,41 +75,41 @@ Optional Generator Decorator
|
||||||
a generator that yields exactly once to control a do-statement.
|
a generator that yields exactly once to control a do-statement.
|
||||||
Here's a sketch of such a decorator:
|
Here's a sketch of such a decorator:
|
||||||
|
|
||||||
class Wrapper(object):
|
class Wrapper(object):
|
||||||
def __init__(self, gen):
|
def __init__(self, gen):
|
||||||
self.gen = gen
|
self.gen = gen
|
||||||
self.state = "initial"
|
self.state = "initial"
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
assert self.state == "initial"
|
assert self.state == "initial"
|
||||||
self.state = "entered"
|
self.state = "entered"
|
||||||
try:
|
try:
|
||||||
return self.gen.next()
|
return self.gen.next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
self.state = "error"
|
self.state = "error"
|
||||||
raise RuntimeError("template generator didn't yield")
|
raise RuntimeError("template generator didn't yield")
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
assert self.state == "entered"
|
assert self.state == "entered"
|
||||||
self.state = "exited"
|
self.state = "exited"
|
||||||
try:
|
try:
|
||||||
self.gen.next()
|
self.gen.next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.state = "error"
|
self.state = "error"
|
||||||
raise RuntimeError("template generator didn't stop")
|
raise RuntimeError("template generator didn't stop")
|
||||||
|
|
||||||
def do_template(func):
|
def do_template(func):
|
||||||
def helper(*args, **kwds):
|
def helper(*args, **kwds):
|
||||||
return Wrapper(func(*args, **kwds))
|
return Wrapper(func(*args, **kwds))
|
||||||
return helper
|
return helper
|
||||||
|
|
||||||
This decorator could be used as follows:
|
This decorator could be used as follows:
|
||||||
|
|
||||||
@do_template
|
@do_template
|
||||||
def opening(filename):
|
def opening(filename):
|
||||||
f = open(filename) # IOError here is untouched by Wrapper
|
f = open(filename) # IOError here is untouched by Wrapper
|
||||||
yield f
|
yield f
|
||||||
f.close() # Ditto for errors here (however unlikely)
|
f.close() # Ditto for errors here (however unlikely)
|
||||||
|
|
||||||
A robust implementation of such a decorator should be made part of
|
A robust implementation of such a decorator should be made part of
|
||||||
the standard library.
|
the standard library.
|
||||||
|
@ -151,14 +156,14 @@ Examples
|
||||||
|
|
||||||
class transactional:
|
class transactional:
|
||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
self.db = db
|
self.db = db
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
pass
|
pass
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
if args and args[0] is not None:
|
if args and args[0] is not None:
|
||||||
self.db.rollback()
|
self.db.rollback()
|
||||||
else:
|
else:
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
4. Example 1 rewritten without a generator:
|
4. Example 1 rewritten without a generator:
|
||||||
|
|
||||||
|
@ -166,9 +171,9 @@ Examples
|
||||||
def __init__(self, lock):
|
def __init__(self, lock):
|
||||||
self.lock = lock
|
self.lock = lock
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
(This example is easily modified to implement the other
|
(This example is easily modified to implement the other
|
||||||
examples; it shows how much simpler generators are for the same
|
examples; it shows how much simpler generators are for the same
|
||||||
|
@ -179,11 +184,9 @@ Examples
|
||||||
@do_template
|
@do_template
|
||||||
def redirecting_stdout(new_stdout):
|
def redirecting_stdout(new_stdout):
|
||||||
save_stdout = sys.stdout
|
save_stdout = sys.stdout
|
||||||
try:
|
sys.stdout = new_stdout
|
||||||
sys.stdout = new_stdout
|
yield None
|
||||||
yield None
|
sys.stdout = save_stdout
|
||||||
finally:
|
|
||||||
sys.stdout = save_stdout
|
|
||||||
|
|
||||||
Used as follows:
|
Used as follows:
|
||||||
|
|
||||||
|
@ -200,7 +203,7 @@ Examples
|
||||||
except IOError, err:
|
except IOError, err:
|
||||||
yield None, err
|
yield None, err
|
||||||
else:
|
else:
|
||||||
yield f, None
|
yield f, None
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
Used as follows:
|
Used as follows:
|
||||||
|
|
Loading…
Reference in New Issue