exception handling in complex Python programs

dbpokorny at gmail.com dbpokorny at gmail.com
Thu Aug 21 21:39:58 EDT 2008


On Aug 20, 10:13 pm, Steven D'Aprano <st... at REMOVE-THIS-
cybersource.com.au> wrote:
> It might not be enjoyable to have a sarcastic remark directed your way,
> but it isn't a personal attack. Just because a comment is about something
> you do doesn't make it a personal attack. Personal attacks are about who
> you are rather than what you do.

If you type in "Personal Attack" in Wikipedia (not an authoritative
source, I know) it takes you to the page on ad hominem arguments.
There you can find the following example of the fallacious ad hominem
argument:

    Person A makes claim X
    There is something objectionable about Person A
    Therefore claim X is false

It is, ultimately, a matter of opinion, but "going out of one's way to
make it hard for other programmers" sounds objectionable to me. I
mean, I wouldn't want to work with anyone like that!

> There's an apparent contradiction in your argument. You seem to be
> arguing against EAFP and in favour of LBYL, but now you're suggesting
> that you don't use type-checking. As near as I can tell, you don't do
> type-checking, you don't do duck typing, you don't like catching
> exceptions. So what do you actually do to deal with invalid data?

Here is an example from a Django web app: when there is a bug, a
generic Exception is thrown and Django catches it and reports a
beautifully formatted stack trace. When something must be reported to
the user, a MyAppException is thrown (not the real name). The HTTP
request handler for the application is wrapped in a single try:...
except MyAppException:.... the big idea is that there should be a
maximum of two try/except blocks on the stack at any particular point
in time [1]: at a high level (already mentioned) and for wrapping
primitive "execute" operations against Rpy and MySQLdb. In practice,
this doesn't always happen - there is one place where an EAFP-style
construct is used (the "operation" in this case is to generate some
HTML and cache it based on some source XML, but the source may have
"syntax errors", so if the HTML can't be generated, then cleanup is
performed and an error message returned).

So to summarize:
  try/except blocks at boundaries between system components: good
  try/except blocks within a single component: slightly concerning

I think I may have overstated the case against EAFP. There are
certainly cases where EAFP makes a lot of sense; I would object to
portraying EAFP as an alternative to defensive programming. [Side
note: defensive programming serves much the same purpose in Python as
it does in C, but I agree that in C there is extra motivation such as
avoiding buffer overruns. I think of defensive programming simply as
"taking proactive steps to reduce the expected time to debug a program
if a programming error should arise".]

> By the way, if you're worried that isinstance() is too long to type, you
> can do this:
>
> isin = isinstance
> isin(123, int)

Actually I'm holding out for type objects to grow __lt__ and __le__
methods so you can do something like

  from abstract_base_classes import sequence
  if type(my_obj) <= sequence:
    ...

This is borrowed from the <= notation for subgroups in math (there are
probably other cases too). I don't use abstract base classes, so I
don't even know if this is right, but hopefully you get the idea.

> No no no, exceptions are not necessarily bugs!!! A bug is an exceptional
> circumstance, but not all exceptional circumstances are bugs.

I tend to agree, but I have found that thinking about these issues
makes me question the wisdom of Python's built-ins throwing exceptions
in non-exceptional circumstances (for instance you try to open a file
that doesn't exist - IMHO this is about as exceptional as trying a no-
wait acquire on a busy lock, in other words it isn't exceptional at
all). As long as we are in fantasy realm, one could argue that open()
should return a pair like this:

  f, open_ok = open(...)

where open_ok is a status object whose __nonzero__ (in 3.0 __bool__ is
used) is true on success and false on an error, and also has an error
code and error message field. The idea is from Django's get_or_create
method in the db API.

[1] That I have to think about. I don't particularly care about try/
except blocks in Django's, Rpy's, or MySQLdb's activation records.

David



More information about the Python-list mailing list