[Python-checkins] python/nondist/peps pep-0343.txt,1.3,1.4
gvanrossum@users.sourceforge.net
gvanrossum at users.sourceforge.net
Sat May 14 07:02:31 CEST 2005
Update of /cvsroot/python/python/nondist/peps
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16548
Modified Files:
pep-0343.txt
Log Message:
Add a motivational section, remove tabs, add colons, and some very
minor edits.
Index: pep-0343.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/peps/pep-0343.txt,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- pep-0343.txt 14 May 2005 04:02:10 -0000 1.3
+++ pep-0343.txt 14 May 2005 05:02:28 -0000 1.4
@@ -17,7 +17,103 @@
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
@@ -25,7 +121,7 @@
Specification
- A new statement is proposed with the syntax
+ A new statement is proposed with the syntax:
do EXPR as VAR:
BLOCK
@@ -53,7 +149,7 @@
BLOCK
except:
exc = sys.exc_info()
- raise
+ raise
finally:
abc.__exit__(exc)
@@ -66,7 +162,7 @@
The calling convention for abc.__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
+ through a non-local goto (a break, continue or return statement
in BLOCK), abc.__exit__() is called without arguments (or perhaps
with three None arguments?). If the finally-suite was reached
through an exception raised in BLOCK, abc.__exit__() is called
@@ -122,18 +218,18 @@
It would be possible to endow certain objects, like files,
sockets, and locks, with __enter__ and __exit__ methods so that
- instead of writing
+ instead of writing:
do locking(myLock):
BLOCK
- one could write simply
+ one could write simply:
do myLock:
BLOCK
I think we should be careful with this; it could lead to mistakes
- like
+ like:
f = open(filename)
do f:
@@ -264,6 +360,10 @@
as an exercise for the reader. (Mail it to me if you'd like to
see it here.)
+References
+
+ [1] http://blogs.msdn.com/oldnewthing/archive/2005/01/06/347666.aspx
+
Copyright
This document has been placed in the public domain.
More information about the Python-checkins
mailing list