8.1. Exceptions

8.1.1. What are and why to use exceptions?

  • Used when error occurs

  • You can catch exception and handles erroneous situation

  • Exception example situations:

    • File does not exists

    • Function argument is invalid

    • Network or database connection could not be established

8.1.2. Most common exceptions

8.1.2.1. AttributeError

  • Attribute reference or assignment fails

Listing 142. AttributeError exception
name = 'Jan'
name.append('Twardowski')
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AttributeError: 'str' object has no attribute 'append'

8.1.2.2. ImportError, ModuleNotFoundError

  • Module could not be located

Listing 143. ModuleNotFoundError exception
import math
import match
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# ModuleNotFoundError: No module named 'match'

8.1.2.3. IndexError

  • Sequence subscript is out of range

Listing 144. IndexError exception
DATA = ['a', 'b', 'c']
DATA[100]
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# IndexError: list index out of range

8.1.2.4. KeyError

  • Dictionary key is not found

Listing 145. KeyError exception
DATA = {'a': 1, 'b': 2}
DATA['x']
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# KeyError: 'x'

8.1.2.5. NameError

  • Local or global name is not found

Listing 146. KeyError exception
print(first_name)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# NameError: name 'first_name' is not defined

8.1.2.6. SyntaxError

  • Parser encounters a syntax error

Listing 147. SyntaxError exception
if True
    print('Yes')
# Traceback (most recent call last):
#   File "<stdin>", line 1
#     if True
#           ^
# SyntaxError: invalid syntax

8.1.2.7. IndentationError

  • Syntax errors related to incorrect indentation

Listing 148. IndentationError exception
if True:
   print('Hello!')
    print('My name...')
   print('Jose Jimenez')
# Traceback (most recent call last):
#   File "<stdin>", line 1
#     print('My name...')
#     ^
# IndentationError: unexpected indent

8.1.2.8. TypeError

  • Operation or function is applied to an object of inappropriate type

Listing 149. TypeError exception
42 + 'a'
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: unsupported operand type(s) for +: 'int' and 'str'
Listing 150. TypeError exception
'a' + 42
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: can only concatenate str (not "int") to str

8.1.2.9. ValueError

  • Argument is right type but an inappropriate value

Listing 151. ValueError exception
float(1.2)
# 1.2

float(1,2)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: float expected at most 1 arguments, got 2

8.1.3. Exception hierarchy

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

8.1.4. Raising exceptions

Listing 152. Raise Exception without message
raise RuntimeError
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# RuntimeError
Listing 153. Exception with additional message
raise RuntimeError('Some message')
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# RuntimeError: Some message

8.1.5. Use case

temperature = input('Type temperature [Kelvin]: ')
# Type temperature [Kelvin]: -10<ENTER>

if float(temperature) < 0:
    raise ValueError
# Traceback (most recent call last):
#   File "<stdin>", line 2, in <module>
# ValueError
temperature = input('Type Temperature [Kelvin]: ')

if type(temperature) not in (float, int):
    raise TypeError('Argument ``a`` must be int or float')

if float(temperature) < 0:
    raise ValueError('Kelvin temperature cannot be negative')

print(temperature)
def apollo13():
    raise RuntimeError('Oxygen tank explosion')

apollo13()
# Traceback (most recent call last):
#   File "<stdin>", line 5, in <module>
#   File "<stdin>", line 2, in apollo13
# RuntimeError: Oxygen tank explosion
def apollo18():
    raise NotImplementedError('Mission dropped due to budget cuts')

apollo18()
# Traceback (most recent call last):
#   File "<stdin>", line 5, in <module>
#   File "<stdin>", line 2, in apollo18
# NotImplementedError: Mission dropped due to budget cuts

8.1.6. Assertion

  • Raises AssertionError if argument is False

  • Can have optional message

import sys

assert sys.version_info >= (3, 8)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AssertionError

assert sys.version_info >= (3, 8), "Python 3.8+ required."
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AssertionError: Python 3.8+ required.

8.1.7. Traceback

8.1.7.1. Traceback analysis

  • Stacktrace is 8 levels deep, it's not Java's 200 ;)

raise RuntimeError
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# RuntimeError
raise RuntimeError('Huston we have a problem')
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# RuntimeError: Huston we have a problem
def apollo13():
    raise RuntimeError('Oxygen tank explosion')

apollo13()
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 2, in apollo13
# RuntimeError: Oxygen tank explosion
def apollo13():
    raise RuntimeError('Oxygen tank explosion')

apollo13()
# Traceback (most recent call last):
#   File "<input>", line 1, in <module>
#   File "/Applications/PyCharm 2019.2 EAP.app/Contents/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
#     pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
#   File "/Applications/PyCharm 2019.2 EAP.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
#     exec(compile(contents+"\n", file, 'exec'), glob, loc)
#   File "/home/python/my_script.py", line 4, in <module>
#     apollo13()
#   File "/home/python/my_script.py", line 2, in apollo13
#     raise RuntimeError('Oxygen tank explosion')
# RuntimeError: Oxygen tank explosion

8.1.7.2. Change verbosity level

  • Change level with sys.tracebacklimit

  • From time to time you can have problems somewhere in the middle, but it's rare

  • Last lines are the most important, in most cases error is there

import sys
sys.tracebacklimit = 2


def apollo13():
    raise RuntimeError('Oxygen tank explosion')

apollo13()
# Traceback (most recent call last):
#   File "/home/python/my_script.py", line 4, in <module>
#     apollo13()
#   File "/home/python/my_script.py", line 2, in apollo13
#     raise RuntimeError('Oxygen tank explosion')
# RuntimeError: Oxygen tank explosion

