15. Exceptions

15.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

15.2. Most common exceptions

Tab. 15.1. Most common exceptions
Exception Raised when…
AttributeError Attribute reference or assignment fails
ImportError Import statement fails
IndexError Sequence subscript is out of range
KeyError Dictionary key is not found
KeyboardInterrupt User hits the interrupt key (Control-C)
NameError Local or global name is not found
NotImplementedError User defined, ie. abstract methods require derived classes to override
SyntaxError Parser encounters a syntax error
IndentationError Syntax errors related to incorrect indentation
TypeError Operation or function is applied to an object of inappropriate type
RuntimeError Error is detected that doesn’t fall in any of the other categories

15.3. Raising exceptions

raise RuntimeError
raise RuntimeError('Some message')
def apollo18():
    raise NotImplementedError('Mission dropped due to budget cuts')

def apollo13():
    raise RuntimeError('Mid-flight Oxygen tank explosion')


apollo18()
apollo13()

15.4. Traceback analysis

def apollo13():
    raise RuntimeError('Mid-flight Oxygen tank explosion')

apollo13()
  • Stacktraces are 8 levels deep, it’s not Java’s 200 ;)

      File "/Users/matt/.virtualenvs/book-python/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 2961, in run_code
        exec(code_obj, self.user_global_ns, self.user_ns)
      File "<ipython-input-2-badb71482ca2>", line 1, in <module>
        runfile('/Users/matt/Developer/book-python/__notepad__.py', wdir='/Users/matt/Developer/book-python')
      File "/Applications/PyCharm 2018.3 EAP.app/Contents/helpers/pydev/_pydev_bundle/pydev_umd.py", line 198, in runfile
        pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
      File "/Applications/PyCharm 2018.3 EAP.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
        exec(compile(contents+"\n", file, 'exec'), glob, loc)
      File "/Users/matt/Developer/book-python/__notepad__.py", line 13, in <module>
        apollo13()
      File "/Users/matt/Developer/book-python/__notepad__.py", line 5, in apollo13
        raise RuntimeError('Mid-flight Oxygen tank explosion')
    RuntimeError: Mid-flight Oxygen tank explosion
    
  • Change level with sys.tracebacklimit:

    import sys
    sys.tracebacklimit = 1
    
  • 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

      File "/Users/matt/Developer/book-python/__notepad__.py", line 5, in apollo13
        raise RuntimeError('Mid-flight Oxygen tank explosion')
    RuntimeError: Mid-flight Oxygen tank explosion
    

15.5. Catching exceptions

  • try
  • except
  • else
  • finally
def apollo13():
    raise RuntimeError('Mid-flight Oxygen tank explosion')


try:
    apollo13()
except RuntimeError:
    print('Houston we have a problem!')
def apollo13():
    raise RuntimeError('Mid-flight Oxygen tank explosion')


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

def apollo13():
    raise RuntimeError('Mid-flight Oxygen tank explosion')


try:
    apollo13()
except RuntimeError as err:
    logging.error(err)
def apollo11():
    print('Program P63 - Landing Maneuvre 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')

Warning

Always catch exception!

# Problematic code which catches 'Ctrl-C'
# User cannot simply kill program
while True:
    try:
        number = float(input('Type number: '))
    except:
        continue
# User can kill program with 'Ctrl-C'
while True:
    try:
        number = float(input('Type number: '))
    except Exception:
        continue

15.6. 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

15.7. Defining own exceptions

import math


class CotangentDoesNotExistsError(ArithmeticError):
    pass


def cotangent(degrees):
    if degrees == 180:
        raise CotangentDoesNotExistsError('Cotangent for 180 degrees is infinite')

    radians = math.radians(degrees)
    return 1 / math.tan(radians)


cotangent(180)
# CotangentDoesNotExistsError: Cotangent for 180 degrees is infinite

15.8. Real life use-case

from django.contrib.auth.models import User

try:
    User.objects.get(username='jose-jimenez')
except User.DoesNotExists:
    print('No such user')

15.9. warnings

import warnings


def ariane5():
    warnings.warn('ariane5(), is deprecated, please use ariane6() instead', PendingDeprecationWarning)
    print('Launching rocket Ariane 5')

def ariane6():
    print('Launching rocket Ariane 6')


ariane5()
ariane6()
$ python __notepad__.py
$ python -W all __notepad__.py
__notepad__.py:5: PendingDeprecationWarning: ariane5(), is deprecated, please use ariane6() instead