-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathflask_reportable_error.py
More file actions
141 lines (102 loc) · 3.4 KB
/
flask_reportable_error.py
File metadata and controls
141 lines (102 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# coding: UTF-8
# @copyright ©2013, Rodrigo Cacilhας <batalema@cacilhas.info>
# Cesar Barros <cesarb@cesarb.eti.br>
import sys
from functools import wraps
from warnings import warn
from flask import render_template
__all__ = ['init', 'ReportableErrorMixin', 'reportable']
def init(app):
config.update(app)
config.template = config.settings.get('TEMPLATE')
config.headers = config.settings.get('HEADERS')
def add_mixins(*mixins):
warn('use @mixin', DeprecationWarning, 2)
config.add_mixins(*mixins)
def mixin(the_mixin):
"""class decorator"""
config.add_mixins(the_mixin)
return the_mixin
#-----------------------------------------------------------------------
# settings object
class config(object):
app = None
mixins = set()
def update(self, app):
self.app = app
self.add_mixins(ReportableErrorMixin)
@app.errorhandler(ReportableErrorMixin)
def reportable_error_handler(exc):
app.logger.log(self.loglevel, '(reported %s) %s', exc.type_name, exc)
template = getattr(exc, 'template', None) or self.template
body = render_template(template, exc=exc) if template \
else exc.report()
headers = getattr(exc, 'headers', None) or self.headers or {}
return body, exc.status_code, headers
def add_mixins(self, *mixins):
for mixin in mixins:
self.mixins.add(mixin)
@property
def settings(self):
app = self.app
if app is None:
raise RuntimeError('you must run init() before using flask_reportable_error')
return app.config.get('REPORTABLE_ERROR', {})
@property
def loglevel(self):
import logging
return self.settings.get('LOGLEVEL', logging.ERROR)
@property
def default_status_code(self):
return self.settings.get('DEFAULT_STATUS_CODE', 500)
config = config()
#-----------------------------------------------------------------------
# the mixin itself
class ReportableErrorMixin(Exception):
_status_code = None
type_name = 'ReportableErrorMixin'
def report(self):
if sys.version_info.major == 3:
return str(self)
else:
return unicode(self)
@property
def status_code(self):
if self._status_code is None:
return config.default_status_code
else:
return self._status_code
@status_code.setter
def status_code(self, value):
self._status_code = value
#-----------------------------------------------------------------------
# the factory
def single_argument_memoize(f):
memo = {}
@wraps(f)
def wrapper(arg):
resp = memo.get(arg)
if resp is None:
resp = memo[arg] = f(arg)
return resp
return wrapper
@single_argument_memoize
def reportable(exception):
base = config.mixins.copy()
base.add(ReportableErrorMixin)
if all(issubclass(exception, a_mixin) for a_mixin in base):
return exception
base.add(exception)
return type(
'Reportable{0.__name__}'.format(exception),
tuple(base),
{ 'type_name': exception.__name__ },
)
#-----------------------------------------------------------------------
# SQLAlchemy support
try:
from sqlalchemy.exc import DontWrapMixin
config.add_mixins(DontWrapMixin)
except ImportError:
# SQLAlchemy is not installed
pass