2.2. Syntax Exceptions
2.2.1. Custom Exceptions
Class which inherits from
Exception
Exceptions should have
Error
at the end of their names
>>> class MyError(Exception):
... pass
Raise without a message:
>>> raise MyError
Traceback (most recent call last):
MyError
Raise with a message:
>>> class MyError(Exception):
... pass
>>>
>>>
>>> raise MyError('More verbose description')
Traceback (most recent call last):
MyError: More verbose description
Catch:
>>> class MyError(Exception):
... pass
>>>
>>> def run():
... raise MyError('we have some problem')
>>>
>>>
>>> try:
... run()
... except MyError as err:
... print(f'Exception happened: {err}')
Exception happened: we have some problem
2.2.2. Embed Exception
Exception can be embedded in a class
Class serves as a namespace
>>> class User:
... def get_from_database(username):
... # some logic to get user from database
... result = ...
... if not result:
... raise self.DoesNotExist
...
... class DoesNotExist(Exception):
... pass
Usage:
>>> try:
... mark = User.get_from_database(username='mwatney')
... except User.DoesNotExist:
... print('Error, user does not exist')
2.2.3. Exception Chain
>>> def login(username, password):
... raise RuntimeError('Cannot login')
>>>
>>>
>>> try:
... login('mwatney', 'Ares3')
... except RuntimeError as err:
... raise PermissionError('Invalid credentials') from err
...
Traceback (most recent call last):
RuntimeError: Cannot login
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
PermissionError: Invalid credentials
2.2.4. Exception Chain Silencing
>>> def login(username, password):
... raise RuntimeError('Cannot login')
>>>
>>>
>>> try:
... login('mwatney', 'Ares3')
... except RuntimeError as err:
... raise PermissionError('Invalid credentials') from None
...
Traceback (most recent call last):
PermissionError: Invalid credentials
2.2.5. Use Case 1
>>> class InvalidCredentials(Exception):
... pass
>>>
>>> class User:
... def __init__(self, username, password):
... self.username = username
... self.password = password
...
... def login(self, username, password):
... if self.username != username or self.password != password:
... raise InvalidCredentials('Invalid username or password')
>>> mark = User('mwatney', 'Ares3')
>>>
>>> mark.login('mwatney', 'invalid')
Traceback (most recent call last):
InvalidCredentials: Invalid username or password
2.2.6. Use Case 2
Django Framework Use-case of Custom Exceptions:
>>>
... 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.DoesNotExist:
... print('Sorry, no such user in database')
2.2.7. Use Case 3
Dragon
>>> class Dragon:
... def take_damage(self, damage):
... if damage >= 10:
... raise self.IsDead
...
... class IsDead(Exception):
... pass
>>>
>>>
>>> wawelski = Dragon()
>>>
>>> try:
... wawelski.take_damage(100)
... except Dragon.IsDead:
... print('Dragon is dead')
Dragon is dead
2.2.8. Assignments
# %% 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: Syntax Exception Define
# - Difficulty: easy
# - Lines: 2
# - Minutes: 2
# %% English
# 1. Define new exception `NegativeKelvinError`
# 2. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj nowy wyjątek `NegativeKelvinError`
# 2. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `class`
# - `pass`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isclass
>>> isclass(NegativeKelvinError)
True
>>> issubclass(NegativeKelvinError, Exception)
True
"""
# Define new exception `NegativeKelvinError`
# type: type[Exception]
...
# %% 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: Syntax Exception Raise
# - Difficulty: easy
# - Lines: 2
# - Minutes: 2
# %% English
# 1. Check value `value` passed to a `result` function
# 2. If `value` is lower than 0, raise `NegativeKelvinError`
# 3. Run doctests - all must succeed
# %% Polish
# 1. Sprawdź wartość `value` przekazaną do funckji `result`
# 2. Jeżeli `value` jest mniejsze niż 0, podnieś `NegativeKelvinError`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `raise`
# - `if`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isclass
>>> isclass(NegativeKelvinError)
True
>>> issubclass(NegativeKelvinError, Exception)
True
>>> result(1)
>>> result(0)
>>> try:
... result(-1)
... except NegativeKelvinError:
... True
True
"""
class NegativeKelvinError(Exception):
pass
# Check value `value` passed to a `result` function
# If `value` is lower than 0, raise `NegativeKelvinError`
# type: Callable[[int], NoReturn]
def result(value):
...
# %% 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: Syntax Exception Embed
# - Difficulty: easy
# - Lines: 2
# - Minutes: 3
# %% English
# 1. Modify `User` class
# 2. Add new exception `DoesNotExist` inside `User` class
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zmodyfikuj klasę `User`
# 2. Dodaj nowy wyjątek `DoesNotExist` wewnątrz klasy `User`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `class`
# - `pass`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isclass
>>> isclass(User.DoesNotExist)
True
>>> issubclass(User.DoesNotExist, Exception)
True
"""
# Modify `User` class
# Add new exception `DoesNotExist` inside `User` class
# type: type[User]
class User:
def __init__(self, username):
self.username = username
def __str__(self):
return f"User('{self.username}')"
# %% 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: Syntax Exception UserDoesNotExist
# - Difficulty: easy
# - Lines: 2
# - Minutes: 3
# %% English
# 1. Modify function `login()`
# 2. Check if combination of username and password exists in `DATA`:
# - if yes: return `User` instance
# - if not: raise `User.DoesNotExist` exception
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zmodyfikuj funkcję `login`
# 2. Sprawdź czy kombinacja username i password występuje w `DATA`:
# - jeżeli tak: zwróć instancję klasy `User`
# - jeżeli nie: podnieś wyjątek `User.DoesNotExist`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `class`
# - `pass`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isclass
>>> isclass(User.DoesNotExist)
True
>>> issubclass(User.DoesNotExist, Exception)
True
>>> try:
... user = login('mwatney', 'Ares3')
... except User.DoesNotExist:
... print('Invalid username and/or password')
... else:
... print('User login')
User login
>>> try:
... user = login('mwatney', 'invalid')
... except User.DoesNotExist:
... print('Invalid username and/or password')
... else:
... print('User login')
Invalid username and/or password
>>> try:
... user = login('invalid', 'Ares3')
... except User.DoesNotExist:
... print('Invalid username and/or password')
... else:
... print('User login')
Invalid username and/or password
"""
DATA = [
{'username': 'mwatney', 'password': 'Ares3'},
{'username': 'mlewis', 'password': 'Nasa69'},
{'username': 'rmartinez', 'password': 'Saturn5'},
]
class User:
def __init__(self, username):
self.username = username
def __str__(self):
return f"User('{self.username}')"
class DoesNotExist(Exception):
pass
# Modify function `login()`
# Check if combination of username and password exists in `DATA`:
# - if yes: return `User` instance
# - if not: raise `User.DoesNotExist` exception
# type: Callable[[str,str], User|Exception]
def login(username, password):
...
# %% 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: Syntax Exception IsDead
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3
# %% English
# 1. Modify `Hero` class
# 2. Add new exception `IsDead` inside `Hero` class
# 3. If `health` is equal or lower than 0, raise `IsDead`
# 4. Run doctests - all must succeed
# %% Polish
# 1. Zmodyfikuj klasę `Hero`
# 2. Dodaj nowy wyjątek `IsDead` wewnątrz klasy `Hero`
# 3. Jeżeli `health` jest równy lub mniejszy niż 0, podnieś `IsDead`
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `class`
# - `pass`
# - `raise`
# - `if`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isclass
>>> isclass(Hero.IsDead)
True
>>> issubclass(Hero.IsDead, Exception)
True
>>> hero = Hero('Mark Watney')
>>> hero.take_damage(1)
>>> try:
... hero.take_damage(20)
... except hero.IsDead:
... True
True
"""
# Modify `Hero` class
# Add custom exception `IsDead` inside `Hero` class
# If `health` is equal or lower than 0, raise `IsDead`
# type: type[Hero]
class Hero:
def __init__(self, name):
self.name = name
self.health = 10
def take_damage(self, damage):
self.health -= damage