Un peu de traitement de décoration (très lâchement inspiré par la monade peut-être et le levage). Vous pouvez supprimer en toute sécurité les annotations de type Python 3.6 et utiliser un style de formatage de message plus ancien.
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
Démo:
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'
Vous pouvez également modifier cette solution pour renvoyer quelque chose d'un peu plus significatif que None
de la except
partie (ou même rendre la solution générique, en spécifiant cette valeur de retour dans fallible
les arguments de).
exception
méthode appelle simplementerror(message, exc_info=1)
. Dès que vous passezexc_info
à l'une des méthodes de journalisation à partir d'un contexte d'exception, vous obtiendrez une traceback.