Sedikit perawatan dekorator (sangat longgar terinspirasi oleh Mungkin monad dan mengangkat). Anda dapat menghapus anotasi tipe Python 3.6 dengan aman dan menggunakan gaya pemformatan pesan yang lebih lama.
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'
Anda juga dapat memodifikasi solusi ini untuk mengembalikan sesuatu yang sedikit lebih bermakna daripada None
dari except
bagian (atau bahkan membuat solusi generik, dengan menentukan nilai pengembalian ini dalam fallible
argumennya).
exception
Metode hanya panggilanerror(message, exc_info=1)
. Segera setelah Anda melewatiexc_info
salah satu metode logging dari konteks pengecualian, Anda akan mendapatkan traceback.