How can a function grab decorator attached to the function inside

三世轮回 提交于 2019-12-11 17:16:33

问题


I have two decorators as timeout and retry, and I have two functions, one of them has timeout and the other one has retry, like this:

@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)

The thing is that when func_inner throw timeout error because of timeout decorator, I want func() to know it has an attr as timeout, and we do retry for this error and show the error message we defined 'Failed command after...', this is retry decorator and please see bold line:

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

But above code, with this:

if hasattr(fn, 'timeout') :
                    _ignore_exceptions.append(TimeoutError)

I want to add TimeoutError in ignore_exceptions, if the function has timeout attr, it will have if it wrapped in timeout, but now func_inner uses @timeout instead of func, the _ignore_exceptions would never include TimeoutError, how can func know that it utilize a function func_inner using timeout decorator and I want TimeourError added in ignore_exceptions for retry.

来源:https://stackoverflow.com/questions/54374233/how-can-a-function-grab-decorator-attached-to-the-function-inside

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!