forked from michaelliao/awesome-python-webapp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapis.py
More file actions
133 lines (115 loc) · 3.8 KB
/
Copy pathapis.py
File metadata and controls
133 lines (115 loc) · 3.8 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Michael Liao'
'''
JSON API definition.
'''
import re, json, logging, functools
from transwarp.web import ctx
class Page(object):
'''
Page object for display pages.
'''
def __init__(self, item_count, page_index=1, page_size=10):
'''
Init Pagination by item_count, page_index and page_size.
>>> p1 = Page(100, 1)
>>> p1.page_count
10
>>> p1.offset
0
>>> p1.limit
10
>>> p2 = Page(90, 9, 10)
>>> p2.page_count
9
>>> p2.offset
80
>>> p2.limit
10
>>> p3 = Page(91, 10, 10)
>>> p3.page_count
10
>>> p3.offset
90
>>> p3.limit
10
'''
self.item_count = item_count
self.page_size = page_size
self.page_count = item_count // page_size + (1 if item_count % page_size > 0 else 0)
if (item_count == 0) or (page_index < 1) or (page_index > self.page_count):
self.offset = 0
self.limit = 0
self.page_index = 1
else:
self.page_index = page_index
self.offset = self.page_size * (page_index - 1)
self.limit = self.page_size
self.has_next = self.page_index < self.page_count
self.has_previous = self.page_index > 1
def __str__(self):
return 'item_count: %s, page_count: %s, page_index: %s, page_size: %s, offset: %s, limit: %s' % (self.item_count, self.page_count, self.page_index, self.page_size, self.offset, self.limit)
__repr__ = __str__
def _dump(obj):
if isinstance(obj, Page):
return {
'page_index': obj.page_index,
'page_count': obj.page_count,
'item_count': obj.item_count,
'has_next': obj.has_next,
'has_previous': obj.has_previous
}
raise TypeError('%s is not JSON serializable' % obj)
def dumps(obj):
return json.dumps(obj, default=_dump)
class APIError(StandardError):
'''
the base APIError which contains error(required), data(optional) and message(optional).
'''
def __init__(self, error, data='', message=''):
super(APIError, self).__init__(message)
self.error = error
self.data = data
self.message = message
class APIValueError(APIError):
'''
Indicate the input value has error or invalid. The data specifies the error field of input form.
'''
def __init__(self, field, message=''):
super(APIValueError, self).__init__('value:invalid', field, message)
class APIResourceNotFoundError(APIError):
'''
Indicate the resource was not found. The data specifies the resource name.
'''
def __init__(self, field, message=''):
super(APIResourceNotFoundError, self).__init__('value:notfound', field, message)
class APIPermissionError(APIError):
'''
Indicate the api has no permission.
'''
def __init__(self, message=''):
super(APIPermissionError, self).__init__('permission:forbidden', 'permission', message)
def api(func):
'''
A decorator that makes a function to json api, makes the return value as json.
@app.route('/api/test')
@api
def api_test():
return dict(result='123', items=[])
'''
@functools.wraps(func)
def _wrapper(*args, **kw):
try:
r = dumps(func(*args, **kw))
except APIError, e:
r = json.dumps(dict(error=e.error, data=e.data, message=e.message))
except Exception, e:
logging.exception(e)
r = json.dumps(dict(error='internalerror', data=e.__class__.__name__, message=e.message))
ctx.response.content_type = 'application/json'
return r
return _wrapper
if __name__=='__main__':
import doctest
doctest.testmod()