canonical file access pattern?

  • Thread starter Hans-Joachim Widmaier
  • Start date
H

Hans-Joachim Widmaier

Recently, there was mentioned how someone who had understood Python's
error handling would write the "open and read file with error handling"
idiom. If I remember correctly, it went like this:

try:
f = file(filename, op)
except IOError, e:
# Handle exception
else:
# Use the file
f.close()

I don't think that's sufficient. First of all, opening a file is only
part of the job--reading and writing can go wrong too, and these are
not handled by the above code. Then I've seen OSError as well as IOError
raised occassionaly.

Seems like I have do this, then:

try:
f = file(filename, op)
except (OSError, IOError), e:
# Handle open() error
else:
try:
# read/write to the file
f.close() # flushes write buffers, so can fail, too
except (OSError, IOError), e:
# Handle read/write errors

Is it really so wrong, then, to fold the read/write operations and close()
into the first try suite and handle all those errors in one go?
I'd just like to have a definitive pattern that I can stick to that is
considered bullet-proof.

Hans-Joachim Widmaier
 
R

Rene Pijlman

Hans-Joachim Widmaier:
Then I've seen OSError as well as IOError raised occassionaly.

The documentation says only IOError can be raised. Is there a bug?

"When a file operation fails for an I/O-related reason, the exception
IOError is raised."
http://www.python.org/doc/current/lib/bltin-file-objects.html
Seems like I have do this, then:

try:
f = file(filename, op)
except (OSError, IOError), e:
# Handle open() error
else:
try:
# read/write to the file
f.close() # flushes write buffers, so can fail, too
except (OSError, IOError), e:
# Handle read/write errors

This is not correct, since the file is not closed when an exception is
thrown by read or write.

If we assume the following:
1. A file that is opened should be closed in all cases
2. Every exception raised by read, write or close should be caught and
handled

Then this would need to be the algorithm:

try:
f = file("spam.txt", "w")
except IOError, e:
# Handle open() error
pass
else:
try:
try:
# read/write to the file
pass
except IOError, e:
# Handle read/write errors
pass
finally:
try:
f.close()
except IOError, e:
# Handle close error
pass

Not very pretty, but I can't think of a simplification that does not
violate one of the assumptions.
 
H

Hans-Joachim Widmaier

Am Tue, 30 Dec 2003 12:00:52 +0100 schrieb Rene Pijlman:
The documentation says only IOError can be raised. Is there a bug?

"When a file operation fails for an I/O-related reason, the exception
IOError is raised."

Apart from the "usual suspects" like file does not exist or is write
protected, errors aren't that common, so you don't see them all the time.
Maybe my memory is failing, but I have this distinct recollection that I
once used a 'except IOError' and got a OSError, much to my surprise. But
then, this has been quite a while ago. Python was much younger
then, I was still a fledgling Pythonist, and maybe I attributed the
error to the wrong operation ...
This is not correct, since the file is not closed when an exception is
thrown by read or write.

Oh, sorry. I silently assumed this to be done in the exception handling
suite. Really should have made that explicit.
If we assume the following:
1. A file that is opened should be closed in all cases 2. Every
exception raised by read, write or close should be caught and handled

Then this would need to be the algorithm:

try:
f = file("spam.txt", "w")
except IOError, e:
# Handle open() error
pass
else:
try:
try:
# read/write to the file
pass
except IOError, e:
# Handle read/write errors
pass
finally:
try:
f.close()
except IOError, e:
# Handle close error
pass

While my first thought was: Why is the finally needed here? The close()
might just as well be the end of the else-suit. But it slowly dawns on me:
If there is another exception than IOError in the read/write suite,
closing of the file wouldn't take place.
Not very pretty, but I can't think of a simplification that does not
violate one of the assumptions.

Yes, I would second that. Alas, this means that the wonderful pattern

for line in file(filename, "r"):
process_line(line)

is unusable for any "production quality" program. ;-(
It certainly is ok for throw-away one-liners and (what I do often!)
interactive file converters.

Thanks for elaborating.

Hans-Joachim Widmaier
 
J

John J. Lee

Hans-Joachim Widmaier said:
Am Tue, 30 Dec 2003 12:00:52 +0100 schrieb Rene Pijlman: [...]
Then this would need to be the algorithm:

try:
f = file("spam.txt", "w")
except IOError, e:
# Handle open() error
pass
else:
try:
try:
# read/write to the file
pass
except IOError, e:
# Handle read/write errors
pass
finally:
try:
f.close()
except IOError, e:
# Handle close error
pass

While my first thought was: Why is the finally needed here? The close()
might just as well be the end of the else-suit. But it slowly dawns on me:
If there is another exception than IOError in the read/write suite,
closing of the file wouldn't take place.
Not very pretty, but I can't think of a simplification that does not
violate one of the assumptions.

Yes, I would second that. Alas, this means that the wonderful pattern

for line in file(filename, "r"):
process_line(line)

is unusable for any "production quality" program. ;-(
[...]

If that were true, why did exceptions get invented? If you don't need
to do something different in all those except: clauses, then don't put
them in. As you know, the nice thing about exceptions is that you're
allowed to handle them in sensible places, so typical usage is like
this:

def frob(filename):
f = file(filename, "w")
try:
# read/write to the file
finally:
f.close()

def blah():
try:
frob(filename)
except IOError, e:
# try something else, or
print e.strerror


or, even better:

def blah():
frob(filename)


Shock, horror, where's the except IOError:, you ask?! It's in the
calling function, of course.

Also, if you want to catch both OSError and IOError, note that
EnvironmentError is their common base class.


John
 
H

Hans-Joachim Widmaier

Am Wed, 31 Dec 2003 14:51:01 +0000 schrieb John J. Lee:

If that were true, why did exceptions get invented? If you don't need
to do something different in all those except: clauses, then don't put
them in.

One thing I need is to create an informative and useful error message
As you know, the nice thing about exceptions is that you're
allowed to handle them in sensible places, so typical usage is like
this:

[handling exceptions in the caller]
Shock, horror, where's the except IOError:, you ask?! It's in the
calling function, of course.

Hmm. The farther away from the actual point of error you catch it, the
harder it is to tell exactly what happened and to redo/work around it.
Also, if you want to catch both OSError and IOError, note that
EnvironmentError is their common base class.

Good tip! I didn't know that before.

Hans-Joachim Widmaier
 
J

John J. Lee

Hans-Joachim Widmaier said:
Am Wed, 31 Dec 2003 14:51:01 +0000 schrieb John J. Lee: [...]
If that were true, why did exceptions get invented? If you don't need
to do something different in all those except: clauses, then don't put
them in.

One thing I need is to create an informative and useful error message
..strerror?

[...]
Hmm. The farther away from the actual point of error you catch it, the
harder it is to tell exactly what happened and to redo/work around it.
[...]

So in some cases, you have fewer except statments, in others, more.
There's really no "canonical pattern" to it.


John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,174
Messages
2,570,940
Members
47,486
Latest member
websterztechnologies01

Latest Threads

Top