Best practise hierarchy for user-defined exceptions

S

Slaunger

Hi there,

I am a newcomer to Pyhton coming from Java working on a relatively
large Pyhton project with several packages and modules. To improve
exception handling I would like to introduce some user-defined
exceptions to distinguish between exceptions raised in self-written
code as compared to std libray modules used there-in.

Say, for instance I would like to define a MyParseError exception to
indicate that something has gone wrong while parsing a byte string, a
file, or while packing into or unpacking from a struct.Struct
instance.

Here is my stub-implemented idea on how to do it so far, which is
inspired by how I would have done it in Java (but which may not be
very Pythonic??):

class MyException(Exception):

pass

class MyStandardError(MyException, StandardError):

pass

class MyParseError(MyStandardError, ValueError):

pass

Some comments and questions

1. The hierarchy is deliberately deep and maps to the std library such
that it is easier to extend
2. The implementations are empty but can be extended with hook for
logging, statistics, etc.
3. I use multiple inheritance in the two sub-classes. I have not tried
that before. Is this A Good Thing or A Bad Thing to do?
4. Which __xx__ methods would you normally implement for the user-
defined exception classes? I was thinking of __str__, for example? Is
there a recommended __str__ idiom to use for that?

-- Slaunger
 
C

Chris Rebert

Hi there,

I am a newcomer to Pyhton coming from Java working on a relatively
large Pyhton project with several packages and modules. To improve
exception handling I would like to introduce some user-defined
exceptions to distinguish between exceptions raised in self-written
code as compared to std libray modules used there-in.

Say, for instance I would like to define a MyParseError exception to
indicate that something has gone wrong while parsing a byte string, a
file, or while packing into or unpacking from a struct.Struct
instance.

Here is my stub-implemented idea on how to do it so far, which is
inspired by how I would have done it in Java (but which may not be
very Pythonic??):

class MyException(Exception):

pass

The above class probably isn't necessary. You don't need to
overgeneralize this much.
class MyStandardError(MyException, StandardError):

pass

Rename the previous class to just MyError in light of removing the other class.
class MyParseError(MyStandardError, ValueError):

pass

This technique is very Pythonic. Subclass from both the exception
class for your module and from a built-in one if there's an applicable
one.
Some comments and questions

1. The hierarchy is deliberately deep and maps to the std library such
that it is easier to extend

Zen of Python: Flat is better than nested.
This is part of the reason I recommend removing the one class.
2. The implementations are empty but can be extended with hook for
logging, statistics, etc.

True of stubs in general...
3. I use multiple inheritance in the two sub-classes. I have not tried
that before. Is this A Good Thing or A Bad Thing to do?

Good in this case, just be careful to use super() if you override any methods.
4. Which __xx__ methods would you normally implement for the user-
defined exception classes? I was thinking of __str__, for example? Is
there a recommended __str__ idiom to use for that?

You might override __init__ if you want to store additional
information about the cause of the exception (e.g. location of parse
error, name of the rule the error occurred in, etc).
The __str__ inherited from Exception is usually sufficient, but you
can override it if you want to. it's a judgement call IMHO.

Cheers,
Chris
 
S

Slaunger

The above class probably isn't necessary. You don't need to
overgeneralize this much.
Seems reasonable.


Rename the previous class to just MyError in light of removing the other class.

OK.


This technique is very Pythonic. Subclass from both the exception
class for your module and from a built-in one if there's an applicable
one.
OK. One thing I *like* about it is that I inherit properties of
ValueError.

One thing I feel *a little uneasy about* is that if I do something
like this

try:
do_something_which_raises_MyParseError_which_I_have_overlooked()
do_something_where_I_know_a_ValueError_can_be_raised()
catch ValueError:
handle_the_anticipated_ValueError_from_std_lib()
finally:
....

I will not notice that it was an unanticpated condition in my own
code, which caused the ValueError
to be raised. If I had just inherited from MyError, it would fall
through (which I would prefer)

Once I had seen a trace back to the unanticipated MyParseError I would
of course correct the code to

try:
do_something_where_I_now_know_MyParseError_can_be_raised()
do_something_where_I_know_a_ValueError_can_be_raised()
catch MyParseError:
handle_the_anticipated_MyParseError_generated_in_own_code()
catch ValueError:
handle_the_anticipated_ValueError_from_std_lib()
finally:
....

Is the above multiple catch methology pythonic as well?

On the other hand, with multiple inheritance this works:

try:
do_something_where_I_know_a_MyParseError_can_be_raised()
do_something_where_I_know_a_ValueError_can_be_raised()
catch ValueError:
handle_a_MyParseError_or_a_ValueError_in_the_same_manner()
finally:
....

which is nice is you are very aware that MyParseError descends from
ValueError (which may not be self-evident)

Zen of Python: Flat is better than nested.
This is part of the reason I recommend removing the one class.

Ah, OK, have to adjust my mindset a little. Willing to accomodate
though... ;-)
Good in this case, just be careful to use super() if you override any methods.

I will!
You might override __init__ if you want to store additional
information about the cause of the exception (e.g. location of parse
error, name of the rule the error occurred in, etc).
The __str__ inherited from Exception is usually sufficient, but you
can override it if you want to. it's a judgement call IMHO.

OK. Seems pretty straight-forward. I like Python more and more.
Cheers,
Chris

That was a very useful answer. Thank you, Chris for taking your time
to reply on my questions.

Cheers,
-- Slaunger
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top