Alternative perspective - focus on the exceptions and what they represent to the thing producing the exception, rather than try..except
or what the consuming code might be doing.
Eric Lippert in his Blog (currently broken link but I assume only temporarily) describes 4 broad categories of Exceptions, summarised below.
Fatal
- Definition: Fatal exceptions are not yor fault, and you cannot sensibly clean up from them.
- Examples: Out of memory, thread aborted.
- Resolution: Don’t catch; let them crash the program.
- Design: Don’t ever throw fatal exceptions directly.
Boneheaded
- Definition: Boneheaded exceptions are violations of the API, and are bugs in your code.
- Examples: Argument is null, index out of range.
- Resolution: Don’t catch; fix them in the code.
- Design: Use code contracts for boneheaded exceptions; do not document the specific exception type.
Vexing
- Definition: Vexing exceptions are due to bad design decisions, thrown in non-exceptional situations.
- Examples: Parsing errors.
- Resolution: Avoid calling vexing functions; if not possible, catch the vexing exception.
- Design: Don’t ever throw vexing exceptions.
Exogenous
- Definition: Exogenous exceptions are from unpredictable, external influences.
- Examples: File not found, resource already in use.
- Resolution: Always catch and handle.
- Design: Throw exogenous exceptions as necessary; document the specific exception type.
Obviously this just focuses on the exceptions themselves there are other important considerations around code which uses exceptions, including:
- If any type of exception cannot be gracefully handled and recovered, ensure the full exception information is logged, including as much information around it as possible.
- If a framework you're using doesn't record unhandled exceptions, include a top-level 'catch-all' near
main
which can generate a crashdump with that information. - Use unit tests to assert when exceptions are expected to be thrown and when they are expected to be handled.