views:

249

answers:

4

Hi,

I have a question regarding error checking in Python. Let's say I have a function that takes a file path as an input:

def myFunction(filepath):
    infile = open(filepath)
    #etc etc...

One possible precondition would be that the file should exist.

There are a few possible ways to check for this precondition, and I'm just wondering what's the best way to do it.

i) Check with an if-statement:

if not os.path.exists(filepath):
    raise IOException('File does not exist: %s' % filepath)

This is the way that I would usually do it, though the same IOException would be raised by Python if the file does not exist, even if I don't raise it.

ii) Use assert to check for the precondition:

assert os.path.exists(filepath), 'File does not exist: %s' % filepath

Using asserts seems to be the "standard" way of checking for pre/postconditions, so I am tempted to use these. However, it is possible that these asserts are turned off when the -o flag is used during execution, which means that this check might potentially be turned off and that seems risky.

iii) Don't handle the precondition at all

This is because if filepath does not exist, there will be an exception generated anyway and the exception message is detailed enough for user to know that the file does not exist


I'm just wondering which of the above is the standard practice that I should use for my codes.

+1  A: 

I think you should go with a mix of iii) and i). If you know for a fact, that python will throw the exception (i.e. case iii), then let python do it. If there are some other preconditions (e.g. demanded by your business logic) you should throw own exceptions, maybe even derive from Exception.

Using asserts is too fragile imho, because they might be turned off.

jhwist
+8  A: 

If all you want to do is raise an exception, use iii):

def myFunction(filepath):
    with open(filepath) as infile:
        pass

If you want to handle it in a special way, then use a try...except block:

def myFunction(filepath):
    try:
        with open(filepath) as infile:
            pass
    except IOError:
        # special handling code here

Under no circumstance is it preferable to check the existence of the file first (option i) or ii)) because in the time between the check or assertion and the time python tries to open the file, it is possible that the file could be deleted, or altered (such as with a symlink), which can lead to bugs or security hole.

Also, as of Python 2.6, the best practice when open files is to use the with open(...) as ... construction. This guarantees that the file will be closed, even if an exception occurs inside the with-block.

In Python 2.5 you can use the with construction if you preface your script with

from __future__ import with_statement
unutbu
+1 on using 'with'
whaley
+2  A: 

Definitely don't use an assert. Asserts should only fail if the code is wrong. External conditions (such as missing files) shouldn't be checked with asserts.

As others have pointed out, asserts can be turned off.

The formal semantics of assert are:

  1. The condition may or may not be evaluated (so don't rely on side effects of the expression).

  2. If the condition is true, execution continues.

  3. It is undefined what happens if the condition is false.

More on this idea.

Ned Batchelder
+1  A: 

The following extends from ~unutbu's example. If the file doesn't exist, or on any other type of IO error, the filename is also passed along in the error message:

path = 'blam'
try:
    with open(path) as f:
        print f.read()
except IOError as exc:
    raise IOError("%s: %s" % (path, exc.strerror))

=> IOError: blam: No such file or directory

shavenwarthog