Ваш декоратор является декоратором фабрики , который возвращает другого декоратора. Здесь вам не нужна фабрика, удалите один слой:
def continual_retry(func):
@wraps(func)
def func_wrapper(*args, **kwargs):
while True:
try:
return func(*args, **kwargs)
except Exception as e:
print(f'Could not perform function {func.__name__}')
print(f' with args {repr(args)}')
print(f' due to error {e.class__}')
redo = input('Retry (y/n)? ')
if redo.lower() != 'y':
print('Exiting program due to error and user input')
sys.exit(0)
return func_wrapper
Вам также необходимо вернуть результат функции , и я изменил цикл while
на бесконечный цикл с while True:
, так как успешный return
выйдет из цикла. Я также обновил вызов func()
для передачи аргументов ключевых слов (return func(*args, **kwargs)
).
Когда Python встречает @continual_retry
, он передает объект функции в continual_retry()
, вызываемую для замены функции результатом, как если бы вы ожидали divide = continual_retry(divide)
, но в вашей версии continual_retry(divide)
возвращает retry_decorated()
Функция, которая сама по себе при вызове, наконец, возвращает объект func_wrapper
. Вы хотите использовать func_wrapper
для замены.
Ваш двухслойный подход хорош, если вы хотите настроить декоратор, где функция фабрики внешнего декоратора принимает аргументы, отличные от функции. Цель состоит в том, чтобы затем использовать это как @continual_retry(config_arg1, config_arg2, ...)
, чтобы Python сначала вызывал эту функцию для получения возвращаемого значения, а затем оформление выполнялось путем вызова этого возвращаемого значения.
Вы можете, например, добавить опцию для ограничения количества повторных попыток:
def continual_retry(limit=None):
def retry_decorator(func):
@wraps(func)
def func_wrapper(*args, **kwargs):
retries = 0
while True
try:
return func(*args, **kwargs)
except Exception as e:
print(f'Could not perform function {func.__name__}')
print(f' with args {repr(args)}')
print(f' due to error {e.class__}')
redo = input('Retry (y/n)? ')
if redo.lower() != 'y':
print('Exiting program due to error and user input')
sys.exit(0)
retries += 1
if limit is not None and retries > limit:
# reached the limit, re-raise the exception
raise
return func_wrapper
return retry_decorator
Теперь вы должны использовать @continual_retry()
или @continual_retry(<integer>)
при оформлении, например ::
@continual_retry(3)
def divide(a, b):
return a / b
, поскольку continual_retry()
создает декоратор, а continual_retry(3)(divide)
создает оболочку, заменяющую исходную функцию.