一个函数如何将装饰器附加到函数内部

时间:2019-01-25 23:40:20

标签: python decorator

我有两个装饰器作为超时和重试,并且我有两个函数,其中一个具有超时,另一个具有重试,如下所示:

@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进行重试。

0 个答案:

没有答案