Occam's razor:
More things should not be used than are necessary.
Python gives you the ability to subclass exceptions. That extensibility is great, when you need it. But it's rare you actually need to do that. Often you can get just as much error reporting clarity through customizing the message of a standard exception type. E.g.:
def get_parsed_mail(mail):
if not mail.attachment:
raise ValueError('No attachment')
if not check_some_necessary_fields_format(mail):
raise ValueError('Invalid attachment field format')
Standard exceptions belong to a well-structured, battle-proven hierarchy that many developers have studied and more-or-less understand.
Custom exceptions do help you signal the module that generated the error ("this error came from MailParser
!") and as well as its kind, but often that information is overkill. Consider:
try:
mail_parts = ParseMail.parse(raw_message)
except Exception as e:
...
Viewed in the context it will be used, the source isn't a mystery. You know from context that the error came from ParseMail
. You know that whether it's a customized MailNoAttachmentError
or merely a ValueError
.
Other than source, different exception types can help you figure the kind of error. What happened, exactly? The real question is, does distinguishing those types help respond to error conditions? Do you expect there to be error handlers for each of these different types of error?
try:
mail_parts = ParseMail.parse(raw_message)
except MailNoAttachmentError as e:
...
except MailInvalidFieldError as e:
...
Is there anything that a program can do to correct those individual cases? Or are you just reporting out to the developer / user? If just reporting to a human, customizing the exception's message is at least as important as the type of the exception. Probably more so.
So custom exceptions are only occasionally necessary. Piggybacking standard exceptions, but using your own customized messages, generally works just as well and is simpler.
But if you feel there is virtue in more precise error source/type signaling, that's fine too. A single kind of custom error, usually subclassed off a common error like ValueError
, suffices for most modules, e.g.:
class ParseMailError(ValueError):
pass
If you wanted to go full Java-esque exception diversity / precision, you can create your own ParseMail
exception hierarchy blended with the standard hierarchy through multiple inheritance:
class ParseMailError(Exception):
pass
class MailNoAttachmentError(ValueError, ParseMailError):
pass
class MailInvalidFieldError(TypeError, ParseMailError):
pass