Un po 'di trattamento per decoratori (ispirato in modo molto approssimativo alla monade e al lifting forse). Puoi rimuovere in sicurezza le annotazioni di tipo Python 3.6 e utilizzare uno stile di formattazione dei messaggi meno recente.
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
demo:
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'
Puoi anche modificare questa soluzione per restituire qualcosa di più significativo di None
alla except
parte (o persino rendere la soluzione generica, specificando questo valore di ritorno negli fallible
argomenti di).
exception
metodo chiama semplicementeerror(message, exc_info=1)
. Non appena si passaexc_info
a uno dei metodi di registrazione da un contesto di eccezione, si ottiene un traceback.