[Python-ideas] with-except-finally blocks

Steven D'Aprano steve at pearwood.info
Thu Apr 16 15:09:13 CEST 2015


On Wed, Apr 15, 2015 at 11:39:37PM -0700, Kale Kundert wrote:
> Hello everyone,
> 
> I'd like to propose a little bit of syntactic sugar: allowing with-blocks to be
> followed by except- and finally-blocks just like try-blocks.  For example:
> 
> with open('spam.txt') as file:
>     print(file.read())
> except IOError:
>     print('No spam here...')
> finally:
>     print('done.')

This has two obvious, and slightly different, interpretations:

with open('spam.txt') as file:
    try:
        print(file.read())
    except IOError:
        print('No spam here...')
   finally:
        print('done.')


try:
    with open('spam.txt') as file:
        print(file.read())
except IOError:
    print('No spam here...')
finally:
    print('done.')



I'm not even going to ask you which of the two you intended, because 
whichever one you pick, half the time people will want the other. And of 
those times, half the time they will end up using this syntactic sugar 
anyway, and then be disturbed when it doesn't quite work the way they 
expected.

To put it another way, I expect this to be an oh-so-subtle bug magnet.


> This proposed syntax is semantically equivalent to wrapping a with-block within
> a try-block like so:
> 
> try:
>     with open('spam.txt') as file:
>         print(file.read())
>     finally:
>         print('done.')
> except IOError:
>     print('No spam here...')

You can't describe "with...finally" in terms of "with...finally", it 
gives a RecursionError :-)

I assume this is an editing mistake, and you intended the finally clause 
to follow the except clause.



> I see two advantages to the proposed syntax.  First and most obviously, it saves
> an extra line and an extra indentation level.  One line may not be a big deal,
> but one indentation level can really affect readability. 

It certainly can affect readability, but not in the way you mean. The 
loss of that indentation level *hurts* readability, it doesn't help it, 
because it obscures the structure of the code.

Either the try block encloses the with, or the with encloses the try. 
They are not part of the same structure in the way that "if...elif... 
else" or "for...else" are.

[Aside: in the second case, the for-loop, "else" is not the best choice 
of keyword. The semantics are more like "then", but we are probably 
stuck with it now.]

Given either version:

with spam:
    try:
        X
    except: ...

try:
    with spam:
        X
except: ...


the fact that X is indented two levels is a good thing, not a bad thing. 
It makes the structure of the code immediately visible, and there is 
absolutely no question of whether the with is enclosing the try or the 
try is enclosing the with.

Existing Python statements which introduce blocks strictly keep to the 
rule that things which are semantically at different levels are indented 
differently. (The exception is the single line form of some statements, 
e.g. "if condition: pass" on a single line.) We never write things like:


if condition: for x in sequence:
    do_this()

try: while flag:
    do_this()
except Error:
    handle_error()

def func(arg): with func(arg) as spam:
    return spam.thing()


even in the cases where they wouldn't be syntactically ambiguous. Such 
things are strictly disallowed, and that is good.


> Second and more
> conceptually, it makes sense to think about exception handling in the context of
> a with-block.

No more than any other block of code. There is nothing special about 
with blocks that go with exception handling, any more than (say) 
indexing into a list, processing a dict, or importing a module.

> More often than not, if you're using a with-block, you're
> expecting that something in that block could throw an exception.

"More often than not"? Not according to your own investigation, where 
you found that only two percent of the time with blocks are associated 
with try blocks. That means that in the overwhelming majority of cases, 
nearly 98% of the time, we are NOT catching exceptions in the 
with-block.

That means that *at best* this syntactic sugar will be of little value, 
and at worst it will be actively harmful. I think it will be actively 
harmful, and give it a strong -1 vote.



-- 
Steve


More information about the Python-ideas mailing list