EAFP vs LBYL (was Re: A little disappointed so far)

David Abrahams dave at boost-consulting.com
Mon May 19 21:25:37 EDT 2003


VanL <vlindberg at verio.net> writes:

> Alex Martelli wrote:
>
>>     try:
>>         useappropriately(thefile)
>>     except ItWasWrong, howwasitwrong:
>>         dealwithwrongness()
>
> My problem with this approach:
>
> (cue dramatic music)
> Our chief error is an IOError...IOError and OSError... Our two errors
> are IOErrors and OSErrors...and RuntimeErrors.... Our *three* errors
> are IOErrors, OSErrors, and RuntimeErrors...and an occasional
> MemoryError.... Our *four*...no... *Amongst* our errors....
>
> I have a hard time catching all the appropriate exceptions that a
> function may throw, especially if useappropriately(thefile) is
> non-trivial.  Pure EAFP code sometimes ends up looking like this
>
> try:
>      useappropriately(thefile)
> except IOError:
>      dealwithIOError()
> except OSError:
>       dealwithOSError()
> except RandomError:
>       dealwithRandomError....

IME it's very unusual to have many different recovery strategies for
different kinds of exceptions.  Unless you're reporting errors to the
user (usually in an outer context), most of what you do with
exception-handling is to restore your whole-program invariants and
propagate the exception to an outer layer.  It's nearly impossible to
write robust code when you try to keep track of different ways in
which your program state may have changed based on what error was
reported to you from inner layers. Therefore, you generally have to
set things up so you can use one recovery strategy regardless of what
happened.  To do that, you need to have well-understood contracts
between caller and callee about what changes they can make.

It's useful to break down those contracts along these lines, from
strongest to weakest:

no-raise: the guarantee that the callee will succeed "no matter what".
Very important for code that you use during error-recovery.  How close
you can come to making that guarantee strictly in Python (or in any
language) is open to question.  Almost any operation in Python could
eat up the last bit of dynamic memory, so you may have to turn a
blind eye to corner cases like that in order to make pragmatic use of
the no-raise guarantee.

strong: the guarantee that if the callee raises an exception, the
program state will be unchanged from what it was before the call.
Call this "commit or rollback".

basic: the guarantee that even if the callee modifies some objects
before raising the exception, their invariants will be preserved.

Nothing should give a guarantee weaker than the basic guarantee.


> and you can *still* get bitten if you haven't anticipated all the ways
> in which things can go wrong.   (And I know about bare excepts... but
> having bare excepts in your code opens up its own kettle of worms.) 

Which kettle?  What do those worms look like?  Code like


      except:
        ...
        raise

is just fine in my book.  At the outer layer, all you lose is precise
and user-friendly error reporting, which after all could be worse.
It's not like your program state gets trashed.

> If I really need something to work, I usually do both a pre-check
> (LBYL) as well as a just-in-case try-except, so that the number of
> places and ways in which the useappropriately() function can go
> wrong is minimized.
>
> Am I seriously misguided here?

Yeah, I think so.  LBYL + try-except just multiplies the amount of
code you have to devote to error-handling.  The beautiful thing about
exception-handling is that it neatly locates error handling at the
level where it belongs and takes it out of the main flow of control.
Since you'll have to cope with exceptions anyway, there's no point in
LBYL except in very special cases.

You can read more about the theory of EH here:

http://www.boost.org/more/generic_exception_safety.html (originally
published in M. Jazayeri, R. Loos, D. Musser (eds.): Generic
Programming, Proc. of a Dagstuhl Seminar, Lecture Notes on Computer
Science. Volume. 1766)

The writing is based around C++, but the main principles apply equally
to Python.

Cheers,
-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com




More information about the Python-list mailing list