Skip to content

Commit

Permalink
add exceptiongroup support to retry_if_exception
Browse files Browse the repository at this point in the history
  • Loading branch information
jakkdl committed Oct 31, 2024
1 parent 11af5c1 commit 04ea6f4
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 1 deletion.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ classifier =

[options]
install_requires =
exceptiongroup; python_version < "3.11"
python_requires = >=3.8
packages = find:

Expand Down
6 changes: 6 additions & 0 deletions tenacity/retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@

import abc
import re
import sys
import typing

if typing.TYPE_CHECKING:
from tenacity import RetryCallState

if sys.version_info < (3, 11):
from exceptiongroup import BaseExceptionGroup

class retry_base(abc.ABC):
"""Abstract base class for retry strategies."""
Expand Down Expand Up @@ -79,6 +82,9 @@ def __call__(self, retry_state: "RetryCallState") -> bool:
exception = retry_state.outcome.exception()
if exception is None:
raise RuntimeError("outcome failed but the exception is None")
if isinstance(exception, BaseExceptionGroup):
# look for any exceptions not matching the predicate
return exception.split(self.predicate)[1] is None
return self.predicate(exception)
else:
return False
Expand Down
46 changes: 46 additions & 0 deletions tests/test_tenacity.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
import tenacity
from tenacity import RetryCallState, RetryError, Retrying, retry

if sys.version_info < (3, 11):
from exceptiongroup import ExceptionGroup

_unset = object()


Expand Down Expand Up @@ -733,6 +736,24 @@ def go(self):
return True


class NoExceptionGroupAfterCount:
def __init__(self, count: int, exceptions: tuple[Exception]):
self.counter = 0
self.count = count
self.exceptions = exceptions

def go(self):
"""Raise an ExceptionGroup until after count threshold has been crossed.
Then return True.
"""
if self.counter < self.count:
self.counter += 1
raise ExceptionGroup("tenacity test group", self.exceptions)

return True


class NoNameErrorAfterCount:
"""Holds counter state for invoking a method several times in a row."""

Expand Down Expand Up @@ -1014,6 +1035,7 @@ def _retryable_test_with_exception_type_custom(thing):
retry=tenacity.retry_if_exception_type(CustomError),
)
def _retryable_test_with_exception_type_custom_attempt_limit(thing):
# this is not used??
return thing.go()


Expand Down Expand Up @@ -1064,6 +1086,30 @@ def test_retry_if_exception_of_type(self):
self.assertTrue(isinstance(n, NameError))
print(n)

def test_retry_if_exception_of_type_exceptiongroup(self):
self.assertTrue(
_retryable_test_with_exception_type_io(
NoExceptionGroupAfterCount(5, exceptions=(IOError(),))
)
)
with pytest.raises(ExceptionGroup):
self.assertTrue(
_retryable_test_with_exception_type_io(
NoExceptionGroupAfterCount(5, exceptions=(IOError(),ValueError()))
)
)
# not supported
with pytest.raises(ExceptionGroup):
e = IOError()
e.__cause__ = NameError()
self.assertTrue(
_retryable_test_with_exception_cause_type(
NoExceptionGroupAfterCount(5, exceptions=(e,))
)
)



def test_retry_except_exception_of_type(self):
self.assertTrue(
_retryable_test_if_not_exception_type_io(NoNameErrorAfterCount(5))
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py3{8,9,10,11,12,12-trio}, pep8, pypy3
envlist = py3{8,9,10,11,12,13,13-trio}, pep8, pypy3
skip_missing_interpreters = True

[testenv]
Expand Down

0 comments on commit 04ea6f4

Please sign in to comment.