Exception Handling
To ensure consistency and proper handling, all exceptions must be instances of a class that derives from the BaseException class. This principle helps maintain code clarity and enables developers to catch and respond to specific error scenarios effectively.
Handiling Exceptions
Selected Exception
>>> try:
... x = "hello"
... y = sum(x)
... except TypeError:
... print("Operation invalid")
Operation invalid
Multiple Handlers Expcetions
>>> try:
... x = "hello"
... z = x / 2
... y = sum(x)
... except (ValueError, TypeError, NameError):
... print("Operation invalid")
... print()
Operation invalid
>>> import sys
>>> try:
... f = open('myfile.txt')
... s = f.readline()
... i = int(s.strip())
... except OSError as err:
... print("OS error: {0}".format(err))
... except ValueError:
... print("Could not convert data to an integer.")
... except BaseException as err:
... print(f"Unexpected {err=}, {type(err)=}")
... raise
OS error: [Errno 2] No such file or directory: 'myfile.txt'
BaseExceptions as a Wildcard
By virtue of all exceptions inheriting from BaseException, it is possible to utilize the "except" statement to capture any exception.
>>> import sys
>>> try:
... f = open('myfile.txt')
... s = f.readline()
... i = int(s.strip())
... except BaseException as err:
... print(f"Unexpected {err=}, {type(err)=}")
Unexpected err=FileNotFoundError(2, 'No such file or directory'), type(err)=<class 'FileNotFoundError'>
Exception Properties Analyses
>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print(type(inst))
... print(inst.args)
... x, y = inst.args
... print('x =', x)
... print('y =', y)
<class 'Exception'>
('spam', 'eggs')
x = spam
y = eggs
Raise Exceptions
Exceptions Chaining
The raise statement in Python enables the option of chaining exceptions, facilitating error handling and flow control within the code.
>>> def func():
... raise ConnectionError
>>> try:
... func()
... except ConnectionError as exc:
... raise RuntimeError('Failed to open database') from exc
Traceback (most recent call last):
File "<code block: n15; title exc_chaning_result.py>", line 4, in <module>
File "<code block: n15; title exc_chaning_result.py>", line 2, in func
ConnectionError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/gabriel/Projects/mastering-python/venv/lib/python3.11/site-packages/markdown_exec/formatters/python.py", line 59, in _run_python
exec(compiled, exec_globals) # noqa: S102
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<code block: n15; title exc_chaning_result.py>", line 6, in <module>
raise RuntimeError('Failed to open database') from exc
RuntimeError: Failed to open database
User-definied Exceptions
When creating user-defined exceptions, it is essential to ensure they are derived either directly or indirectly from the built-in Exception class. Conventionally, these custom exceptions are named with endings such as "Error," following a clear and descriptive naming pattern for enhanced code readability.
from dataclasses import dataclass, field
@dataclass
class DatabaseConnectionError(Exception):
message: str = field(init=False, default="Couldn't connect to database")