8.1.8. Catching exceptions

  • try

  • except

  • else

  • finally

Listing 154. Catch single exception
def apollo13():
    raise RuntimeError('Oxygen tank explosion')


try:
    apollo13()
except RuntimeError:
    print('Houston we have a problem!')

# Houston we have a problem!
Listing 155. Catch many exceptions with the same handling
def apollo13():
    raise RuntimeError('Oxygen tank explosion')


try:
    apollo13()
except (RuntimeError, TypeError, NameError):
    print('Houston we have a problem!')

# Houston we have a problem!
Listing 156. Catch many exceptions with different handling
try:
    with open(r'/tmp/iris.csv') as file:
        print(file.read())

except FileNotFoundError:
    print('File does not exist')

except PermissionError:
    print('Permission denied')

# File does not exist
Listing 157. Exceptions logging
import logging


def apollo13():
    raise RuntimeError('Oxygen tank explosion')

try:
    apollo13()
except RuntimeError as err:
    logging.error(err)

# ERROR:root:Oxygen tank explosion

8.1.9. else and finally

  • else is executed when no exception occurred

  • finally is executed always (even if there was exception)

  • Used to close file, connection or transaction to database

Listing 158. else is executed when no exception occurred
def apollo11():
    print('Try landing on the Moon')

try:
    apollo11()
except Exception:
    print('Abort')
else:
    print('Landing a man on the Moon')

# Try landing on the Moon
# Landing a man on the Moon
Listing 159. finally is executed always (even if there was exception)
def apollo11():
    print('Try landing on the Moon')

try:
    apollo11()
except Exception:
    print('Abort')
finally:
    print('Returning safely to the Earth')

# Try landing on the Moon
# Returning safely to the Earth
def apollo11():
    print('Program P63 - Landing Manoeuvre Approach Phase')
    raise RuntimeError('1201 Alarm')
    raise RuntimeError('1202 Alarm')
    print('Contact lights')
    print('The Eagle has landed!')
    print("That's one small step for [a] man, one giant leap for mankind.")

try:
    apollo11()
except RuntimeError:
    print("Yo're GO for landing")
except Exception:
    print('Abort')
else:
    print('Landing a man on the Moon')
finally:
    print('Returning safely to the Earth')

# Program P63 - Landing Manoeuvre Approach Phase
# Yo're GO for landing
# Returning safely to the Earth

8.1.10. Always catch exceptions!

  • Ctrl-C raises KeyboardInterrupt

Listing 160. User cannot simply kill program with Ctrl-C
while True:
    try:
        number = float(input('Type number: '))
    except:
        continue
Listing 161. User can kill program with Ctrl-C
while True:
    try:
        number = float(input('Type number: '))
    except Exception:
        continue

8.1.11. Defining own exceptions

  • class which inherits from Exception

class MyError(Exception):
    pass


raise MyError
# Traceback (most recent call last):
#   File "<stdin>", line 5, in <module>
# MyError

raise MyError('More verbose description')
# Traceback (most recent call last):
#   File "<stdin>", line 5, in <module>
# MyError: More verbose description

8.1.11.1. Use-case

from django.contrib.auth.models import User


def login(request):
    username = request.POST.get('username')
    password = request.POST.get('password')

    try:
        user = User.objects.get(username, password)
    except User.DoesNotExists:
        print('Sorry, no such user in database')

8.1.12. Exit Status Code

  • exit with status 0 - no error

  • any other status - error

  • This will not work in Jupyter

try:
    float('hello')
except ValueError:
    print('Cannot type cast to float')
    exit(1)

# Cannot type cast to float
# [...] program exited with status 1

8.1.13. Assignments

8.1.13.1. Example

English
  1. Ask user to input angle in degrees

  2. Cotangens for 180 degrees is infinite

  3. Define own exception

  4. If user typed angle equal to 180, raise your exception

Polish
  1. Poproś użytkownika o wprowadzenie kąta

  2. Cotangens dla konta 180 ma nieskończoną wartość

  3. Zdefiniuj własny wyjątek

  4. Jeżeli użytkownik wprowadził kąt równy 180, podnieś swój wyjątek

Solution
class CotangentError(Exception):
    pass


degrees = input('Type angle [deg]: ')

if int(degrees) == 180:
    raise CotangentError('Cotangent for 180 degrees is infinite')

8.1.13.2. Raise Exception

English
  1. Ask user to input age

  2. If user has less than 18 years

  3. Raise an exception PermissionError with message "Adults only"

Polish
  1. Poproś użytkownika o wprowadzenie wieku

  2. Jeżeli użytkownik ma mniej niż 18 lat

  3. Wyrzuć wyjątek PermissionError z komunikatem "Adults only"

8.1.13.3. Catch Exception

English
  1. Ask user to input temperature in Kelvins

  2. Convert temperature to float

  3. Print 'Invalid temperature' if cannot type cast to float

  4. Print temperature

Polish
  1. Poproś użytkownika o wprowadzenie temperatury w Kelwinach

  2. Przekonwertuj temperaturę do float

  3. Wypisz "Invalid temperature" jak nie można rzutować do float

  4. Wypisz temperaturę

8.1.13.4. Define Exception

English
  1. Ask user to input temperature in Kelvins

  2. User will always type proper int or float

  3. Define exception for negative temperature

  4. Raise your exception if temperature is less than 0

Polish
  1. Poproś użytkownika o wprowadzenie temperatury w Kelwinach

  2. Użytkownik zawsze poda poprawne int lub float

  3. Zdefiniuj wyjątek dla temperatur ujemnych

  4. Podnieś własny wyjątek jeżeli temperatura jest poniżej 0