15.3. Exception Catching

  • try - block of code that may raise exception

  • except - block of code that handles exception

  • else - block of code that is executed when no exception occurred

  • finally - block of code that is executed always (even if there was exception)

  • try is required and then one of the others blocks

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError:
...     print('Permission error')
... except (ConnectionRefusedError, ConnectionAbortedError) as err:
...     print(f'Connection error: {err}')
... else:
...     print('Login successful')
... finally:
...     print('Disconnect')
...
Permission error
Disconnect

15.3.1. Problem

  • If Python encounter an exception, the whole program crashes

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>>
>>> login('mwatney', 'Ares3')
Traceback (most recent call last):
PermissionError: Access denied

15.3.2. Solution

  • Avoid using except Exception

  • Always use specific exception like ValueError, TypeError, etc.

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except Exception:
...     print('Caught an exception')
...
Caught an exception

15.3.3. Catch Specific Exception

  • Catch single exception

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError:
...     print('Login error')
...
Login error

15.3.4. Catch Exception with Message

  • Catch single exception

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError as err:
...     print(f'Login error: {err}')
...
Login error: Access denied

15.3.5. Catch Different Exceptions

  • Catch exceptions with different handling

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError:
...     print('Login error')
... except ValueError:
...     print('Invalid password and/or username')
...
Login error

15.3.6. Catch Multiple Exception

  • Catch exceptions with the same handling

  • You can also capture the exception message with (..., ...) as err

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError:
...     print('Permission error')
... except (ConnectionRefusedError, ConnectionAbortedError) as err:
...     print(f'Connection error: {err}')
...
Permission error

15.3.7. Else

  • else is executed when no exception occurred

>>> def login(username, password):
...     pass
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError:
...     print('Permission error')
... else:
...     print('Login successful')
...
Login successful

15.3.8. 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 login(username, password):
...     raise PermissionError('Cannot login')
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError:
...     print('Permission error')
... finally:
...     print('Disconnect')
...
Permission error
Disconnect

15.3.9. Pokemon Exception Handling

  • "Gotta catch 'em all"

  • Ctrl-C raises KeyboardInterrupt

  • Operating system shutdown raises SystemExit

Except without specific exception (this could be a SyntaxError if future versions of Python):

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except:
...     print('Caught')
...
Caught

This is better, but still to broad:

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except Exception:
...     print('Caught')
...
Caught

Propert way to handle this situation is to catch specific exceptions:

>>> def login(username, password):
...     raise PermissionError('Access denied')
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError:
...     print('Caught')
...
Caught

15.3.10. Recap

  • try - block of code that may raise exception

  • except - block of code that handles exception

  • else - block of code that is executed when no exception occurred

  • finally - block of code that is executed always (even if there was exception)

  • try is required and then one of the others blocks

>>> def login(username, password):
...     raise PermissionError('Cannot login')
>>>
>>>
>>> try:
...     login('mwatney', 'Ares3')
... except PermissionError:
...     print('Permission error')
... except (ConnectionRefusedError, ConnectionAbortedError) as err:
...     print(f'Connection error: {err}')
... else:
...     print('Login successful')
... finally:
...     print('Disconnect')
...
Permission error
Disconnect

15.3.11. Use Case - 1

>>> 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.3.12. Use Case - 2

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

15.3.13. Use Case - 3

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

Proper way to handle this situation:

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

15.3.14. Assignments

# FIXME: Python 3.9 has different exception message then newer versions

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Exception Catch One
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3

# %% English
# 1. Modify function `convert`
# 2. Return `value` converted to `float`
# 3. If `ValueError` occurs, print 'Invalid value'
# 4. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj funkcję `convert`
# 2. Zwróć przekonwertowane `value` do `float`
# 3. Jeżeli wystąpi `ValueError`, wypisz 'Invalid value'
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `try`
# - `except`
# - `print()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> result = open(__file__).read()
>>> assert 'except'+':' not in result, \
'Do not leave empty except'

>>> convert(1)
1.0
>>> convert(1.0)
1.0
>>> convert(1,0)
Traceback (most recent call last):
TypeError: convert() takes 1 positional argument but 2 were given

>>> convert('1')
1.0
>>> convert('1.0')
1.0
>>> convert('1,0')
Invalid value

>>> convert((1.0))
1.0
>>> convert((1.0))
1.0
>>> convert((1,0))
Traceback (most recent call last):
TypeError: float() argument must be a string or a real number, not 'tuple'

