Exception class
Exception classes are used to indicate that something has gone wrong with
the program at runtime. Functions use the raise
keyword, if an error is
anticipated, and specify the exception class they intend to throw. This
module defines a handful of custom exception classes and shows how they
can be used in the context of a function.
class CustomError(Exception):
"""Custom class of errors.
This is a custom exception for any issues that arise in this module.
One of the reasons why developers design a class like this is for
consumption by downstream services and command-line tools.
If we designed a standalone application with no downstream consumers, then
it makes little sense to define a custom hierarchy of exceptions. In that
case, we should use the existing hierarchy of builtin exception
classes which are listed in the Python docs:
https://docs.python.org/3/library/exceptions.html
"""
class DivisionError(CustomError):
"""Any division error that results from invalid input.
This exception can be subclassed with the following exceptions if they
happen enough across the codebase:
- ZeroDivisorError
- NegativeDividendError
- NegativeDivisorError
That being said, there's a point of diminishing returns when we design
too many exceptions. It is better to design few exceptions that many
developers handle than design many exceptions that few developers handle.
"""
def divide_positive_numbers(dividend, divisor):
"""Divide a positive number by another positive number.
Writing a program in this style is considered defensive programming.
For more on this programming style, check the Wikipedia link below:
https://en.wikipedia.org/wiki/Defensive_programming
"""
if dividend <= 0:
raise DivisionError(f"Non-positive dividend: {dividend}")
elif divisor <= 0:
raise DivisionError(f"Non-positive divisor: {divisor}")
return dividend // divisor
def main():
# Exception classes are no different from concrete classes in that
# they all have inheritance baked in
assert issubclass(DivisionError, CustomError)
# Try a couple of inputs that are known to throw an error based on
# the exceptions thrown in `divide_positive_numbers`
for dividend, divisor in [(0, 1), (1, 0), (-1, 1), (1, -1)]:
division_failed = False
try:
divide_positive_numbers(dividend, divisor)
except DivisionError as e:
division_failed = True
assert str(e).startswith("Non-positive")
assert division_failed is True
# Now let's do it correctly to skip all the exceptions
result = divide_positive_numbers(1, 1)
assert result == 1
if __name__ == "__main__":
main()