Wandering Thoughts archives

2010-04-20

Standard format Unix errors in Python programs

Here's some things on printing standard format Unix errors in Python programs specifically, based on things that I've both seen and done myself.

First, I tend to wind up putting standard warn() and die() functions into my programs, but they only handle very low level details (putting the program name in and flushing things as necessary). Beyond that:

  • you should always catch EnvironmentError instead of anything more specific. Functions are inconsistent about whether they raise IOError or OSError, so you shouldn't try to guess, just deal with the general case.
  • don't try to do anything more complex with the EnvironmentError exception object than turning it into a string (with str() or just by printing it as one). This will print the errno number as well as the text error message, but I don't consider this a bad thing.

  • for things that cause the program to stop, if you handle each potentially failing statement with its own try/except block you will rapidly go mad and repeat yourself a lot. Try to wrap as much as possible in a single generic block. The ultimate version of this is to have a single try/except in your main function and have the rest of your program ignore the issue.

    (This is not as applicable to warning messages, because you pretty much have to handle them fairly locally.)

    However, the more you aggregate exception handling, the more you need some way of keeping track of what the program was doing at the point where it blew up. I tend to solve this with what I call phase tracking.

In general, you have a design decision to make about how to handle fatal errors. One way to handle them is to call die() immediately where you hit the error; another way is to let the exception bubble up (either in its original form or re-wrapped into an internal exception class). The drawback of the former is what I covered in ImportableMain; the more the low-level bits of your program wind up calling sys.exit(), the less you can conveniently test them by themselves because a failure immediately shuts down everything. The drawback of the latter is that you are forced into some sort of phase tracking system.

Sidebar: my versions of warn() and die()

Generally my versions of these functions look like this:

import sys
def warn(msg):
  sys.stdout.flush()
  sys.stderr.write("%s: %s\n" % \
                   (sys.argv[0], msg))
  sys.stderr.flush()

def die(msg):
  warn(msg)
  sys.exit(1)

(See here for why I put in the calls to .flush().)

More complicated games are possible with the name of the program (so that it gets shortened by taking out the full path), but usually I don't bother.

python/PythonStandardErrors written at 01:16:23; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.