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

Steve Holden steve at holdenweb.com
Mon Oct 2 13:27:09 EDT 2006


Simon Willison wrote:
> 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.
> 
> 2.
> 
> Put the method on an object, which stores the reason for a failure:
> 
> if obj.do_something():
>   # It succeeded
> else:
>   # It failed; obj.get_error_reason() can be called if you want to know
> why
> 
> This has an API that is closer to my ideal True/False, but requires me
> to maintain error state inside an object. I'd rather not keep extra
> state around if I don't absolutely have to.
> 
> 3.
> 
> error = do_something()
> if error:
>   # It failed
> else:
>   # It succeeded
> 
> This is nice and simple but suffers from cognitive dissonance in that
> the function returns True (or an object evaluating to True) for
> failure.
> 
> 4.
> 
> The preferred approach works like this:
> 
> if do_something():
>   # Succeeded
> else:
>   # Failed
> 
> BUT this works too...
> 
> ok = do_something()
> if ok:
>   # Succeeded
> else:
>   # ok.reason has extra information
>   reason = ok.reason
> 
> This can be implemented by returning an object from do_something() that
> has a __nonzero__ method that makes it evaluate to False. This solves
> my problem almost perfectly, but has the disadvantage that it operates
> counter to developer expectations (normally an object that evaluates to
> False is 'empty').
> 
> I know I should probably just pick one of the above and run with it,
> but I thought I'd ask here to see if I've missed a more elegant
> solution.
> 
> Thanks,
> 
> Simon
> 
I should have thought the simplest spelling of your requirements would be

try:
     do_something()
     print "It worked"
except:
     # it didn't

However there are good reasons why this pattern isn't often recommended: 
it masks types of exception that you may not have anticipated.

regards
  Steve
-- 
Steve Holden       +44 150 684 7255  +1 800 494 3119
Holden Web LLC/Ltd          http://www.holdenweb.com
Skype: holdenweb       http://holdenweb.blogspot.com
Recent Ramblings     http://del.icio.us/steve.holden




More information about the Python-list mailing list