>>> convert([1])
Traceback (most recent call last):
TypeError: float() argument must be a string or a real number, not 'list'
>>> convert([1.0])
Traceback (most recent call last):
TypeError: float() argument must be a string or a real number, not 'list'
>>> convert([1,0])
Traceback (most recent call last):
TypeError: float() argument must be a string or a real number, not 'list'
"""

# Modify function `convert`
# Return `value` converted to `float`
# If `ValueError` occurs, print 'Invalid value'
# type: Callable[[Any], float|None]
def convert(value):
    return float(value)


# FIXME: Python 3.9 has different exception message then newer versions

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Exception Catch Many
# - Difficulty: easy
# - Lines: 6
# - Minutes: 3

# %% English
# 1. Modify function `convert`
# 2. Return `value` converted to `float`
# 3. If `ValueError` occurs, print 'Invalid value'
# 4. If `TypeError` occurs, print 'Invalid type'
# 5. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj funkcję `convert`
# 2. Zwróć przekonwertowane `value` do `float`
# 3. Jeżeli wystąpi `ValueError`, wypisz 'Invalid value'
# 4. Jeżeli wystąpi `TypeError`, wypisz 'Invalid type'
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `try`
# - `except`
# - `print()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> result = open(__file__).read()
>>> assert 'except'+':' not in result, \
'Do not leave empty except'

>>> convert(1)
1.0
>>> convert(1.0)
1.0
>>> convert(1,0)
Traceback (most recent call last):
TypeError: convert() takes 1 positional argument but 2 were given

>>> convert('1')
1.0
>>> convert('1.0')
1.0
>>> convert('1,0')
Invalid value

>>> convert((1.0))
1.0
>>> convert((1.0))
1.0
>>> convert((1,0))
Invalid type

>>> convert([1])
Invalid type
>>> convert([1.0])
Invalid type
>>> convert([1,0])
Invalid type
"""

# Modify function `convert`
# Return `value` converted to `float`
# If `ValueError` occurs, print 'Invalid value'
# If `TypeError` occurs, print 'Invalid type'
# type: Callable[[Any], float|None]
def convert(value):
    return float(value)


# FIXME: Python 3.9 has different exception message then newer versions

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Exception Catch Many
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3

# %% English
# 1. Modify function `convert`
# 2. Return `value` converted to `float`
# 3. If `ValueError or `TypeError` occurs, print 'Invalid argument'
# 4. Use only one `except`
# 4. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj funkcję `convert`
# 2. Zwróć przekonwertowane `value` do `float`
# 3. Jeżeli wystąpi `ValueError` lub `TypeError`, wypisz 'Invalid argument'
# 4. Użyj tylko jednego `except`
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `try`
# - `except`
# - `print()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> result = open(__file__).read()
>>> assert 'except'+':' not in result, \
'Do not leave empty except'

>>> convert(1)
1.0
>>> convert(1.0)
1.0
>>> convert(1,0)
Traceback (most recent call last):
TypeError: convert() takes 1 positional argument but 2 were given

>>> convert('1')
1.0
>>> convert('1.0')
1.0
>>> convert('1,0')
Invalid argument

>>> convert((1.0))
1.0
>>> convert((1.0))
1.0
>>> convert((1,0))
Invalid argument

>>> convert([1])
Invalid argument
>>> convert([1.0])
Invalid argument
>>> convert([1,0])
Invalid argument
"""

# Modify function `convert`
# Return `value` converted to `float`
# If `ValueError` or `TypeError` occurs, print 'Invalid argument'
# Use only one `except`
# type: Callable[[Any], float|None]
def convert(value):
    return float(value)


# FIXME: Python 3.9 has different exception message then newer versions

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Exception Catch Exception
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3

# %% English
# 1. Modify function `convert`
# 2. Return `value` converted to `float`
# 3. If any exception occurs, then return `None`
# 4. Do not leave empty `except`
# 5. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj funkcję `convert`
# 2. Zwróć przekonwertowane `value` do `float`
# 3. Jeżeli wystąpi jakikolwiek wyjątek, to zwróć `None`
# 4. Nie pozostawiaj pustego `except`
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `try`
# - `except`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> result = open(__file__).read()
>>> assert 'except'+':' not in result, \
'Do not leave empty except'

>>> print(convert(1))
1.0
>>> print(convert(1.0))
1.0
>>> print(convert(1,0))
Traceback (most recent call last):
TypeError: convert() takes 1 positional argument but 2 were given

>>> print(convert('1'))
1.0
>>> print(convert('1.0'))
1.0
>>> print(convert('1,0'))
None

>>> print(convert((1.0)))
1.0
>>> print(convert((1.0)))
1.0
>>> print(convert((1,0)))
None

>>> print(convert([1]))
None
>>> print(convert([1.0]))
None
>>> print(convert([1,0]))
None
"""

# Modify function `convert`
# Return `value` converted to `float`
# If any exception occurs, then return `None`
# Do not leave empty `except`
# type: Callable[[Any], float|None]
def convert(value):
    return float(value)


# FIXME: Python 3.9 has different exception message then newer versions

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Exception Catch Message
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3

# %% English
# 1. Modify function `convert`
# 2. Return `value` converted to `float`
# 3. If any exception occurs, then print error original message and return `None`
# 4. Do not leave empty `except`
# 5. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj funkcję `convert`
# 2. Zwróć przekonwertowane `value` do `float`
# 3. Jeżeli wystąpi jakikolwiek wyjątek, to wypisz oryginalny komunikat i zwróć `None`
# 4. Nie pozostawiaj pustego `except`
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `try`
# - `except`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> result = open(__file__).read()
>>> assert 'except'+':' not in result, \
'Do not leave empty except'

