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

chore: Allow combining non base-derived objects for retries #485

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

hasier
Copy link
Contributor

@hasier hasier commented Jul 4, 2024

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.

@@ -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)
Copy link
Owner

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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 😕

Copy link
Contributor

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?

Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8.4.2 breaks ability to compose regular functions with retry_base
3 participants