Exceptions

Published

2023-08-02

Exceptions

By this time, you’ve seen quite a few exceptions. Exceptions occur when something goes wrong. We refer to this as raising an exception.

Exceptions may be raised by the Python interpreter or by built-in functions or by methods provided by Python modules. You may even raise exceptions in your own code (but we’ll get to that later).

Exceptions include information about the type of exception which has been raised, and where in the code the exception occurred. Sometimes, quite a bit of information is provided by the exception. In general, a good approach is to start at the last few lines of the exception message, and work backward if necessary to see what went wrong.

There are many types of built-in exceptions in Python. Here are a few that you’re likely to have seen before.

SyntaxError

When a module is executed or imported, Python will read the file, and try parsing the file. If, during this process, the parser encounters a syntax error, a SyntaxError exception is raised. SyntaxError can also be raised when invalid syntax is used in the Python shell.

>>> # if requires a condition and colon
>>> if
  File "<stdin>", line 1
    if
      ^
SyntaxError: invalid syntax

Here you see the exception includes information about the error and where the error occurred. The ^ is used to point to a portion of code where the error occurred.

IndentationError

IndentationError is a subtype of SyntaxError. Recall that indentation is significant in Python—we use it to structure branches, loops, and functions. So IndentationError occurs when Python encounters a syntax error that it attributes to invalid indentation.

if True:
x = 1    # This should be indented!
  File "<stdin>", line 2
    x = 1  # should be indented
    ^
IndentationError: expected an indented block 
    after 'if' statement on line 1

Again, almost everything you need to know is included in the last few lines of the message. Here Python is informing us that it was expecting an indented block of code immediately following an if statement.

AttributeError

There are several ways an AttributeError can be raised. You may have encountered an AttributeError by misspelling the name of a method in a module you’ve imported.

>>> import math
>>> math.foo   # There is no such method or constant in math
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'math' has no attribute 'foo'

An AttributeError is different from a SyntaxError in that it occurs at runtime, and not during the parsing or early processing of code. An AttributeError is only related to the availability of attributes and not violations of the Python syntax rules.

NameError

A NameError is raised when Python cannot find an identifier. For example, if you were to try to perform a calculation with some variable x without previously having assigned a value to x.

>>> x + 1   # Without having previously assigned a value to x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

Any attempted access of an undefined variable will result in a NameError.

>>> foo   # Wasn't defined earlier
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined

IndexError

Individual elements of a sequence can be accessed using an index into the sequence. This presumes, of course, that the index is valid—that is, there is an element at that index. If you try to access an element of a sequence by its index, and there is no index, Python will raise an IndexError.

>>> lst = []
>>> lst[2]   # There is no element at index 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

This (above) fails because the list is empty and there is no element at index 2. Hence, 2 is an invalid index and an IndexError is raised.

Here’s another example:

>>> alphabet = 'abcdefghijklmnopqrstuvwxyz'
>>> alphabet[26]  # Python is 0-indexed so z has index 25
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range

TypeError

A TypeError is raised when a value of one type is expected and a different type is supplied. For example, sequence indices—for lists, tuples, strings—must be integers. If we try using a float or str as an index, Python will raise a TypeError.

>>> lst = [6, 5, 4, 3, 2]
>>> lst['foo']   # Can't use a string as an index
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers or slices, not str
>>> lst[1.0]   # Can't use a float as an index
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers or slices, not float

Python will also raise a TypeError if we try to perform operations on operands which are not supported. For example, we cannot concatenate an int to a str or add a str to an int.

>>> 'foo' + 1  # Try concatenating 1 with 'foo'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
>>> 1 + 'foo'  # Try adding 'foo' to 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

We cannot calculate the sum of a number and a string. We cannot calculate the sum of any list or tuple which contains a string.

>>> sum('Add me up!')  # Can't sum a string
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> sum(1)  # Python sum() requires an iterable of numerics
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

By the same token, we cannot get the length of a float or int.

>>> len(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()

ValueError

A ValueError is raised when the type of some argument or operand is correct, but the value is not. For example, math.sqrt(x) will raise a ValueError if we try to take the square root of a negative number.

>>> import math
>>> math.sqrt(-1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: math domain error

Note that dividing by zero is considered an arithmetic error, and has its own exception (see below).

ZeroDivisionError

Just as in mathematics, Python will not allow us to divide by zero. If we try to, Python will raise a ZeroDivisionError. Note that this occurs with floor division and modulus as well (as they depend on division).

>>> 10 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 10 % 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> 10 // 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

FileNotFoundError

We’ve seen how to open files for reading and writing. There are many ways this can go wrong, but one common issue is FileNotFoundError. This exception is raised when Python cannot find the specified file. The file may not exist, may be in the wrong directory, or may be named incorrectly.

open('non-existent file')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 
    'non-existent file'

Original author: Clayton Cafiero < [given name] DOT [surname] AT uvm DOT edu >

No generative AI was used in producing this material. This was written the old-fashioned way.

This material is for free use under either the GNU Free Documentation License or the Creative Commons Attribution-ShareAlike 3.0 United States License (take your pick).