Could Emacs be rewritten in Python?

Alex Martelli aleax at aleax.it
Mon Apr 14 18:46:40 CEST 2003


Robin Munn wrote:
   ...
> How is try ... finally "broken and fragile code"? How is this:
> 
>     try:
>         old_stdout = sys.stdout
>         new_stdout = cStringIO.StringIO()
>         sys.stdout = new_stdout
>         function_whose_output_I_want_to_capture()
>         do_something_with(new_stdout)
>     finally:
>         sys.stdout = old_stdout
> 
> in any way inferior to this (making up a semi-Pythonic syntax):
> 
>     with sys.stdout = cStringIO.StringIO():
>         function_whose_output_I_want_to_capture()
>         do_something_with(sys.stdout)
> 
> Yes, sample #2 is shorter. But it would require adding a new keyword to
> the language, which is a pretty heavy price to pay. And sample #2
> doesn't need a temporary variable -- but sample #1's temporary variable
> is a function-local variable, so speed and namespace-clutter costs are
> minimal.
> 
> What I really want to know is why you would consider sample #1 "broken
> and fragile code". I can't think of any circumstances where sample #1
> would fail to achieve its desired effect. Can you?

Just to play devil's advocate, I think sample #1 would behave very
strangely if its _first_ statement failed -- before propagating the
exception, it would (weirdly...) bind sys.sdout to some old value
for old_stdout (if any) -- and if there was no previous value for
old_stdout it would propagate the WRONG exception, namely the
UnboundLocalError met in the finally clause, rather than the
AttributeError (say) met in the first statement of the try clause.

In other words, I think your try clause encompasses too much -- a
common error, which only bites when some of the statements erroneously
placed into the try-clause happens to raise an exception.  Specifically
I believe that in this case you should optimally place the first THREE
statements, that are currently in your try-clause, OUTSIDE of it --
that's not critical, but, conceptually, until you're done rebinding
sys.stdout, there's no reason to ensure sys.stdout later gets bound
back to its previous (saved) value.

try/finally is quite error-prone that way, because somehow it SEEMS
we'd want to put into the try clause all the "initialization"
statements that, in fact, what we really want to have in it is what
comes AFTER "initialization".

Another difference between the two snippets, which I don't
understand, comes when function_blah_blah does its own rebinding
of sys.stdout.  In that case, the first snippet "ignores" the
rebinding (calls do_something_with with the value you had just
bound to sys.stdout BEFORE calling function_blahblah), the
second snippet "accepts" the rebinding (calls do_etc with the
value CURRENTLY bound to sys.stdout). Since I don't know what's
SUPPOSED to be happening under such circumstances, I can't tell
which of the two versions (if either) is wrong, but I do find
the difference puzzling.  Assuming the second hypothetical snippet
has the correct semantics, then, maybe the first one should be:

    old_stdout = sys.stdout
    sys.stdout = cStringIO.StringIO()
    try:
        function_whose_output_I_want_to_capture()
        do_something_with(sys.stdout)
    finally:
        sys.stdout = old_stdout

to achieve the same semantics -- specifically, what we want
to put in the try clause is just what would go in the with
clause's body if it existed, while the "saving the previous
state" (implicit in the hypothetical 'with') and the actual
initialization (the assignment that follows the with keyword
itself in the hypothetical case) need to be BEFORE the try
clause.  (Possibly there is some subtlety about what is
supposed to happen if cStringIO.StringIO rebinds sys.stdout
and then raises an exception?  In that case, then maybe
the second assignment I'm putting here before the try: needs
to go inside the try clause).  I think this kind of issues
shows try/finally isn't quite as simple to use as one might 
hope, so, perhaps, thinking of potential alternatives to
try/finally isn't necessarily a bad idea.


Alex





More information about the Python-list mailing list