Python exceptions for C programmers

May 13, 2010

If you are a C programmer, the simple way to think about Python exceptions is that they are almost always error returns; where a C function would be documented as 'returns whatever, or MY_ERROR on error', an idiomatic Python equivalent is 'returns whatever, or raises MyException on error'. Whether this works better (and when) is a matter of some debate, but it is the Pythonic approach and is used by most modules that you'll run into.

(Ideally the modules will document this. Note that it's relatively common to just describe the module's exceptions and generally what when they get raised, and then not annotate every function and method with what exceptions it raises.)

Thus, you deal with exceptions in more or less the same way that you'd deal with error returns, with the exception that you don't have the option of ignoring them; either you handle them or your program dies. The immediate corollary is that if a program ever dies with a Python stack backtrace, the fault is usually in some code that didn't handle an error return.

(Of course the program might have run into an impossible situation or an internal bug, but this is generally uncommon. One hopes.)

Doing something useful with exceptions that you catch is another issue, unfortunately; many error return exceptions contain very little immediately useful information about what exactly went wrong (some contain information that can be painstakingly decoded if you know what the module that generated them is doing). It's still worth explicitly catching them in programs, if only to tell the user 'something went wrong with <X>' in a somewhat friendly manner.

(This situation is not really much different from getting generic 'something went wrong' error returns from libraries in C. By the way, I strongly suggest avoiding broad try's because they will mask genuine coding bugs that you want to hear about.)

Exceptions don't have to be handled exactly like C error returns, where you wrap each separate call in your function with its own try:/except: block; I can think of at least two additional patterns. First, if all you are going to do with the error is to stop doing anything in the function and return an error indicator to your caller, you can just not catch the exception at all. Repeat for as many levels of function as necessary; I have programs where the only try:/except: blocks are in main(). You can use try:/finally: to handle a certain amount of per-function cleanup, or just rely on Python's garbage collection to do things like close files for you when an opened file object goes out of scope.

(This is the point where things like phase tracking come in handy so your top-level handler can tell the user what was going on when the error happened.)

Second, even if you need cleanup or error reporting in a function, you can merge a run of code with the same exception handling needs into one try:/except: block, instead of having N blocks that all do the same thing. This approach of merging error handling will be familiar to people who have read the Linux kernel source, and often results in a function with only a single try: that covers most of its code.

Sidebar: so what can you with an exception object?

The honest answer is 'not very much, unless the module documentation tells you otherwise'. Almost always the most sensible thing you can do with the exception object is turn it into a string and show it to the user.

(By 'exception object' I mean what you get in e when you do 'except MyException, e:' and then the exception happens.)

In theory modules could put additional information about the problem into the exception object, so that you could do things like tell different sorts of problems apart. In practice this information is almost always encoded into a module's exception type hierarchy, so that you usually have MyAuthException and MyConnectionException exceptions (both of them being subclasses of MyException) instead of an errorcause member of MyException.

Written on 13 May 2010.
« 'Borrowing' IPv4 netblocks to get around address space exhaustion
A sysadmin mistake: shooting your virtual foot off »

Page tools: View Source, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Thu May 13 01:32:10 2010
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.