Raising instances of Exception
and BaseException
will have a negative impact on any code trying
to catch these exceptions.
First, the only way to handle differently multiple Exceptions is to check their message, which is error-prone and difficult to maintain.
What’s more, it becomes difficult to catch only your exception. The best practice is to catch only exceptions which require a specific handling.
When you raise Exception
or BaseException
in a function the caller will have to add an except Exception
or
except BaseException
and re-raise all exceptions which were unintentionally caught. This can create tricky bugs when the caller forgets
to re-raise exceptions such as SystemExit
and the software cannot be stopped.
It is recommended to either:
- raise a more specific Built-in exception when one matches. For example
TypeError
should be raised when the type of a parameter is not the one expected.
- create a custom exception class deriving from
Exception
or one of its subclasses. A common practice for libraries is to have one
custom root exception class from which every other custom exception class inherits. It enables other projects using this library to catch all errors
coming from the library with a single "except" statement
This rule raises an issue when Exception
or BaseException
are raised.
Noncompliant Code Example
def process1():
raise BaseException("Wrong user input for field X") # Noncompliant
def process2():
raise BaseException("Wrong configuration") # Noncompliant
def process3(param):
if not isinstance(param, int):
raise Exception("param should be an integer") # Noncompliant
def caller():
try:
process1()
process2()
process3()
except BaseException as e:
if e.args[0] == "Wrong user input for field X":
# process error
pass
elif e.args[0] == "Wrong configuration":
# process error
pass
else:
# re-raise other exceptions
raise
Compliant Solution
class MyProjectError(Exception):
"""Exception class from which every exception in this library will derive.
It enables other projects using this library to catch all errors coming
from the library with a single "except" statement
"""
pass
class BadUserInputError(MyProjectError):
"""A specific error"""
pass
class ConfigurationError(MyProjectError):
"""A specific error"""
pass
def process1():
raise BadUserInputError("Wrong user input for field X")
def process2():
raise ConfigurationError("Wrong configuration")
def process3(param):
if not isinstance(param, int):
raise TypeError("param should be an integer")
def caller():
try:
process1()
process2()
process3()
except BadUserInputError as e:
# process error
pass
except ConfigurationError as e:
# process error
pass
See