Python Exceptions
Guidelines for Python Exceptions#
Code with no exceptions#
Code that doesn’t use exceptions is always checking if it’s OK to do something
It’s easier to ask forgiveness than it is to get permission. - Grace Hopper
def print_object(some_object):
# Check if the object is printable...
if isinstance(some_object, str):
print(some_object)
elif isinstance(some_object, dict):
print(some_object)
elif isinstance(some_object, list):
print(some_object)
# 97 elifs later...
else:
print("unprintable object")
can be simplified to:
def print_object(some_object):
# Check if the object is printable...
try:
printable = str(some_object)
print(printable)
except TypeError:
print("unprintable object")
Choosing an exception#
Choose the one that matches most closely from the exception hierachy
Intrinsic Checks#
Never duplicate error checks that python does intrinsically for you
The try, except else#
try:
this()
except TypeError:
print('problem')
else:
no_exception()
finally:
clean_up()
else
is run when there is no exception in the try
Narrow First#
Matching is beginning to end, a more general clause will stop other exceptions so best to keep the narrowest expection first.
Base raise#
When raise
is used in excpetion handling code it bubbled the exception up. For example when we want to keep a record of the crash but have no inetention of handling it.
Don’t raise generic Exception#
More specific catches will not catch it and a more specific raise beforehand will be caught in the catch of Exception
Idiomatic Python#
Idiomatic Python is written in the EAFP (Easier to Ask for Forgiveness than Permission
) style (where reasonable). We can do so because exceptions are cheap in Python.
Best Practices#
Never use a bare except:
clause or you’ll end up suppressing real errors you didn’t intend to catch
Take advantage of Python built-ins and standard library modules that already throw exceptions
Merely logging and moving on#
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise
By using the base raise
it simply bubbles up the exception as opposed to returning a new stack trace if you specify the error
Unhandled#
If the exception is left unhandled, the default behavior is for the interpreter to print a full traceback and the error message included in the exception.
It is better to print a more user-friendly version of the error
except Exception as err: sys.stderr.write(‘ERROR: {}’.format(str(err))) return 1
Catching multiple exceptions#
You can catch multiple exception types with:
except (RuntimeError, TypeError, NameError):
pass
If your code only deliberately raises exceptions that you define within your module’s hierarchy, then all other types of exceptions raised by your module must be the ones that you didn’t intend to raise. These are bugs in your API’s code.
Having a root exception for your package#
The string printed as the exception type is the name of the built-in exception that occurred. This is true for all built-in exceptions, but need not be true for user-defined exceptions (although it is a useful convention)
From the Python docs on Exceptions