88 lines
3.9 KiB
Markdown
88 lines
3.9 KiB
Markdown
---
|
|
title: "Two strategies for handling exceptions in Python"
|
|
slug: /two-strategies-for-handling-exceptions-in-python/
|
|
date: 2023-10-03
|
|
tags: ["learning", "python"]
|
|
---
|
|
|
|
The following function illustrates two strategies for handling exceptions in
|
|
Python:
|
|
|
|
```py
|
|
def parse_articles(articles: Dict[str, Any]) -> List[List]:
|
|
|
|
if not articles:
|
|
raise ValueError("No articles to parse")
|
|
|
|
articles_list = []
|
|
|
|
for article in articles.values():
|
|
try:
|
|
time_added, given_title, resolved_url = (
|
|
article["time_added"],
|
|
article["given_title"],
|
|
article["resolved_url"],
|
|
)
|
|
except KeyError as e:
|
|
logging.warning(
|
|
f"Article missing {e} property. Skipping article: {given_title}."
|
|
)
|
|
continue
|
|
|
|
articles_list.append([time_added, given_title, resolved_url])
|
|
|
|
return articles_list
|
|
```
|
|
|
|
The function is a basic transformer: it receives a dictionary and loops through
|
|
a subset of its properties which are also dictionaries, returning selected keys
|
|
from each as a multidimensional array.
|
|
|
|
The first exception handler checks whether the `articles` dictionary is
|
|
populated or `None`. If either are the case, the function exits and returns a
|
|
`ValueError`. This approach _propagates_ the error and is deliberately and
|
|
transparently obstructive: the function exits before it can execute the main
|
|
code. If this exception is not adequately handled by the caller, it will cause a
|
|
runtime error.
|
|
|
|
The second instance of exception handling occurs in the loop. If any of the
|
|
three specified properties (`time_added`, `given_title`, `resolved_url`) are
|
|
absent from the `articles` dictionary, a `KeyError` exception will be thrown.
|
|
The outcome is quite different from the first exception however. The function
|
|
will not exit immediately, it simply won't append this set of properties to the
|
|
list, and will move on to the next iteration of the loop.
|
|
|
|
This behaviour, known as _graceful degradation_ is afforded by the _try, except_
|
|
scaffolding. It literally allows us to `try` an execution before an exception
|
|
can be raised. And if an exception is raised, we can augment the code with a
|
|
`finally` block to return something regardless of the exception, such as an
|
|
empty list.
|
|
|
|
In the scenario above, `finally` isn't used since the `continue` ensures that
|
|
the execution proceeds to the next iteration of the loop. In addition, even if
|
|
none of the `articles` contain the requisite keys, an empty list will be still
|
|
be returned.
|
|
|
|
Both strategies, propagation and graceful degradation, have their merits.
|
|
|
|
Propagation is useful because it identifies the error immediately and blocks the
|
|
code execution, meaning that failures do not occur silently or go unchecked,
|
|
forcing the caller to implement a handling routine. This is particulaly useful
|
|
for genuine exceptions - cases that _should not_ occur in regular execution. If
|
|
they occur, they are so severe that they should be addressed immediately, not
|
|
deferred. One such scenario might be a memory-intensive script or lambda running
|
|
in a severless cloud provider: you would not want it to waste compute time on a
|
|
process that is not going to return the expected value.
|
|
|
|
The obvious benefit of graceful degradation is that it doesn't halt the program
|
|
straight away. This is most suitable for cases where the exception in question
|
|
is fairly common. In the context of the code example this would be not returning
|
|
articles that lack the requisite keys. Providing most of the articles are
|
|
parsable, the fact that some are skipped is unlikely to be a terminal fault in
|
|
the application.
|
|
|
|
Finally, the two methods differ in the amount of work required of the developer.
|
|
Propagation requires explicit error handling from the function or method that
|
|
receives the error. In contrast, graceful degradation allows for silent
|
|
failure - the error is not immediately apparent and may only be detected in logs
|
|
at a later stage.
|