Lesson 9 of 10

Your own exceptions

Goal

Catch only what you can handle

01

Explanation

Built-in exceptions like ValueError and KeyError are generic. They don't tell you whether the problem is in your config or somewhere else, which is why libraries define their own exceptions as a family: a base class that inherits from Exception, and a subclass for each case. Calling code can then catch exactly what it can handle: except ConfigError catches the whole family, and except MissingKeyError catches just one case.

When you turn one of Python's own errors, like KeyError, into one from your family, write raise MissingKeyError("port") from err. The original error is kept in __cause__, and both appear together in the traceback instead of the cause getting lost.

The problem with the current code is that except Exception swallows every error, even your own bugs. A missing key, an invalid value and a typo in the key name all return port 8000 without a word, and you won't find out the config is broken until the program has reached your users.

02

Your task

Define ConfigError inheriting from Exception, and MissingKeyError(key) inheriting from ConfigError and storing key. Then make read_port(config) return the port as a number, raising MissingKeyError when the key is missing and ConfigError when its value isn't a number, using from in both cases.

Checks

6 checks
  1. ConfigError inherits from Exception
  2. MissingKeyError belongs to the ConfigError family
  3. read_port returns the port as a number
  4. A missing key raises MissingKeyError carrying the key name
  5. The original cause is kept with from
  6. A non-numeric value raises ConfigError caused by ValueError
main.py
OutputPython (Pyodide)

Loading Python. This only happens once. Your browser keeps it after that.

–/6

Checks start once the environment is ready.

6 checks listed under “Your task”.