jd / tenacity

Retrying library for Python
http://tenacity.readthedocs.io
Apache License 2.0
6.82k stars 283 forks source link

Retry in recursive context is broken since 8.3.0 #478

Closed sashaCher closed 5 months ago

sashaCher commented 5 months ago

Hello We observed breaking change in @retry decorator behavior after upgrading the library from 8.2.3 to 8.3.0/8.4.1 version.

Assuming the next code sample (simplified and adjusted to illustrate the exact use-case):

from tenacity import RetryCallState, retry

MAX_RETRY_FIX_ATTEMPTS = 2

def do_retry(retry_state: RetryCallState):
    ex = retry_state.outcome.exception()
    _subject_: str = retry_state.args[0]

    if _subject_ == 'Fix':  # no retry on fix failure
        return False

    if retry_state.attempt_number >= MAX_RETRY_FIX_ATTEMPTS:
        return False

    if ex:
        do_fix_work()
        return True

    return False

@retry(reraise=True, retry=do_retry)
def _do_work(subject: str):
    if subject == 'Error':
        print(f'{subject} is not working')
        raise Exception(f'{subject} is not working')
    print(f'{subject} is working')

def do_any_work(subject: str):
    _do_work(subject)

def do_fix_work():
    _do_work('Fix')

if __name__ == '__main__':
    do_any_work('Error')

In version 8.2.3 it worked correctly means do_any_work function was called twice with do_fix_work in the middle and produced the next output:

Error is not working
Fix is working
Error is not working
Traceback (most recent call last):
...call-stack...
Exception: Error is not working

After upgrading to 8.3.0 or 8.4.1 the code is not working the same way. There's no additional call of do_any_work after do_fix_work and the code does not end with error:

Error is not working
Fix is working

Process finished with exit code 0
jd commented 5 months ago

Thanks for reporting! I don't have time to dig into this now, but, cc @hasier in case that might be one of your recent PRs.

hasier commented 5 months ago

Just opened a fix in #479, looks like we were overwriting a local context when recursively calling a decorated function, sorry about that! @jd let me know if that looks ok to you :)

sashaCher commented 5 months ago

Thank you for fast resolution!!!