[Python-ideas] Extending error handling on with statements.

Steven D'Aprano steve at pearwood.info
Mon Mar 28 00:40:17 CEST 2011

Jakob Bowyer wrote:
> I personally love using with statements when handling file like objects.
> This is all well and good until an exception is thrown from the with
> statement. This is ok if you expect the exception because you can use try

You should always expect an exception when doing file I/O.

> and except but personally I feel that another condition to with would feel
> more 'pythonic' this means that you could fail the with statement with an
> exception jump to the clause, then jump back to the with statement trying
> the code in the clause e.g. rather than
> try:
>     with open('nofile.txt','r') as inp:
>         #nofile.txt does not exist and throws an exception
> except IOError:
>     with open('another.txt','r') as inp:
>         #carry on where you left off...
> You could simply have
> with open('nofile.txt','r') as inp:
>     #exception here
> else:
>     #give a new file to the with statement here and/or run some panic code
> where your program does something to fix the situation.

You say "jump back to the with statement", and "give a new file to the 
with statement". It sounds like you are thinking of turning with into a 
looping construct, e.g. this BASIC-like pseudo-code:

10 somefile = 'nofile.txt')
20 with open(somefile, 'r') as inp:
70 else:
     # Try again with a new file.
80    somefile = 'a different file.txt'
90    goto 20

The obvious problem with this is obvious: if the *second* file also 
fails to open, you will loop forever as the handler jumps to the `else` 
clause, sets the same name, and returns to try the with statement again. 
This will be an annoying source of errors. I don't know if I like this 
idea: I can see that it can be useful to repeat a block if an error 
occurs, but I think that it needs to be more obvious that you are looping.

You also seem to be assuming that the only error that will be caught 
will be "file not found" type errors. The beauty of a try-except block 
is that you can have different handlers depending on the error:

somefile = 42  # Oops!
     with open(somefile, 'r') as inp:
except TypeError:

Your suggested `else` clause loses all information about what sort of 
error occurs, as well as where:

outfile = 'output.txt'
with open(outfile, 'w') as out:
     out.write(42)  # Oops!
     # Try another file.
     outfile = 'another file.txt'

Lastly, your suggested syntax would be confusing. In try blocks, the 
`else` clause runs when there is no error. In with blocks, it would run 
when there is an error. That's not helpful: things that look similar 
should be similar.

Of course, you can fix this problem by changing the with statement to 
use `except` clauses:

with open(fname) as f:
except TypeError:
except IOError as e:
else:  # no error

but this adds much complexity to the with statement, and except for the 
magic goto, you can already do that at the cost of one line and one 
indent level:

     with open(fname) as f:
except TypeError:
except IOError as e:
else:  # no error

Saving one indent level and a line doesn't seem important enough for new 
syntax, especially new syntax which essentially duplicates functionality 
that already exists.


More information about the Python-ideas mailing list