Я пишу ETL-фреймворк в Python 3.7, который использует функции в качестве «задач» со специальным декоратором.Большинство из этих задач выполняется в цикле.Если что-то вызывает исключение в цикле, я хочу, чтобы функция обрабатывала это исключение, записывая данные об ошибке и продолжая цикл.
Это упрощенный пример того, что у меня пока есть:
class TaskExecutionError(RuntimeError):
def __init__(self, msg="", context={}):
self.msg = msg
self.context = context
def __str__(self):
return self.msg or "Error executing task."
def task(fn):
@wraps(fn)
def _wrapper(*args, **kwargs):
start_ts = datetime.utcnow()
try:
return fn(*args, **kwargs)
except TaskExecutionError as e:
logger.exception(f"Task execution error will be logged: {e}.")
fail_data = {
"task_name": fn.__name__,
"args": list(args),
"kwargs": kwargs,
"context": e.context,
"fail_message": str(e),
"fail_time": str(datetime.utcnow()),
# etc.
}
)
# Write failure data in an object store
finally:
end_ts = datetime.utcnow()
logger.info(f"*** Wallclock: {end_ts - start_ts}.")
_wrapper.is_task = True
return _wrapper
@task
def test_fail_log(a, b, c, kwa=1, kwb=2):
"""
Test handling failures.
"""
for i in range(10):
if i % 3:
raise TaskExecutionError(context={"i": i})
else:
print("All's well")
Это работает хорошо, поскольку я вижу, что сообщение печатается и сохраняется, однако, конечно, выполнение прерывается, как толькокак первое исключение поднято.
Как мне решить эту проблему, чтобы казнь продолжалась?
Кажется, я не могу использовать очень удобный механизм исключений, и мне, вероятно, придется разработать пользовательскую функцию handle_failure()
или около того.Но я не уверен, как лучше передать контекст декоратора функции в функцию handle failure()
, пока я вызываю ее из декорированной функции.
Поскольку я собираюсь использовать этот механизм в нескольких @task
декорированных функциях, я хотел бы сделать легкий вызов, если это возможно, без большого количества аргументов.
Спасибо за любые ваши предложения.