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

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

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

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

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

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

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

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

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

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

15.4.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'

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

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

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

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

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

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

15.4.17. Assignments

Code 15.4. Solution
"""
* Assignment: Exception Catch Except
* Type: class assignment
* 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. Write solution inside `result` function
    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. Rozwiązanie zapisz wewnątrz funkcji `result`
    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
"""

# Convert value passed to the `result` function as a `float`
# If conversion fails then print 'Invalid value'
# Write solution inside `result` function
# type: Callable[[str], None]
def result(value):
    ...


Code 15.5. Solution
"""
* Assignment: Exception Catch Else
* Type: class assignment
* 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. Write solution inside `result` function
    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. Rozwiązanie zapisz wewnątrz funkcji `result`
    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

# Przekonwertuj wartośc przekazaną do funckji `result` jako `float`
# Jeżeli konwersja się nie powiedzie, podnieś `TypeError`
# Jeżeli wartość jest poniżej ADULT, podnieś `PermissionError`
# Rozwiązanie zapisz wewnątrz funkcji `result`
# type: Callable[[int|float]], None]
def result(age):
    ...


Code 15.6. Solution
"""
* Assignment: Exception Catch Finally
* Type: class assignment
* 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. Write solution inside `result` function
    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. Rozwiązanie zapisz wewnątrz funkcji `result`
    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
"""

# Convert value passed to the function as a `degrees: int`
# If conversion fails, raise exception `TypeError` with message:
# 'Invalid type, expected int or float'
# Use `finally` to print `degrees` value
# Write solution inside `result` function
# type: Callable[[int], None]
def result(degrees):
    ...