我有两个装饰器作为超时和重试,并且我有两个函数,其中一个具有超时,另一个具有重试,如下所示:
@timeout(seconds=1)
def func_inner(timeout):
time.sleep(timeout)
@retry(count=2, message="Failed command after {timeout} seconds")
def func(timeout):
func_inner(timeout)
func(timeout=3)
问题是,当func_inner
由于超时装饰器而引发超时错误时,我希望func()
知道其attr为timeout
,我们将重试此错误并显示错误消息,我们定义了“ ...之后失败的命令”,这是重试修饰符,请参见粗体行:
def retry(count=1, delay=None, expected_value=None, raise_exception=True,
ignore_exceptions=[], message=None):
def _get_param(kargs, name, default):
return kargs.pop('retry_' + name, default)
def decorator(fn):
def wrapper(*args, **kargs):
'''
Retry settings can be overridden on function call
@retry(count=2)
def fn(param):
return param
fn(123, retry_count=10, retry_delay=10)
'''
_count = _get_param(kargs, 'count', count)
_delay = _get_param(kargs, 'delay', delay)
_expected_value = _get_param(kargs, 'expected_value', expected_value)
_raise_exception = _get_param(kargs, 'raise_exception', raise_exception)
_ignore_exceptions = _get_param(kargs, 'ignore_exceptions', ignore_exceptions)
_message = _get_param(kargs, 'message', message)
# convert single item to list
_ignore_exceptions = \
_ignore_exceptions \
if isinstance(_ignore_exceptions, list) \
else [_ignore_exceptions]
logging.debug('bytecode:')
logging.debug(list(get_instructions(fn)))
# If function is wrapped into @timeout
# then ignore TimeoutError exception implicitly
if hasattr(fn, 'timeout') :
_ignore_exceptions.append(TimeoutError)
# Retry parameters can be used for recursive functions
# see autotest.sertver.utils.http_list
wrapper.retry_params = dict(
retry_count=_count,
retry_delay=_delay,
retry_expected_value=_expected_value,
retry_raise_exception=_raise_exception,
retry_message=_message,
)
log_params = dict(
wrapper.retry_params,
**convert_func_arguments_to_keyword(fn, *args, **kargs))
fn_expected = _expected_value \
if isinstance(_expected_value, types.FunctionType) \
else None
_message = _message + ' ' if _message else ''
message_unexpected_value = upperfirst(
_message +
("got unexpected value <{retry_actual_value}>" if fn_expected else
"expected value <{retry_expected_value}> but was <{retry_actual_value}>")
)
message_exception = upperfirst(_message + "got error: {exception_type} - {retry_error}")
actual_value = None
last_exception = None
index = 1
while (index <= _count):
try:
actual_value = fn(*args, **kargs)
if _expected_value is None or \
(fn_expected(actual_value) if fn_expected else actual_value == _expected_value):
return actual_value
# Don't log single try
if _message and _count > 1:
print "[%s/%s] %s" % (
index, _count,
message_unexpected_value.format(
retry_actual_value=actual_value,
** log_params)
)
except Exception as e:
# Reset previous actual_value in case of exception
actual_value = None
last_exception = e
for ex in _ignore_exceptions:
if (isinstance(ex, types.TypeType) and isinstance_or_cause(e, ex)) \
or (isinstance(ex, types.FunctionType) and ex(e)):
if _message:
print "[%s/%s] %s" % (
index, _count,
message_exception.format(
exception_type=type(e).__name__,
retry_error=e,
** log_params)
)
break
else:
raise e
index += 1
if _delay:
time.sleep(_delay)
if last_exception:
raise RetryError(
message_exception.format(
exception_type=type(last_exception).__name__,
retry_error=last_exception,
**log_params))
if _raise_exception:
raise RetryError(
message_unexpected_value.format(
retry_actual_value=actual_value,
**log_params))
return actual_value
return wrapper
return decorator
但是上面的代码是这样的:
if hasattr(fn, 'timeout') :
_ignore_exceptions.append(TimeoutError)
我想在ignore_exceptions中添加TimeoutError,如果函数具有超时属性,则将其包装为超时,但是现在func_inner使用@timeout而不是func,_ignore_exceptions
将永远不会包含{{1} },TimeoutError
怎么知道它利用超时装饰器利用函数func
,并且我想在{_1}}中添加ignore_exceptions进行重试。