Трохи декоративної обробки (дуже вільно натхненна монадою "Можливо" та "ліфтинг". Ви можете безпечно видаляти анотації типу Python 3.6 та використовувати старіший стиль форматування повідомлень.
fallible.py
from functools import wraps
from typing import Callable, TypeVar, Optional
import logging
A = TypeVar('A')
def fallible(*exceptions, logger=None) \
-> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
"""
:param exceptions: a list of exceptions to catch
:param logger: pass a custom logger; None means the default logger,
False disables logging altogether.
"""
def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:
@wraps(f)
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except exceptions:
message = f'called {f} with *args={args} and **kwargs={kwargs}'
if logger:
logger.exception(message)
if logger is None:
logging.exception(message)
return None
return wrapped
return fwrap
Демонстрація:
In [1] from fallible import fallible
In [2]: @fallible(ArithmeticError)
...: def div(a, b):
...: return a / b
...:
...:
In [3]: div(1, 2)
Out[3]: 0.5
In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
File "/Users/user/fallible.py", line 17, in wrapped
return f(*args, **kwargs)
File "<ipython-input-17-e056bd886b5c>", line 3, in div
return a / b
In [5]: repr(res)
'None'
Ви також можете змінити це рішення, щоб повернути щось трохи більш значуще, ніж None
із except
частини (або навіть зробити рішення загальним, вказавши це повернене значення в fallible
аргументах s).
exception
Метод просто викликаєerror(message, exc_info=1)
. Як тільки ви перейдетеexc_info
до будь-якого з методів реєстрації даних із контексту винятку, ви отримаєте прослідкування.