Warning
This page is located in archive.

Computer Lab 09, debugging, code testing, exceptions

Error types:

  1. Syntax error (omitting colon at the end of the def statement, unclosed bracket, non-matching quotation marks, …)
  2. Runtime error (division by zero, indexing error, using Python's keyword for a variable name, …)
  3. Semantic error (error in the program's logic, hard to spot, interpreter nor runtime system is of no help here)

Syntax Error

def sum_numbers(lst)
    sum = 0
    for item in lst:
        sum += item
    return sum
  File "syntax01.py", line 1
    def sum_numbers(lst)   
                       ^   
SyntaxError: invalid syntax


def add_gold(inv, inc_gold):
    inv["gold"] += inc_gold
 
if __name__ == "__main__":
    inventory = {
    'gold' : 500,
    'pouch' : ['flint', 'twine', 'gemstone'],
    'backpack' : ['xylophone','dagger', 'bedroll','bread loaf']
 
 
    add_gold(inventory, 50)
    print(inventory)
  File "syntax02.py", line 11
    add_gold(inventory, 50)
           ^
SyntaxError: invalid syntax


mytuple = ("apple", "banana', "cherry")
 
for x in mytuple:
  print(x)
  File "syntax03.py", line 1
    mytuple = ("apple", "banana', "cherry")
                                        ^
SyntaxError: invalid syntax

Runtime Error

  • When a runtime error occurs an exception object is created.
  • Then, traceback and an error message are printed out.
  • If not handled, the program force ends.

NAME ERROR

if __name__ == "__main__":
    value += 1
    print("Value = ", value)
Traceback (most recent call last):
  File "runtime01.py", line 2, in <module>
    value += 1
NameError: name 'value' is not defined

TYPE ERROR (wrong number of parameters)

def sum_numbers(a, b):
    return a + b
 
if __name__ == "__main__":
    nr_1 = 8
    nr_2 = 10
    nr_3 = -6
    sum = sum_numbers(nr_1, nr_2, nr_3)
    print("sum = ", sum)
Traceback (most recent call last):
  File "runtime02.py", line 8, in <module>
    sum = sum_numbers(nr_1, nr_2, nr_3)
TypeError: sum_numbers() takes 2 positional arguments but 3 were given

TYPE ERROR (division of strings)

def div_numbers(a, b):
    return a / b
 
if __name__ == "__main__":
    nr_1 = input("Enter first number: ")
    nr_2 = input("Enter second number: ")
    sum = div_numbers(nr_1, nr_2)
    print("Quotient = " + str(sum))
Enter first number: 10
Enter second number: 5
Traceback (most recent call last):
  File "runtime02.py", line 7, in <module>
    sum = div_numbers(nr_1, nr_2)
  File "runtime02.py", line 2, in div_numbers
    return a / b
TypeError: unsupported operand type(s) for /: 'str' and 'str'

TYPE ERROR (trying to concatenate str and int)

def div_numbers(a, b):
    return a / b
 
if __name__ == "__main__":
    nr_1 = float(input("Enter first number: "))
    nr_2 = float(input("Enter second number: "))
    sum = div_numbers(nr_1, nr_2)
    print("Quotient = " + sum)
Enter first number: 10
Enter second number: 5
Traceback (most recent call last):
  File "runtime02.py", line 8, in <module>
    print("Quotient = " + sum)
TypeError: must be str, not float

KEY ERROR (indexing dictionary with a non-existent key)

fridge = {"apple": 10, 
            "banana": 8,
            "cherry": 50}
 
print(fridge["kiwi"])
Traceback (most recent call last):
  File "runtime03.py", line 5, in <module>
    print(fridge["kiwi"])
KeyError: 'kiwi'

KEY ERROR (indexing dictionary with a wrong data type)

fridge = {"apple": 10, 
            "banana": 8,
            "cherry": 50}
 
print(fridge[0])
Traceback (most recent call last):
  File "runtime03.py", line 5, in <module>
    print(fridge[0])
KeyError: 0

INDEX ERROR (how to make this pythonic?)

programming_languages = ["Java", "Python", "C++"]
count = 0 
 
while count <= len(programming_languages):
	print(programming_languages[count])
	count += 1
Java
Python
C++
Traceback (most recent call last):
  File "syntax04.py", line 5, in <module>
    print(programming_languages[count])
IndexError: list index out of range

ZERO DIVISION ERROR

import random 
 
def get_inverted(x):
    return 1/x
 
if __name__ == "__main__":
    random.seed(128)
    for _ in range(10):
        value = random.randrange(0,10)
        inv_value = get_inverted(value)
        print("1/{} is {}.".format(value, inv_value))
1/3 is 0.3333333333333333.
1/6 is 0.16666666666666666.
1/8 is 0.125.
1/5 is 0.2.
1/2 is 0.5.
1/7 is 0.14285714285714285.
Traceback (most recent call last):
  File "08_runtime.py", line 10, in <module>
    inv_value = get_inverted(value)
  File "08_runtime.py", line 4, in get_inverted
    return 1/x
ZeroDivisionError: division by zero

FILE NOT FOUND ERROR

filename = "dummyfile.txt"
f = open(filename, "r")
lines =  f.readlines()
for line in lines:
    print(line)
f.close()
Traceback (most recent call last):
  File "runtime05.py", line 2, in <module>
    f = open(filename, "r")
FileNotFoundError: [Errno 2] No such file or directory: 'dummyfile.txt'

Handling Runtime Errors

  • If not handled, the program stops after the exception is identified and an error message is printed out.
  • Usually, the parts of the code which might cause an exception are easily identified.
  • Use try-except to catch the exception and handle this situation yourself without stopping the program.

FLAWED VERSION

def div_numbers(a, b):
    return a / b
 
if __name__ == "__main__":
    nr_1 = input("Enter first number: ")
    nr_2 = input("Enter second number: ")
    sum = div_numbers(nr_1, nr_2)
    print("Quotient = " + str(sum))
Enter first number: 10
Enter second number: 5
Traceback (most recent call last):
  File "runtime02.py", line 7, in <module>
    sum = div_numbers(nr_1, nr_2)
  File "runtime02.py", line 2, in div_numbers
    return a / b
TypeError: unsupported operand type(s) for /: 'str' and 'str'

ATTEMPT #1: TRY-EXCEPT in the function

def div_numbers(a, b):
    try: 
        q = a / b
        return q
    except TypeError:
        return None
 
if __name__ == "__main__":
    nr_1 = input("Enter first number: ")
    nr_2 = input("Enter second number: ")
    sum = div_numbers(nr_1, nr_2)
 
    if sum is not None:
        print("Quotient = " + str(sum))
    else:
        print("Error when calling the div_numbers() function.")
Enter first number: 10
Enter second number: 5
Error when calling the div_numbers() function.

ATTEMPT #2: TRY-CATCH in the function + print(e)

try: 
        q = a / b
        return q
except TypeError as e:
        print(e)
        return None
TypeError: unsupported operand type(s) for /: 'str' and 'str'

ATTEMPT #3: TRY-CATCH in the function + print traceback

import traceback 
 
def div_numbers(a, b):
    try: 
        q = a / b
        return q
    except TypeError:
        # printing stack trace 
        traceback.print_exc() 
        return None
Traceback (most recent call last):
  File "handling01.py", line 5, in div_numbers
    q = a / b

ATTEMPT #4: SANITIZING INPUT DATA

def div_numbers(a, b):
        q = a / b
        return q
 
if __name__ == "__main__":
    try:
        nr_1 = float(input("Enter first number: "))
        nr_2 = float(input("Enter second number: "))
 
        sum = div_numbers(nr_1, nr_2)
        print("Quotient = " + str(sum))
    except ValueError:
        print("You did not enter valid numbers!")        

ATTEMPT #5: RAISING EXCEPTION

def div_numbers(a, b):
    if type(a) is not float or type(b) is not float:
        raise TypeError("a or b is not float!")
 
    q = a / b
    return q
 
if __name__ == "__main__":
 
    nr_1 = "a"
    nr_2 = 5.0
 
    try:
        sum = div_numbers(nr_1, nr_2)
        print("Quotient = " + str(sum))
    except TypeError as e:
        print(e)
a or b is not float!

Semantic Error

  • Usually very difficult to spot!
  • Mismatch between the problem specification and what the code actually does.
  • Is there something the program was supposed to do but which doesn't seem to be happening?
  • Is something happening that shouldn't?
  • Several methods to find where the problem is:
  1. Create a set of well-defined inputs and corresponding outputs. Compare these ground-truth outputs with the actual outputs of your code. Based on this, try to identify the place where the error could originate.
  2. Place diagnostic print() statements in your code to inspect the content of your variables.
  3. Use the debugger to step through your program run by line. Set up (conditional) breakpoints.
  4. Write doctests (you already know) + unittest (will be explained once you learn about objects) to make ensure that the code does not break with changes.

Spot the Error

EXERCISE 1

def sum_even_numbers(lst):
    sum = 0
    for item in lst:
        if item % 2 == 0:
            sum += item
        return sum
 
if __name__ == "__main__":
    input_list = [9, 1, 0, 2, 8]
    sum_of_list = sum_even_numbers(input_list)
    print(sum_of_list)

EXERCISE 2

x = float(input('Enter a number: '))
y = float(input('Enter a number: '))
 
z = x+y/2
print ('The average of the two numbers you have entered is:',z)
Enter a number: 3
Enter a number: 4
The average of the two numbers you have entered is: 5.0

EXERCISE 3

# A function to append a list onto itself, with the intention of
# returning a new list, but leaving the input unaltered
def double_list(in_list):
    """Append a list to itself."""
    in_list += in_list
    return in_list
 
# Make a list
my_list = [3, 2, 1]
 
# Double it
my_list_double = double_list(my_list)
 
# Later on in our program, we want a sorted my_list
my_list.sort()
 
# Let's look at my_list:
print('We expect [1, 2, 3]')
print('We get   ', my_list)

Weekly homework 09 - Piecewise function

In this homework, you will need to write 2 functions in one module (file):

  • Function my_pwc_function implementing a piecewise function defined by the following formula:

\[ f(x)= \begin{cases} (x-6)^2+5 & 6 \le x \\ 5 & 4 \leq x\lt 6 \\ \frac{5}{2}x-5 & 2 \leq x\lt 4 \\ \text{undefined} & x \lt 2 \end{cases} \]

def my_pwc_function(x):
    """Returns a function value of the piecewise function specified in the problem setting
 
    :param x: float, parameter of the mathematical function
    :return: float, corresponding function value 
    :raises: ValueError: for such 'x' where the piecewise function is undefined
 
    >>> my_pwc_function(5)
    5.0
    >>> my_pwc_function(3)
    2.5
    """

  • Function get_function_values(par_lst) which computes function values (using my_pwc_function()) of all numbers given in the par_lst list and stores all the results in a dictionary. In case of an undefined function value, place 'NA' string as a value to the corresponding key.

def get_function_values(par_lst):
    """Returns dictionary where the key are numbers from par_list and values are corresponding
    function values.
 
    :param par_lst: list of floats, values to be passed to the piecewise function
    :return: dict of x:my_pwc_function(x), function values
 
    >>> get_function_values([5, 1, 3])
    {5:5.0, 1:'NA', 3:2.5}
 
    """
Required filename: 09_weekly_hw.py.

courses/be5b33prg/labs/week_09.txt · Last modified: 2022/11/24 17:14 by ypsilnik