[Python-Dev] Merging PEP 310 and PEP 340-redux?
Michael Hudson
mwh at python.net
Fri May 13 15:12:34 CEST 2005
Guido van Rossum <gvanrossum at gmail.com> writes:
> I just read Raymond Chen's rant against control flow macros:
> http://blogs.msdn.com/oldnewthing/archive/2005/01/06/347666.aspx
>
> I think this pretty much kills PEP 340, as well as Nick Coghlan's
> alternative: both proposals let you write a "template" that can be
> used to hide exception-catching code, which is a form of control flow
> (and a pretty important one if you read Chen's rant against exceptions
> referenced by the former, even if you don't agree with everything he
> says in the latter).
Well, I'm not sure what the content of the latter article is, other
than "getting things right can be hard".
BTW, the "else:" on try statements is so very handy for getting this
sort of thing (more) corrent.
> Which leaves us, IMO, with the choice between PEP 310 and my own
> "PEP-340-redux" proposal; these *only* introduce a finally-clause,
> which does not affect the control flow. I'm not counting exceptions
> that might happen in the finally-clause; exceptions can happen
> anywhere anyway. But I am counting the *catching* of an exception as
> control flow, since that means that code past BLOCK (in the same
> function) is reachable even if BLOCK was not executed to completion;
> and this is the argument against PEP 340 and against Nick's
> alternative.
>
> Let's compare and contrast the two remaining competitors:
>
> PEP 310
> =======
>
> Syntax:
> with EXPR [= VAR]:
> BLOCK
>
> Translation:
> [VAR =] abc = EXPR
> if hasattr(abc, "__enter__"):
> abc.__enter__()
> try:
> BLOCK
> finally:
> abc.__exit__()
>
> Pros:
> - dead simple
>
> Cons:
> - can't use a decorated generator for EXPR
Sorry, why not? [note: I work this out, below, but I still think the
code is worth posting]
import sys
class BlockTemplate(object):
def __init__(self, g, args, kw):
self.g = g
self.args = args
self.kw = kw
def __enter__(self):
self.giter = self.g(*self.args)
self.giter.next()
def __exit__(self):
try:
self.giter.next()
except StopIteration:
pass
else:
raise RuntimeError, "generator not exhausted"
def template(g):
def _(*args, **kw):
return BlockTemplate(g, args, kw)
return _
@template
def redirected_stdout(out):
print 'hi'
save_stdout = sys.stdout
sys.stdout = out
yield None
sys.stdout = save_stdout
print 'ho'
## with redirected_stdout(fileobj):
## print 1
output = open("foo", "w")
abc = redirected_stdout(output)
abc.__enter__()
try:
print 1
finally:
abc.__exit__()
output.close()
print repr(open("foo").read())
(this was a bit harder to get right than I expected, mind).
Oh, I guess the point is that with a decorated generator you can yield
a value to be used as VAR, rather than just discarding the value as
here. Hmm.
> PEP 340 redux
> =============
>
> Syntax:
> do EXPR [as VAR]:
> BLOCK
>
> Translation:
> abc = EXPR
> [VAR =] abc.__enter__()
> try:
> BLOCK
> finally:
> abc.__exit__(*"sys.exc_info()") # Not exactly
These two expansions look very similar to me. What am I missing?
> Pros:
> - can use a decorated generator as EXPR
> - separation of EXPR and VAR (VAR gets what EXPR.__enter__() returns)
Oh! Hmm. This is a bit subtle.
I guess I should think about some examples.
> Cons:
> - slightly less simple (__enter__ must return something for VAR;
> __exit__ takes optional args)
If things were fiddled such that sys.exc_info() return non-Nones when
a finally clause is being executed because of an exception, we don't
really need this wart, do we?
> Everything else is equal or can be made equal. We can make them more
> equal by treating the arguments passed to __exit__() as a separate
> decision, and waffling about whether __enter__() should be optional (I
> think it's a bad idea even for PEP 310; it *could* be made optional
> for PEP 340 redux).
I don't really recall why it's optional in PEP 310.
> Let's also not quibble about the keyword used; again, that can be a
> separate decision. Note that only PEP 310 can use the "VAR = EXPR"
> syntax; PEP 340 redux *must* use "EXPR as VAR" since it doesn't assign
> the value of EXPR to VAR; PEP 310 can be rewritten using this syntax
> as well.
>
> So then the all-important question I want to pose is: do we like the
> idea of using a (degenerate, decorated) generator as a "template" for
> the do-statement enough to accept the slightly increased complexity?
Looking at my above code, no (even though I think I've rendered the
point moot...). Compare and contrast:
@template
def redirected_stdout(out):
save_stdout = sys.stdout
sys.stdout = out
yield None
sys.stdout = save_stdout
class redirected_stdout(object):
def __init__(self, output):
self.output = output
def __enter__(self):
self.save_stdout = sys.stdout
sys.stdout = self.output
def __exit__(self):
sys.stdout = self.save_stdout
The former is shorter and contains less (well, no) 'self.'s, but I
think I find the latter somewhat clearer.
> The added complexity is caused by the need to separate VAR from EXPR
> so that a generator can be used. I personally like this separation; I
> actually like that the "anonymous block controller" is logically
> separate from the variable bound by the construct.
Nevertheless, I think I actually like this argument!
> From Greg Ewing's response to the proposal to endow file objects
> with __enter__ and __exit__ methods, I believe he thinks so too.
>
> Straight up-or-down votes in the full senate are appreciated at this point.
+1 for the PEP 340 variant.
> On to the secondary questions:
>
> - Today I like the 'do' keyword better; 'with' might confuse folks
> coming from Pascal or VB
No opinion.
> - I have a more elaborate proposal for __exit__'s arguments. Let the
> translation be as follows:
>
> abc = EXPR
> [VAR =] abc.__enter__()
> oke = False # Pronounced "okay"
> exc = ()
> try:
> try:
> BLOCK
> oke = True
> except:
> exc = sys.exc_info()
> raise
> finally:
> abc.__exit__(oke, *exc)
-"a bit"
> PS. I've come up with another interesting use case: block signals for
> the duration of a block. This could be a function in the signal
> module, e.g. signal.blocking([ist of signals to block]). The list
> would default to all signals. Similar signal.ignoring().
First you need to hit the authors of various libcs with big sticks.
Cheers,
mwh
--
<shapr> ucking keyoar
-- from Twisted.Quotes
More information about the Python-Dev
mailing list