Add a motivational section, remove tabs, add colons, and some very

minor edits.
This commit is contained in:
Guido van Rossum 2005-05-14 05:02:28 +00:00
parent 3df485114c
commit 33cff6d943
1 changed files with 107 additions and 7 deletions

View File

@ -17,7 +17,103 @@ Introduction
Motivation and Summary Motivation and Summary
TBD. PEP 340, Anonymous Block Statements, combined many powerful ideas:
using generators as block templates, adding exception handling and
finalization to generators, and more. Besides praise it received
a lot of opposition from people who didn't like the fact that it
was, under the covers, a (optential) looping construct. This
meant that break and continue in a block-statement would break or
continue the block-statement, even if it was used as a non-looping
resource management tool.
But the final blow came when I read Raymond Chen's rant about
flow-control macros[1]. Raymond argues convincingly that hiding
flow control in macros makes your code inscrutable, and I find
that his argument applies to Python as well as to C. I realized
that PEP 340 templates can hide all sorts of control flow; for
example, its example 4 (auto_retry()) catches exceptions and
repeats the block up to three times.
However, the with-statement of PEP 310 does *not* hide control
flow, in my view: while a finally-suite temporarily suspends the
control flow, in the end, the control flow resumes as if the
finally-suite wasn't there at all. Consider this:
with VAR = EXPR:
BLOCK1
BLOCK2
Here, just as if the first line was "if True" instead, we know
that if BLOCK1 completes without an exception, BLOCK2 will be
reached; and if BLOCK1 raises an exception or executes a
non-local goto (a break, continue or return), BLOCK2 is *not*
reached. The magic added by the with-statement at the end doesn't
affect this.
(You may ask, what if a bug in the __exit__ method causes an
exception? Then all is lost -- but this is no worse than with
other exceptions; the nature of exceptions is that they can happen
*anywhere*, and you just have to live with that. Even if you
write bug-free code, a KeyboardInterrupt exception can still cause
it to exit between any two virtual machine opcodes.)
This argument almost led me to endorse PEP 310, but I had one idea
left from the PEP 340 euphoria that I wasn't ready to drop: using
generators as "templates" for abstractions like acquiring and
releasing a lock or opening and closing a file is a powerful idea,
as can be seen by looking at the examples in that PEP.
Inspired by a counter-proposal to PEP 340 by Phillip Eby I tried
to create a decorator that would turn a suitable generator into an
object with the necessary __entry__ and __exit__ methods. Here I
ran into a snag: while it wasn't too hard for the locking example,
it was impossible to do this for the opening example. The idea
was to define the template like this:
@with_template
def opening(filename):
f = open(filename)
yield f
f.close()
and used it like this:
with f = opening(filename):
...read data from f...
The problem is that in PEP 310, the result of calling EXPR is
assigned directly to VAR, and then VAR's __exit__ method is called
upon exit from BLOCK1. But here, VAR clearly needs to receive the
opened file, and that would mean that __exit__ would have to be a
method on the file.
While this can be solved using a proxy class, this is awkward and
made me realize that a slightly different translation would make
writing the desired decorator a piece of cake: let VAR receive the
result from calling the __enter__ method, and save the value of
EXPR to call its __exit__ method later. Then the decorator can
return an instance of a wrapper class whose __enter__ method calls
the generator's next() method and returns whatever next() returns;
the wrapper instance's __exit__ method calls next() again but
expects it to raise StopIteration. (Details below in the section
Optional Generator Decorator.)
So now the final hurdle was that the PEP 310 syntax:
with VAR = EXPR:
BLOCK1
would be deceptive, since VAR does *not* receive the value of
EXPR. Given PEP 340, it was an easy step to:
with EXPR as VAR:
BLOCK1
or, using an alternate keyword that has been proposed a number of
times:
do EXPR as VAR:
BLOCK1
Use Cases Use Cases
@ -25,7 +121,7 @@ Use Cases
Specification Specification
A new statement is proposed with the syntax A new statement is proposed with the syntax:
do EXPR as VAR: do EXPR as VAR:
BLOCK BLOCK
@ -53,7 +149,7 @@ Specification
BLOCK BLOCK
except: except:
exc = sys.exc_info() exc = sys.exc_info()
raise raise
finally: finally:
abc.__exit__(exc) abc.__exit__(exc)
@ -66,7 +162,7 @@ Specification
The calling convention for abc.__exit__() is: as follows. If the The calling convention for abc.__exit__() is: as follows. If the
finally-suite was reached through normal completion of BLOCK or finally-suite was reached through normal completion of BLOCK or
through a "non-local goto" (a break, continue or return statement through a non-local goto (a break, continue or return statement
in BLOCK), abc.__exit__() is called without arguments (or perhaps in BLOCK), abc.__exit__() is called without arguments (or perhaps
with three None arguments?). If the finally-suite was reached with three None arguments?). If the finally-suite was reached
through an exception raised in BLOCK, abc.__exit__() is called through an exception raised in BLOCK, abc.__exit__() is called
@ -122,18 +218,18 @@ Other Optional Extensions
It would be possible to endow certain objects, like files, It would be possible to endow certain objects, like files,
sockets, and locks, with __enter__ and __exit__ methods so that sockets, and locks, with __enter__ and __exit__ methods so that
instead of writing instead of writing:
do locking(myLock): do locking(myLock):
BLOCK BLOCK
one could write simply one could write simply:
do myLock: do myLock:
BLOCK BLOCK
I think we should be careful with this; it could lead to mistakes I think we should be careful with this; it could lead to mistakes
like like:
f = open(filename) f = open(filename)
do f: do f:
@ -264,6 +360,10 @@ Examples
as an exercise for the reader. (Mail it to me if you'd like to as an exercise for the reader. (Mail it to me if you'd like to
see it here.) see it here.)
References
[1] http://blogs.msdn.com/oldnewthing/archive/2005/01/06/347666.aspx
Copyright Copyright
This document has been placed in the public domain. This document has been placed in the public domain.