2.2. Syntax Custom Exceptions

2.2.1. Define Custom Exceptions

  • Class which inherits from Exception

  • Exceptions should have Error at the end of their names

>>> class MyError(Exception):
...     pass
>>>
>>>
>>> raise MyError
Traceback (most recent call last):
MyError
>>>
>>> raise MyError('More verbose description')
Traceback (most recent call last):
MyError: More verbose description

2.2.2. Example

>>> class InsufficientPrivileges(Exception):
...     pass
>>>
>>>
>>> role = 'user'
>>>
>>> if role != 'admin':
...     raise InsufficientPrivileges
Traceback (most recent call last):
InsufficientPrivileges

2.2.3. Use Case - 0x01

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.4. Use Case - 0x02

  • Dragon

>>> class Dragon:
...     def take_damage(self, damage):
...         raise self.IsDead
...
...     class IsDead(Exception):
...         pass
>>>
>>>
>>> wawelski = Dragon()
>>>
>>> try:
...     wawelski.take_damage(10)
... except Dragon.IsDead:
...     print('Dragon is dead')
Dragon is dead

2.2.5. Assignments

"""
* Assignment: Exception New Kelvin
* Type: homework
* Complexity: easy
* Lines of code: 4 lines
* Time: 2 min

English:
    1. Define new exception `NegativeKelvinError`
    2. Check value `value` passed to a `result` function
    3. If `value` is lower than 0, raise `NegativeKelvinError`
    4. Run doctests - all must succeed

Polish:
    1. Zdefiniuj nowy wyjątek `NegativeKelvinError`
    2. Sprawdź wartość `value` przekazaną do funckji `result`
    3. Jeżeli `value` jest mniejsze niż 0, podnieś `NegativeKelvinError`
    4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `class`
    * `pass`
    * `raise`
    * `if`

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isclass

    >>> isclass(NegativeKelvinError)
    True
    >>> issubclass(NegativeKelvinError, Exception)
    True
    >>> result(1)
    >>> result(0)

    >>> try:
    ...     result(-1)
    ... except NegativeKelvinError:
    ...     True
    ... except Exception:
    ...     False
    ... else:
    ...     False
    True
"""

# Define new exception `NegativeKelvinError`
# type: type[Exception]
...


# Check value `value` passed to a `result` function
# If `value` is lower than 0, raise `NegativeKelvinError`
# type: Callable[[int], NoReturn]
def result(value):
    ...


"""
* Assignment: Exception New IsDead
* Type: homework
* Complexity: easy
* Lines of code: 4 lines
* Time: 3 min

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
    >>> 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
    ... except Exception:
    ...     False
    ... else:
    ...     False
    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