Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Exception Groups for RetryException #573

Open
daniel-sanche opened this issue Dec 13, 2023 · 0 comments
Open

Support Exception Groups for RetryException #573

daniel-sanche opened this issue Dec 13, 2023 · 0 comments
Labels
priority: p2 Moderately-important priority. Fix may not be included in next release. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@daniel-sanche
Copy link
Contributor

daniel-sanche commented Dec 13, 2023

When a retry operation fails, it emits a RetryException, with the cause field populated to give context on the latest failure in the chain

While showing the latest failure is helpful, we lose the context on all the other errors that occurred before the final terminal failure

Python 3.11+ has a new ExceptionGroup type, which gives a helpful traceback of a group of exceptions that should be raised together. We should use this for RetryErrors where supported

Rough code I had in mind:

# In exceptions.py
class RetryErrorGroup(RetryError, ExceptionGroup):
  pass

# In retry_base.py
def build_retry_error(
    exc_list: list[Exception],
    reason: RetryFailureReason,
    timeout_val: float | None,
    **kwargs: Any,
) -> tuple[Exception, Exception | None]:
    if reason == RetryFailureReason.TIMEOUT:
        # return RetryError with the most recent exception as the cause
        src_exc = exc_list[-1] if exc_list else None
        timeout_val_str = f"of {timeout_val:0.1f}s " if timeout_val is not None else ""
        if sys.version < ("3", "11"):
            return (
                exceptions.RetryError(
                     f"Timeout {timeout_val_str}exceeded",
                     src_exc,
                 ),
               src_exc,
            )
        else:
             return exceptions.RetryErrorGroup(
                 f"Timeout {timeout_val_str}exceeded", 
                 exc_list
             )
    elif exc_list:
         if sys.version < ("3", "11"):
              # return most recent exception encountered
              return exc_list[-1], None
          else:
              # raise most recent exception, chained with transient errors
              return exc_list[-1], RetryErrorGroup("Transient errors", exc_list[:-1]
    else:
        # no exceptions were given in exc_list. Raise generic RetryError
        return exceptions.RetryError("Unknown error", None), None

--

This might also be a good chance to re-think RetryError a bit. Maybe make a subclass called OperationTimeout, since that's a more accurate explanation of the error. Include the timeout value itself. Maybe make a separate RetryExhausted subclass if we implement a max_attempts option as well

@ohmayr ohmayr added type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. priority: p2 Moderately-important priority. Fix may not be included in next release. labels Dec 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority: p2 Moderately-important priority. Fix may not be included in next release. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests

2 participants