7.3. Exception Catching

  • try

  • except

  • else

  • finally

  • try is required and then one of the others blocks

>>> def run(value):
...     try:
...         return int(value)
...     except TypeError:
...         print('type error')
...     except ValueError:
...         print('value error')
>>> run( 1 )
1
>>> run( 1.0 )
1
>>> run('1')
1
>>> run('1.0')
value error
>>>
>>> run( 'one' )
value error
>>> run( [1, 0] )
type error

7.3.1. Catch Exception

  • Catch single exception

>>> def database_connect():
...     raise ConnectionError('Cannot connect to database')
>>>
>>>
>>> try:
...     database_connect()
... except ConnectionError:
...     print('Connection Error')
Connection Error

7.3.2. Catch Exception with Details

  • Catch single exception

>>> def database_connect():
...     raise ConnectionError('Cannot connect to database')
>>>
>>>
>>> try:
...     database_connect()
... except ConnectionError as error:
...     print(f'Connection Error: {error}')
Connection Error: Cannot connect to database

7.3.3. Catch Different Exceptions

  • Catch exceptions with different handling

>>> def database_connect():
...     raise ConnectionError('Cannot connect to database')
>>>
>>>
>>> try:
...     database_connect()
... except ConnectionRefusedError:
...     print('Connection Refused')
... except ConnectionAbortedError:
...     print('Connection Aborted')
... except ConnectionError:
...     print('Connection Error')
Connection Error

7.3.4. Catch Multiple Exception

  • Catch exceptions with the same handling

>>> def database_connect():
...     raise ConnectionError('Cannot connect to database')
>>>
>>>
>>> try:
...     database_connect()
... except (ConnectionRefusedError, ConnectionAbortedError):
...     print('Cannot establish connection')
... except ConnectionError:
...     print('Connection Error')
Connection Error

7.3.5. Else

  • else is executed when no exception occurred

>>> def database_connect():
...     raise ConnectionError('Cannot connect to database')
>>>
>>>
>>> try:
...     db = database_connect()
... except ConnectionError:
...     print('Connection Error')
... else:
...     print('Connection Established')
...     db.query('SELECT * FROM users')
Connection Error

7.3.6. Finally

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

finally is executed always (even if there was exception). Typically it is used to close file, connection or transaction to database:

>>> def database_connect():
...     raise ConnectionError('Cannot connect to database')
>>>
>>>
>>> try:
...     db = database_connect()
... except ConnectionError:
...     print('Connection Error')
... else:
...     print('Connection Established')
...     db.query('SELECT * FROM users')
... finally:
...     print('Connection Closed')
Connection Error
Connection Closed

7.3.7. Pokemon Exception Handling

  • "Gotta catch 'em all"

  • Ctrl-C raises KeyboardInterrupt

  • Operating system shutdown raises SystemExit

One cannot simply kill program with Ctrl-C:

>>> 
... while True:
...     try:
...         number = float(input('Type number: '))
...     except:
...         continue

One can kill program with Ctrl-C:

>>> 
... while True:
...     try:
...         number = float(input('Type number: '))
...     except Exception:
...         continue

7.3.8. Exception Chain

>>> def database_connect():
...     raise ConnectionError
>>>
>>>
>>> try:
...     database_connect()
... except ConnectionError as error:
...     raise RuntimeError('Failed to open database') from error  
Traceback (most recent call last):
ConnectionError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
RuntimeError: Failed to open database

7.3.9. Exception Chain Silencing

>>> def database_connect():
...     raise ConnectionError
>>>
>>>
>>> try:
...     database_connect()
... except ConnectionError:
...     raise RuntimeError('Failed to open database') from None
Traceback (most recent call last):
RuntimeError: Failed to open database

7.3.10. Recap

>>> try:
...     'Expression to evaluate'
... except TypeError:
...     'Catch single exception'
... except ValueError as error:
...     'Catch single exception and use it as "error" variable'
... except (IndexError, KeyError):
...     'Catch multiple exceptions'
... except Exception:
...     'Catch any other exceptions'
... else:
...     'What to do if no exception occurs'
... finally:
...     'What to do either if exception occurs or not'
'Expression to evaluate'
'What to do if no exception occurs'
'What to do either if exception occurs or not'

7.3.11. Use Case - 0x01

>>> def database_connect():
...     print('Connecting...')
>>>
>>>
>>> try:
...     db = database_connect()
... except ConnectionError:
...     print('Sorry, no internet connection')
... except PermissionError:
...     print('Sorry, permission denied')
... except Exception:
...     print('Sorry, unknown error')
... else:
...     print('Connection established')
...     print('Executing query...')
... finally:
...     print('Disconnect from database')
Connecting...
Connection established
Executing query...
Disconnect from database

