-
Notifications
You must be signed in to change notification settings - Fork 3
/
jsend.py
104 lines (92 loc) · 3.59 KB
/
jsend.py
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
import traceback
from functools import wraps
import json
"""
A simple implementation of the jsend standard. See:
http://labs.omniti.com/labs/jsend
"""
def jsend_success(data):
"""
Used when everything was fine with the call.
@return json: {status: "success", data: data}
"""
assert isinstance(data, dict), 'data must be a dictionary'
return json.dumps({"status": "success", "data": data})
def jsend_fail(data):
"""
Used when there was a problem with the request's data or some state wasn't satisfied.
Data should be a dictionary of error messages usually keyed by field names.
@return json: {status: "fail", data: data}
"""
assert isinstance(data, dict), 'data must be a dictionary'
return json.dumps({"status": "fail", "data": data})
def jsend_error(message, code=None, data=None):
"""
Used when there was an exception during a valid API call.
@param message: A human readable error message
@param code: A numeric error code
@param data: a dict containing any other error information for example stack traces
@return json: {status: "error", message: message, <code: code, data: data>}
"""
assert isinstance(message, (str, unicode)), 'message must be a string or unicode'
if data:
assert isinstance(data, dict), 'data must be None or a dictionary'
if code:
assert isinstance(code, (int, float)), 'code must be None or numeric (int or float)'
res = {"status": "error", "message": message}
if code:
res["code"] = code
if data:
res["data"] = data
return json.dumps(res)
def jsend_error_catcher(func):
"""
Wrapper for json ajax routes.
It will convert JsendValueError and JsendTypeError exceptions into jsend_fail
It will catch any uncaught exceptions and return a jsend_error.
If the route returns nothing, it will return an jsend_success with an empty data dict.
"""
@wraps(func)
def wrapped(*args, **kwargs):
try:
res = func(*args, **kwargs)
return res or jsend_success({})
except JsendValueError as e:
return jsend_fail({e.field_name: e.message})
except JsendTypeError as e:
return jsend_fail({e.field_name: e.message})
except Exception as e:
print traceback.format_exc()
data = {
"type": type(e).__name__,
"message": unicode(e),
"stack_trace": traceback.format_exc(),
}
return jsend_error("An error occured", data=data)
return wrapped
class JsendValueError(ValueError):
"""
Raise this instead of a value error while inside a route marked with the jsend_error_catcher decorator
and the field and message parameters will be used to generate a jsend_fail response
"""
def __init__(self, field_name, message):
super(JsendValueError, self).__init__(message)
self.field_name = field_name
class JsendTypeError(JsendValueError):
"""
Raise this instead of a type error while inside a route marked with the jsend_error_catcher decorator
and the field and message parameters will be used to generate a jsend_fail response
"""
pass
class FailCheck(object):
""" Used to return a jsend_fail with multiple errors all at once """
def __init__(self):
super(FailCheck, self).__init__()
self.fails = {}
def add(self, field, description):
self.fails[field] = description
def failed(self):
return bool(self.fails)
def fail(self):
if(self.failed()):
return jsend_fail(self.fails)