Solidify loose ends.
This commit is contained in:
parent
79c7f789bd
commit
45c25ac443
99
pep-0340.txt
99
pep-0340.txt
|
@ -22,10 +22,6 @@ Introduction
|
|||
(Reliable Acquisition/Release Pairs), and PEP 325
|
||||
(Resource-Release Support for Generators).
|
||||
|
||||
This proposal is just a strawman; we've had a heated debate about
|
||||
this on python-dev recently [1], and I figured it would be time to
|
||||
write up a precise spec in PEP form.
|
||||
|
||||
Motivation and Summary
|
||||
|
||||
(Thanks to Shane Hathaway -- Hi Shane!)
|
||||
|
@ -70,7 +66,7 @@ Motivation and Summary
|
|||
|
||||
Use Cases
|
||||
|
||||
TBD. For now, see the Examples section near the end.
|
||||
See the Examples section near the end.
|
||||
|
||||
Specification: the __next__() Method
|
||||
|
||||
|
@ -159,14 +155,15 @@ Specification: the Extended 'continue' Statement
|
|||
This is also the case in the body of the block-statement proposed
|
||||
below.
|
||||
|
||||
EXPR2 may contain commas; "continue 1, 2, 3" is equivalent to
|
||||
"continue (1, 2, 3)".
|
||||
|
||||
Specification: the Anonymous Block Statement
|
||||
|
||||
A new statement is proposed with the syntax
|
||||
|
||||
block EXPR1 as VAR1:
|
||||
BLOCK1
|
||||
else:
|
||||
BLOCK2
|
||||
|
||||
Here, 'block' and 'as' are new keywords; EXPR1 is an arbitrary
|
||||
expression (but not an expression-list) and VAR1 is an arbitrary
|
||||
|
@ -268,14 +265,14 @@ Specification: the Anonymous Block Statement
|
|||
the limitations of all finalization semantics) that the block will
|
||||
be resumed eventually.
|
||||
|
||||
I haven't decided yet whether the block-statement should also
|
||||
allow an optional else-clause, like the for-loop, but I'm leaning
|
||||
against it. I think it would be confusing, and emphasize the
|
||||
Unlike the for-loop, the block-statement does not have an
|
||||
else-clause. I think it would be confusing, and emphasize the
|
||||
"loopiness" of the block-statement, while I want to emphasize its
|
||||
*difference* from a for-loop. In addition, there are several
|
||||
possible semantics for an else-clause.
|
||||
possible semantics for an else-clause, and only a very weak use
|
||||
case.
|
||||
|
||||
Specification: Generator Exception Handling
|
||||
Specification: Generator Exit Handling
|
||||
|
||||
Generators will implement the new __next__() method API, as well
|
||||
as the old argument-less next() method which becomes an alias for
|
||||
|
@ -330,11 +327,10 @@ Specification: Generator Exception Handling
|
|||
When __next__() is called with an argument that is not None, the
|
||||
yield-expression that it resumes will return the value attribute
|
||||
of the argument. If it resumes a yield-statement, the value is
|
||||
ignored (or should this be considered an error?). When the
|
||||
*initial* call to __next__() receives an argument that is not
|
||||
None, the generator's execution is started normally; the
|
||||
argument's value attribute is ignored (or should this be
|
||||
considered an error?). When __next__() is called without an
|
||||
ignored (this is similar to ignoring the value returned by a
|
||||
function call). When the *initial* call to __next__() receives an
|
||||
argument that is not None, TypeError is raised; this is likely
|
||||
caused by some logic error. When __next__() is called without an
|
||||
argument or with None as argument, and a yield-expression is
|
||||
resumed, the yield-expression returns None.
|
||||
|
||||
|
@ -360,22 +356,20 @@ Specification: Generator Exception Handling
|
|||
cases work differently; in Python, you cannot save the block for
|
||||
later use, and you cannot test whether there is a block or not.
|
||||
|
||||
Loose Ends
|
||||
Alternatives Considered
|
||||
|
||||
These are things that need to be resolved before accepting the
|
||||
PEP.
|
||||
- Many alternatives have been proposed for 'block', including '@'
|
||||
and no keyword at all. I haven't seen a proposal for another
|
||||
keyword that I like better than 'block' yet, and not using a
|
||||
keyword at all makes many folks (including me) uncomfortable.
|
||||
|
||||
- Fill in the remaining TBD sections.
|
||||
|
||||
- Address Phillip Eby's proposal to have the block-statement use
|
||||
- Phillip Eby has proposed to have the block-statement use
|
||||
an entirely different API than the for-loop, to differentiate
|
||||
between the two (a generator would have to be wrapped in a
|
||||
decorator to make it support the block API).
|
||||
|
||||
- Decide on the keyword ('block', 'with', '@', nothing, or
|
||||
something else?).
|
||||
|
||||
- Whether a block-statement should allow an else-clause.
|
||||
between the two. A generator would have to be wrapped in a
|
||||
decorator to make it support the block API. IMO this adds more
|
||||
complexity with very little benefit; and we can't relly deny
|
||||
that the block-statement is conceptually a loop -- it supports
|
||||
break and continue, after all.
|
||||
|
||||
Comparison to Thunks
|
||||
|
||||
|
@ -418,7 +412,7 @@ Comparison to Thunks
|
|||
and I'd be bummed if I couldn't write this as:
|
||||
|
||||
def findSomething(self, key, default=None):
|
||||
block synchronized(self.lock):
|
||||
block locking(self.lock):
|
||||
for item in self.elements:
|
||||
if item.matches(key):
|
||||
return item
|
||||
|
@ -427,7 +421,7 @@ Comparison to Thunks
|
|||
This particular example can be rewritten using a break:
|
||||
|
||||
def findSomething(self, key, default=None):
|
||||
block synchronized(self.lock):
|
||||
block locking(self.lock):
|
||||
for item in self.elements:
|
||||
if item.matches(key):
|
||||
break
|
||||
|
@ -478,16 +472,17 @@ Comparison to Thunks
|
|||
|
||||
However, the use cases for multiple blocks seem elusive.
|
||||
|
||||
Alternatives Considered
|
||||
|
||||
TBD.
|
||||
(Proposals have since been made to change the implementation of
|
||||
thunks to remove most of these objections, but the resulting
|
||||
semantics are fairly complex to explain and to implement, so IMO
|
||||
that defeats the purpose of using thunks in the first place.)
|
||||
|
||||
Examples
|
||||
|
||||
1. A template for ensuring that a lock, acquired at the start of a
|
||||
block, is released when the block is left:
|
||||
|
||||
def synchronized(lock):
|
||||
def locking(lock):
|
||||
lock.acquire()
|
||||
try:
|
||||
yield
|
||||
|
@ -496,7 +491,7 @@ Examples
|
|||
|
||||
Used as follows:
|
||||
|
||||
block synchronized(myLock):
|
||||
block locking(myLock):
|
||||
# Code here executes with myLock held. The lock is
|
||||
# guaranteed to be released when the block is left (even
|
||||
# if by an uncaught exception).
|
||||
|
@ -549,18 +544,40 @@ Examples
|
|||
|
||||
5. It is possible to nest blocks and combine templates:
|
||||
|
||||
def synchronized_opening(lock, filename, mode="r"):
|
||||
block synchronized(lock):
|
||||
def locking_opening(lock, filename, mode="r"):
|
||||
block locking(lock):
|
||||
block opening(filename) as f:
|
||||
yield f
|
||||
|
||||
Used as follows:
|
||||
|
||||
block synchronized_opening("/etc/passwd", myLock) as f:
|
||||
block locking_opening("/etc/passwd", myLock) as f:
|
||||
for line in f:
|
||||
print line.rstrip()
|
||||
|
||||
6. Coroutine example TBD.
|
||||
6. It is possible to write a regular iterator with the
|
||||
semantics of example 1:
|
||||
|
||||
class locking:
|
||||
def __init__(self, lock):
|
||||
self.lock = lock
|
||||
self.state = 0
|
||||
def __next__(self, arg=None):
|
||||
# ignores arg
|
||||
if self.state:
|
||||
assert self.state == 1
|
||||
self.lock.release()
|
||||
self.state += 1
|
||||
raise StopIteration
|
||||
else:
|
||||
self.lock.acquire()
|
||||
self.state += 1
|
||||
return None
|
||||
def __exit__(self, type, value=None, traceback=None):
|
||||
assert self.state in (0, 1, 2)
|
||||
if self.state == 1:
|
||||
self.lock.release()
|
||||
raise type, value, traceback
|
||||
|
||||
Acknowledgements
|
||||
|
||||
|
|
Loading…
Reference in New Issue