Chaining: __cause__ and __context__
The new exception remembers the old one. Look at which attribute is filled in each case.
raise lets your own code signal a problem, raise ... from records the cause, and a small custom exception class gives callers a precise name to catch. add_note (Python 3.11) attaches extra context, and assert is for checking your own assumptions, not user input.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 7 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write class InvalidScore(ValueError): pass and a function grade(score) that raises InvalidScore if the score is not between 0 and 100, otherwise returns "pass" for 40 or more and "fail" below.
Write load_age(data) for a dict like {"age": "x"}. It must raise ValueError("invalid age") chained from the original error when data["age"] is missing or not a number.
Write a function that tries int(text), adds a note with the text when it fails and re-raises. Catch it outside and print the notes.
Explain it without notes
How do you create a custom exception, and why would you rather do that than raise Exception("...")?
Explain the difference between raise X from Y, raising inside an except block without from, and raise X from None.
When is assert appropriate, and when is it a bug waiting to happen?
What does a bare raise do, and where is it legal?
Expected output
port: 8080
error: invalid port setting
caused by: KeyError 'port'
error: invalid port setting
caused by: ValueError invalid literal for int() with base 10: 'eighty'