Building better programs in Python (Part 3)

Error messages for syntax errors

If we're going to benefit from error messages, we need to understand what they are saying. Here are some examples that were presented earlier as well as the error messages that were generated.

Example 1

Running the code

print(31 23)

gives the error

Traceback (most recent call last):
  File "<string>", line 1
    print(31 23)
              ^
SyntaxError: invalid syntax

This syntax error indicated where the error was, but didn't specify what the error was.

Example 2

Running the code

def f(x, y)
    z = x + y

gives the error

Traceback (most recent call last):
  File "<string>", line 1
    def f(x, y)
              ^
SyntaxError: invalid syntax

In this example, we had a missing colon and, again, no details were given.

Example 3

Running the code

def f(x, y):
    z = x + y
return z

gives the error

Traceback (most recent call last):
  File "<string>", line 3
SyntaxError: 'return' outside function

Here, the return statement was indented incorrectly and the error message made it clear that it had to do with the return statement.


Decoding error messages

Let's look at a few more examples. We consider a code that has an incomplete line, so we can anticipate a syntax error. Running the code

print("string

gives the error

Traceback (most recent call last):
  File "<string>", line 1
    print("string
                ^
SyntaxError: EOL while scanning string literal

The message refers to a “string literal”, which means a value that is a string, and EOL, which means end of line. That is, the computer was looking for a matching quotation mark but instead reached the end of the line. Let's supply the quotation mark and see what happens: running the code

print("string"

gives the error

Traceback (most recent call last):
  File "<string>", line 1
    print("string"
                 ^
SyntaxError: unexpected EOF while parsing

The computer ran into EOF, that is, the end of the file, while parsing, that is, while trying to figure out the syntax of what had been written. In other words, it was trying to match the opening parenthesis but the program ended.

Here's a summary of what we learned, including the abbreviation EOL, and a meaning of the first error message:

  • EOL means End Of Line
    SyntaxError: EOL while scanning string literal means “I was reading a string but reached the end of the line before it ended.”
  • EOF means End Of File
    SyntaxError: unexpected EOF while parsing means “I was expecting to read more to make sense of what I'd read so far, but then I reached the end.”

Common syntax errors

  • Missing colon
  • Unmatched quotes, parentheses, or brackets
  • Use of = instead of ==
  • Inconsistent or missing indentation
  • Using a reserved keyword as an identifier

Since not all error messages are particularly descriptive or understandable, it is worth keeping in mind common errors, such as my speciality, leaving out colons, or unmatched quotation marks or parentheses, as we just saw in the example, or square or curly brackets, which we'll use later. Use of = instead of == is a mistake that I make a lot. Inconsistent or missing indentation is one that can be hard to detect if tabs and blank spaces are interpreted differently. Another tricky one is accidentally using a reserved keyword as an identifier. You can look on the help page to find a list of such words.

Caution

Where the error is detected may not be where the error occurred.

If you have trouble finding the error at the spot indicated by the output message, keep in mind that, although the error was detected at that point, it might have occurred earlier.

Semantics errors

Syntax errors are relatively easy to handle because of the error messages that are generated. For semantics errors, you need to use tests to check that what is produced is what you wanted.

Example 1

Consider the code

print(2 * input("Enter a number: "))

In this example the user input was printed twice as it was interpreted as a string, not a number.

Example 2

Consider the code

def is_odd_string(word):
    if len(word) % 2 == 1:
        return True

print(is_odd_string("ab"))

Here None is printed, since the function did not return a value when the string was even.

Example 3

Consider the code

def is_vowel(char):
    is_a = char == "a" or "A"
    is_e = char == "e" or "E"
    is_i = char == "i" or "I"
    is_o = char == "o" or "O"
    is_u = char == "u" or "U"
    return is_a or is_e or is_i or is_o or is_u

print(is_vowel("b"))

Here, the result is the string A, which was interpreted as a Boolean due to our mistaken use of or.


Error messages for runtime errors

Let's now look at runtime errors that we generated earlier.

TypeError

Even if you don't understand the details, the fact that it is a “TypeError” gives you a big clue: there is a mismatch between the type of data you have provided and the type that is required. For example, running the code

print("high" + 5)

gives the error

Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly

In this case, after a string and a plus sign, it was a string, not an integer, that should have been provided.

ValueError

In this example, we are using int on a string that was not a string form of an integer: running the code

print(int("dog"))

gives the error

Traceback (most recent call last):
  File "<string>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'dog'

Here we have a ValueError. There again is the term “literal”. We're being told that it is the wrong kind of value.

ZeroDivisionError

In this example, we are trying to divide by zero, which is made quite clear by the error message: running the code

def is_multiple(number, factor):
    return number % factor == 0

print(is_multiple(10, 0))

gives the error

Traceback (most recent call last):
  File "<string>", line 4, in <module>
  File "<string>", line 2, in is_multiple
ZeroDivisionError: integer division or modulo by zero

IndexError

Running the code

def starts_with_a(word):
    return word[0] == "a"

print(starts_with_a(""))

gives the error

Traceback (most recent call last):
  File "<string>", line 4, in <module>
  File "<string>", line 2, in starts_with_a
IndexError: string index out of range

The error here was caused by the fact that we were using the index 0 on the empty string, which is one kind of index error. More generally, this kind of error occurs when the index is for a position that is not in the string.