-
-
Notifications
You must be signed in to change notification settings - Fork 285
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
chore: Allow combining non base-derived objects for retries #485
base: main
Are you sure you want to change the base?
Conversation
tenacity/asyncio/retry.py
Outdated
@@ -104,7 +104,7 @@ def __init__(self, *retries: typing.Union[retry_base, async_retry_base]) -> None | |||
async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override] | |||
result = False | |||
for r in self.retries: | |||
result = result or await _utils.wrap_to_async_func(r)(retry_state) | |||
result = result or (await _utils.wrap_to_async_func(r)(retry_state) is True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you added is True
everywhere. This seems like a different change not required. Are we sure we need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While adding the tests I noticed that, if a wrong combination of strategies is added (combination of async strategies in a sync context), then we'd end up trying to and
/or
a coroutine, which hangs forever as it's never going to be resolved in the sync context. By adding the is True
we directly check if the result is exactly that and avoid endlessly waiting for the coroutine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, thinking about it a bit more... Maybe we'd rather let it hang? Not sure what's best they, without the changes it hangs forever, which is not very intuitive, and with these changes it finishes without failures but it does not run the strategy at all, giving an invalid outcome 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it feasible to trap this invalid call pattern and raise an exception to make it obvious that there's a programming error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I came up with this, it is more coupled to the actual implementations, but it now fails on invalid scenarios so it's more explicit. I used a similar check to the one already existing in __init__.py
, let me know what you think @jd f8725d6
Fixes #481
Versions prior to introducing #451 inadvertently allowed to use plain callables as retry strategies, as opposed to
retry_base
deriving classes, as they conform to the__call__
typing. It is not a big must, but slightly changing the__and__
/__or__
checks would allow for that again.NOTE: combining non-async strategies with async functions will not work, other combinations should, as the tests show.