7.3.12. Use Case - 0x02

>>> try:
...     with open(r'/tmp/my-file.txt') as file:
...         print(file.read())
... except FileNotFoundError:
...     print('File does not exist')
... except PermissionError:
...     print('Permission denied')
File does not exist

7.3.13. Use Case - 0x03

SetUp:

>>> from pathlib import Path
>>> Path('/tmp/myfile.txt').unlink(missing_ok=True)

Execute

>>> try:
...     file = open('/tmp/myfile.txt')
... except Exception:
...     print('Error, file cannot be open')
... finally:
...     print('Close file')
Error, file cannot be open
Close file

7.3.14. Use Case - 0x03

>>> def apollo13():
...     raise RuntimeError('Oxygen tank explosion')
>>>
>>>
>>> try:
...     apollo13()
... except RuntimeError:
...     print('Houston we have a problem')
Houston we have a problem

7.3.15. Use Case - 0x04

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

7.3.16. Use Case - 0x05

>>> 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("You'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
You're GO for landing
Returning safely to the Earth

7.3.17. Assignments

Code 7.4. Solution
"""
* Assignment: Exception Catch Except
* Required: yes
* Complexity: easy
* Lines of code: 4 lines
* Time: 3 min

English:
    1. Convert value passed to the `result` function as a `float`
    2. If conversion fails then print 'Invalid value'
    3. Non-functional requirements:
        a. Write solution inside `result` function
        b. Mind the indentation level
    4. Run doctests - all must succeed

Polish:
    1. Przekonwertuj wartośc przekazaną do funckji `result` jako `float`
    2. Jeżeli konwersja się nie powiedzie to wypisz 'Invalid value'
    3. Wymagania niefunkcjonalne:
        a. Rozwiązanie zapisz wewnątrz funkcji `result`
        b. Zwróć uwagę na poziom wcięć
    4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `try`
    * `except`
    * `print()`

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> result('1')
    >>> result('1.0')
    >>> result('1,0')
    Invalid value
"""


def result(value):
    ...


Code 7.5. Solution
"""
* Assignment: Exception Catch Else
* Required: yes
* Complexity: easy
* Lines of code: 7 lines
* Time: 5 min

English:
    1. Convert value passed to the `result` function as a `float`
    2. If conversion fails, raise `TypeError`
    3. If value is below ADULT, raise `PermissionError`
    4. Non-functional requirements:
        a. Write solution inside `result` function
        b. Mind the indentation level
    5. Run doctests - all must succeed

Polish:
    1. Przekonwertuj wartośc przekazaną do funckji `result` jako `float`
    2. Jeżeli konwersja się nie powiedzie, podnieś `TypeError`
    3. Jeżeli wartość jest poniżej ADULT, podnieś `PermissionError`
    4. Wymagania niefunkcjonalne:
        a. Rozwiązanie zapisz wewnątrz funkcji `result`
        b. Zwróć uwagę na poziom wcięć
    5. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `try`
    * `except`
    * `else`
    * `raise`

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> result(21)
    >>> result('one')
    Traceback (most recent call last):
    TypeError
    >>> result(1)
    Traceback (most recent call last):
    PermissionError
"""

ADULT = 18


def result(age):
    ...


Code 7.6. Solution
"""
* Assignment: Exception Catch Finally
* Required: yes
* Complexity: easy
* Lines of code: 8 lines
* Time: 8 min

English:
    1. Convert value passed to the function as a `degrees: int`
    2. If conversion fails, raise exception `TypeError` with message:
       'Invalid type, expected int or float'
    3. Use `finally` to print `degrees` value
    4. Non-functional requirements:
        a. Write solution inside `result` function
        b. Mind the indentation level
    5. Run doctests - all must succeed

Polish:
    1. Przekonwertuj wartość przekazaną do funckji jako `degrees: int`
    2. Jeżeli konwersja się nie powiedzie to podnieś wyjątek `TypeError`
       z komunikatem 'Invalid type, expected int or float'
    3. Użyj `finally` do wypisania wartości `degrees`
    4. Wymagania niefunkcjonalne:
        a. Rozwiązanie zapisz wewnątrz funkcji `result`
        b. Zwróć uwagę na poziom wcięć
    5. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `try`
    * `except`
    * `finally`
    * `raise`

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> result(1)
    1
    >>> result(180)
    180
    >>> result(1.0)
    1
    >>> result('one')
    Traceback (most recent call last):
    TypeError: Invalid type, expected int or float
"""


def result(degrees):
    ...