tenacity icon indicating copy to clipboard operation
tenacity copied to clipboard

Add optional label to BaseRetrying class

Open aragaer opened this issue 2 years ago • 5 comments

If label is set it will be used instead of a function name in before_sleep_log.

I didn't find any appropriate place to add a test, so I simply wrote a small script to check that it all works:

#!/usr/bin/env python
import logging
import sys

from tenacity import (
    before_sleep_log,
    Retrying,
    stop_after_attempt,
    wait_fixed,
)

logger = logging.getLogger('MY LOGGER')
logging.basicConfig(stream=sys.stdout, level=logging.INFO)

for attempt in Retrying(
        label="MY LABEL",
        stop=stop_after_attempt(5),
        before_sleep=before_sleep_log(logger, logging.INFO),
        wait=wait_fixed(1)):
    with attempt:
        raise Exception("yo")

aragaer avatar Sep 26 '23 16:09 aragaer

⚠️ No release notes detected. Please make sure to use reno to add a changelog entry.

mergify[bot] avatar Sep 26 '23 16:09 mergify[bot]

Current code assumes that there is always a function name available (there even was a comment saying that this should never happen) -- which is not the case when using a context manager. Currently logging functions would output "Retrying <unknown>" and there is no way to specify which section of code failed and needs the retry. The idea is to specify an optional label for a retry block. And if retry block is repeated in a loop it also allows to specify which iteration caused a retry:

for item in some_iterable:
    try:
        for attempt in Retrying(
                label=f"doing stuff with {item}",
                stop=stop_after_attempt(5),
                before_sleep=before_sleep_log(logger, logging.DEBUG),
                wait=wait_fixed(10)):
            with attempt:
                data = do_stuff(item)
    except RetryError:
        continue
    ...

aragaer avatar Sep 27 '23 10:09 aragaer

This code snippet makes me think this should just be an argument to before_sleep to allow changing the string passed to logger.log.

jd avatar Sep 27 '23 10:09 jd

So that the snippet would become like this?

for item in some_iterable:
    try:
        for attempt in Retrying(
                stop=stop_after_attempt(5),
                before_sleep=before_sleep_log(logger, logging.DEBUG, label=f"doing stuff with {item}"),
                wait=wait_fixed(10)):
            with attempt:
                data = do_stuff(item)
    except RetryError:
        continue
    ...

However fn_name is also used in before_log and after_log. Having a single label shared between them at BaseRetryObject makes a bit more sense than having a separate label for each function.

aragaer avatar Sep 27 '23 11:09 aragaer

I've moved the "get label" code to the RetryCallState class and modified all the log functions to use it.

aragaer avatar Sep 28 '23 11:09 aragaer