>>> print(convert(1))
1.0
>>> print(convert(1.0))
1.0
>>> print(convert(1,0))
Traceback (most recent call last):
TypeError: convert() takes 1 positional argument but 2 were given

>>> print(convert('1'))
1.0
>>> print(convert('1.0'))
1.0
>>> print(convert('1,0'))
could not convert string to float: '1,0'
None

>>> print(convert((1.0)))
1.0
>>> print(convert((1.0)))
1.0
>>> print(convert((1,0)))
float() argument must be a string or a real number, not 'tuple'
None

>>> print(convert([1]))
float() argument must be a string or a real number, not 'list'
None
>>> print(convert([1.0]))
float() argument must be a string or a real number, not 'list'
None
>>> print(convert([1,0]))
float() argument must be a string or a real number, not 'list'
None
"""

# Modify function `convert`
# Return `value` converted to `float`
# If any exception occurs, then print error original message and return `None`
# Do not leave empty `except`
# type: Callable[[Any], float|None]
def convert(value):
    return float(value)


# FIXME: Python 3.9 has different exception message then newer versions

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Exception Catch Else
# - Difficulty: easy
# - Lines: 6
# - Minutes: 3

# %% English
# 1. Modify function `convert`
# 2. Try converting argument `value` do `float`
# 3. If any exception occurs, then return `False`
# 4. If no exception occurs, then return `True`
# 5. Use `try`, `except`, `else`
# 6. Do not leave empty `except`
# 7. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj funkcję `convert`
# 2. Spróbuj przekonwertować argument `value` do `float`
# 3. Jeżeli wystąpi jakikolwiek wyjątek, to zwróć `False`
# 4. Jeżeli nie wystąpi żaden wyjątek, to zwróć `True`
# 5. Użyj `try`, `except`, `else`
# 6. Nie pozostawiaj pustego `except`
# 7. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `try`
# - `except`
# - `else`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> result = open(__file__).read()
>>> assert 'except'+':' not in result, \
'Do not leave empty except'

>>> convert(1)
True
>>> convert(1.0)
True
>>> convert(1,0)
Traceback (most recent call last):
TypeError: convert() takes 1 positional argument but 2 were given

>>> convert('1')
True
>>> convert('1.0')
True
>>> convert('1,0')
False

>>> convert((1.0))
True
>>> convert((1.0))
True
>>> convert((1,0))
False

>>> convert([1])
False
>>> convert([1.0])
False
>>> convert([1,0])
False
"""

# Modify function `convert`
# Try converting argument `value` as a `float`
# If any exception occurs, then return `False`
# If no exception occurs, then return `True`
# Use `try`, `except`, `else`
# Do not leave empty `except`
# type: Callable[[Any], bool]
def convert(value):
    return float(value)


# FIXME: Python 3.9 has different exception message then newer versions

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Exception Catch Finally
# - Difficulty: easy
# - Lines: 2
# - Minutes: 3

# %% English
# 1. Modify function `convert`
# 2. Try converting argument `value` do `float`
# 3. If any exception occurs, then return `False`
# 4. If no exception occurs, then return `True`
# 5. After all (doesn't matter if exception occurred or not), print 'done'
# 6. Use `try`, `except`, `else`, `finally`
# 7. Do not leave empty `except`
# 8. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj funkcję `convert`
# 2. Spróbuj przekonwertować argument `value` do `float`
# 3. Jeżeli wystąpi jakikolwiek wyjątek, to zwróć `False`
# 4. Jeżeli nie wystąpi żaden wyjątek, to zwróć `True`
# 5. Po wszystkim (niezależnie czy wystąpił wyjątek czy nie), wypisz 'done'
# 6. Użyj `try`, `except`, `else`, `finally`
# 7. Nie pozostawiaj pustego `except`
# 8. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `try`
# - `except`
# - `else`
# - `finally`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> result = open(__file__).read()
>>> assert 'except'+':' not in result, \
'Do not leave empty except'

>>> convert(1)
done
True
>>> convert(1.0)
done
True
>>> convert(1,0)
Traceback (most recent call last):
TypeError: convert() takes 1 positional argument but 2 were given

>>> convert('1')
done
True
>>> convert('1.0')
done
True
>>> convert('1,0')
done
False

>>> convert((1.0))
done
True
>>> convert((1.0))
done
True
>>> convert((1,0))
done
False

>>> convert([1])
done
False
>>> convert([1.0])
done
False
>>> convert([1,0])
done
False
"""

# Modify function `convert`
# Try converting argument `value` do `float`
# If any exception occurs, then return `False`
# If no exception occurs, then return `True`
# After all (doesn't matter if exception occurred or not), print 'done'
# Use `try`, `except`, `else`, `finally`
# Do not leave empty `except`
# type: Callable[[Any], bool]
def convert(value):
    try:
        float(value)
    except Exception:
        return False
    else:
        return True