-
-
Notifications
You must be signed in to change notification settings - Fork 303
Open
Description
If a user decorates a function with @retry(inst) for a retry_base instance inst for any of the builtin strategies as a positional argument—an easy misspelling of @retry(retry=inst))—tenacity will hang indefinitely as the resulting decoration attempts to call the strategy with the function as an argument, which will generally be an error.
Below is a program that recreates this failure for all builtin strategies.
from multiprocessing import Pool, Process
import sys
from tenacity import retry
from tenacity.retry import *
def make_class(strat):
"""
This sample will hang indefinitely while defining the class Bar
retry() thinks the strategy is what I want to retry,the unrolled
equivalent is:
def retryable(self, param):
pass
retryable = retry(strat)(retryable)
so the thing being retried is strat(retryable) which is very unlikely
to be a valid call
"""
class Bar:
print("Defining Bar...")
@retry(strat) # oops! I forgot to type retry=
def retryable(self, param):
pass
print("Done defining Bar") # we never get here
return Bar
def make_func(strat): [1/1317]
"""
This sample will hang indefinitely while defining the function task()
"""
@retry(strat) # oops! I forgot to type retry=
def task(x):
print("Running task") # will never print
pass
return task
strats = {
"retry_if_exception": retry_if_exception(lambda e: True),
"retry_if_exception_type": retry_if_exception_type(TimeoutError),
"retry_unless_exception_type": retry_unless_exception_type(RuntimeError),
"retry_if_result": retry_if_result(lambda e: True),
"retry_if_not_result": retry_if_not_result(lambda e: False),
"retry_if_exception_message": retry_if_exception_message(match=".*"),
"retry_if_not_exception_message": retry_if_not_exception_message(match=".*"),
"retry_any": retry_any(retry_if_result(lambda x: lambda y: True),),
"retry_all": retry_all(retry_if_result(lambda x: lambda y: True),),
}
TIMEOUT = 5
if __name__ == "__main__":
for name,strat in strats.items():
for f in (make_class, make_func):
proc = Process(target=f, args=(strat,))
print(f"Calling {f.__name__} with arg {name} with a timeout of {TIMEOUT} seconds")
proc.start()
proc.join(TIMEOUT) # give the process a few seconds
if proc.exitcode is None:
print(f"*** Process for {f.__name__}({name}) is hung, terminating ***\n")
proc.terminate()
else:
print(f"Process terminated with exit code {proc.exitcode}")Output:
Calling make_class with arg retry_if_exception with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_if_exception) is hung, terminating ***
Calling make_func with arg retry_if_exception with a timeout of 5 seconds
*** Process for make_func(retry_if_exception) is hung, terminating ***
Calling make_class with arg retry_if_exception_type with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_if_exception_type) is hung, terminating ***
Calling make_func with arg retry_if_exception_type with a timeout of 5 seconds
*** Process for make_func(retry_if_exception_type) is hung, terminating ***
Calling make_class with arg retry_unless_exception_type with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_unless_exception_type) is hung, terminating ***
Calling make_func with arg retry_unless_exception_type with a timeout of 5 seconds
*** Process for make_func(retry_unless_exception_type) is hung, terminating ***
Calling make_class with arg retry_if_result with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_if_result) is hung, terminating ***
Calling make_func with arg retry_if_result with a timeout of 5 seconds
*** Process for make_func(retry_if_result) is hung, terminating ***
Calling make_class with arg retry_if_not_result with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_if_not_result) is hung, terminating ***
Calling make_func with arg retry_if_not_result with a timeout of 5 seconds
*** Process for make_func(retry_if_not_result) is hung, terminating ***
Calling make_class with arg retry_if_exception_message with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_if_exception_message) is hung, terminating ***
Calling make_func with arg retry_if_exception_message with a timeout of 5 seconds
*** Process for make_func(retry_if_exception_message) is hung, terminating ***
Calling make_class with arg retry_if_not_exception_message with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_if_not_exception_message) is hung, terminating ***
Calling make_func with arg retry_if_not_exception_message with a timeout of 5 seconds
*** Process for make_func(retry_if_not_exception_message) is hung, terminating ***
Calling make_class with arg retry_any with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_any) is hung, terminating ***
Calling make_func with arg retry_any with a timeout of 5 seconds
*** Process for make_func(retry_any) is hung, terminating ***
Calling make_class with arg retry_all with a timeout of 5 seconds
Defining Bar...
*** Process for make_class(retry_all) is hung, terminating ***
Calling make_func with arg retry_all with a timeout of 5 seconds
*** Process for make_func(retry_all) is hung, terminating ***
Metadata
Metadata
Assignees
Labels
No labels