Pythonic API design: detailed errors when you usually don't care

John J. Lee jjl at pobox.com
Tue Oct 3 14:32:15 EDT 2006


"Simon  Willison" <swillison at gmail.com> writes:

> Hi all,
> 
> I have an API design question. I'm writing a function that can either
> succeed or fail. Most of the time the code calling the function won't
> care about the reason for the failure, but very occasionally it will.
> 
> I can see a number of ways of doing this, but none of them feel
> aesthetically pleasing:
> 
> 1.
> 
> try:
>   do_something()
> except HttpError:
>   # An HTTP error occurred
> except ApplicationError:
>   # An application error occurred
> else:
>   # It worked!
> 
> This does the job fine, but has a couple of problems. The first is that
> I anticipate that most people using my function won't care about the
> reason; they'll just want a True or False answer. Their ideal API would
> look like this:
> 
> if do_something():
>   # It succeeded
> else:
>   # It failed
> 
> The second is that the common path is success, which is hidden away in
> the 'else' clause. This seems unintuitive.

It's hard to answer this without knowing any of the real details
you've hidden from us: I'm not sure I believe you that the code at the
call site necessarily needs to know whether the function call
succeeded at all.  Perhaps the preferred usage might sometimes be:

do_something()


Exceptions raised may be caught (or not, if the application does not
find that necessary), elsewhere than at the call site -- they can be
caught further up the stack.  You are of course aware of this fact,
but your discussion seems to imply that this crucial piece of
knowledge was not active in your mind when you wrote it.

I think Python users are right to "default to" raising exceptions.
There's a nice label for this practice:

http://c2.com/cgi/wiki?SamuraiPrinciple

"""
Samurai Principle

Return victorious, or not at all.
"""


:-)

(though I imagine that term is used with subtly different meanings by
different people...)


As others have suggested, often it is useful if most exceptions that
can be raised by a callable are instances of a single class.  This can
be achieved through inheritance (make them all derive from one class)
or composition (stuff the various original exceptions into a wrapper
exception).


John




More information about the Python-list mailing list