Practice

Syntax Errors

Let’s get more comfortable with exceptions. First, you’ve probably seen this one already: The IndentationError.

>>> def my_function():
... print("Hello!")
  File "<stdin>", line 2
    print("Hello!")
        ^
IndentationError: expected an indented block

Notice that we started a new function scope with the def keyword, but didn’t indent the next line of the function, the print() argument.

You’ve probably also seen the more general SyntaxError. This one’s probably obvious - something is misspelled, or the syntax is otherwise wrong. Python gives us a helpful little caret ^ under the earliest point where the error was detected, however you’ll have to learn to read this with a critical eye as sometimes the actual mistake precedes the invalid syntax. For example:

>>> a = [4,
... x = 5
  File "<stdin>", line 2
    x = 5
      ^
SyntaxError: invalid syntax

Here, the invalid syntax is x = 5, because assignment statements aren’t valid list elements, however the actual error is the missing right bracket ] on the line above.

Common Exceptions

You’ll get plenty of practice triggering syntax errors on your own. Let’s practice triggering some exceptions. Type this perfectly valid code into your REPL and see what happens:

>>> a = 1 / 0
Here's what you should have seen in your REPL:

Of course, you’ll get a divide-by-zero error, or as Python calls it, ZeroDivisionError. Some other common errors are TypeError when trying to perform an action on two unrelated types, KeyError when trying to access a dictionary key that doesn’t exist, and AttributeError when trying to access a variable or call a function that doesn’t exist on an object.

>>> 2 + "3"

>>> my_dict = {"hello": "world"}
>>> my_dict["foo"]

>>> my_dict.append("foo")
Here's what you should have seen in your REPL:

Raising our own Exceptions

Making our own Exceptions is cheap and easy, and useful for keeping track of various error states that are specific to your application. Simply inherit from the general Exception class:

>>> class MyException(Exception):
...     pass
>>> raise MyException()
Here's what you should have seen in your REPL:

It’s also sometimes helpful to change the default behavior for your custom Exceptions. In this case, you can simply provide your own __init__() method inside your Exception subclass:

class MyException(Exception):
...     def __init__(self, message):
...         new_message = f"!!!ERROR!!! {message}"
...         super().__init__(new_message)
...
>>> raise MyException("Something went wrong!")
Here's what you should have seen in your REPL:

try, except

In Python, the “try-catch” statements use try and except. As we discussed, try is the code that could possibly throw an Exception, and except is the code that runs if the error is raised. Practice catching a KeyError by trying to access a fake dictionary key:

>>> try:
...     my_dict = {"hello": "world"}
...     print(my_dict["foo"])
... except KeyError:
...     print("Oh no! That key doesn't exist")
...
Here's what you should have seen in your REPL:

Let’s add in catching the specific KeyError object so that we can access it during the except block:

>>> try:
...     my_dict = {"hello": "world"}
...     print(my_dict["foo"])
... except KeyError as key_error:
...     print(f"Oh no! The key {key_error} doesn't exist!")
...
Here's what you should have seen in your REPL:

Re-Raising

Sometimes it’s helpful to catch an error, perform an action, and then pass the error on rather than swallowing it. This is useful when, for example, something goes wrong deep inside your code and you need to perform a special action, but also let code further up the chain know that something is wrong and the program can’t continue. Let’s divide one number by other, decrementing until we hit zero. Catch that error and immediately raise a RuntimeError:

>>> while True:
...     for divisor in range(5, -1, -1):
...         try:
...             quotient = 10 / divisor
...             print(f"10 / {divisor} = {quotient}")
...         except ZeroDivisionError:
...             print("Oops! We tried to divide by zero!")
...             raise RuntimeError
...
Here's what you should have seen in your REPL: