backoff icon indicating copy to clipboard operation
backoff copied to clipboard

Improve Typing and Error Handling in `on_exception`

Open davidslater opened this issue 2 years ago • 0 comments

Currently, on_exception takes an argument exception: _MaybeSequence[Type[Exception]].

However, this cannot handle any Sequence of Type[Exception]. For instance, if a list[Type[Exception]] is given, as in the following code:

import backoff

i: int = 0
@backoff.on_exception(backoff.expo, [ValueError])
def _retry():
    global i
    i += 1
    print(f"i = {i}")
    if i < 3:
        raise ValueError(f"failure {i}")
    if i == 3:
        raise RuntimeError("runtime error")

    return "success"

_retry()

then a hard-to-understand stack trace is generated:

i = 1
Traceback (most recent call last):
  File "/Users/davidslater/git/litl/backoff/backoff/_sync.py", line 107, in retry
    ret = target(*args, **kwargs)
  File "/Users/davidslater/git/litl/backoff/script.py", line 10, in _retry
    raise ValueError(f"failure {i}")
ValueError: failure 1

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/davidslater/git/litl/backoff/script.py", line 16, in <module>
    _retry()
  File "/Users/davidslater/git/litl/backoff/backoff/_sync.py", line 108, in retry
    except exception as e:
TypeError: catching classes that do not inherit from BaseException is not allowed

Similar issues can also arise with the same traceback, such as:

@backoff.on_exception(backoff.expo, ValueError())

which are hard to understand and debug, as there are no lines between the function call in user code and the except exception as e in the library.

I see two major different ways to improve this: A) modify the type information. If done directly, this would change the type to Union[Type[Exception], Tuple[Type[Exception]]], Alternatively, you could add to _typing.py:

_MaybeTuple = Union[T, Tuple[T]]

and then change the type to _MaybeTuple[Type[Exception]].

B) Do checking of exception when it is given to the function to improve error response. Essentially, just check if it is an Exception or a tuple of exception types and raise a ValueError if not.

davidslater avatar Feb 09 '23 22:02 davidslater