-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbottle_mysql.py
128 lines (107 loc) · 4.16 KB
/
bottle_mysql.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
'''
Bottle-MySQL is a plugin that integrates MySQL with your Bottle
application. It automatically connects to a database at the beginning of a
request, passes the database handle to the route callback and closes the
connection afterwards.
To automatically detect routes that need a database connection, the plugin
searches for route callbacks that require a `db` keyword argument
(configurable) and skips routes that do not. This removes any overhead for
routes that don't need a database connection.
Results are returned as dictionaries.
Usage Example::
'''
__author__ = 'Kaiyang Lv'
__version__ = '0.0.1'
__license__ = None
import inspect
import bottle
import pymysql.cursors
class MySQLPlugin(object):
'''
This plugin passes a mysql database handle to route callbacks
that accept a `db` keyword argument. If a callback does not expect
such a parameter, no connection is made. You can override the database
settings on a per-route basis.
'''
name = 'mysql'
api = 2
def __init__(self, dbuser=None, dbpass=None, dbname=None, dbhost='localhost', dbport=3306, keyword='db',
charset='utf8', dictrows=True, autocommit=True):
self.dbuser = dbuser
self.dbpass = dbpass
self.dbname = dbname
self.dbhost = dbhost
self.dbport = dbport
self.keyword = keyword
self.dictrows = dictrows
self.autocommit = autocommit
self.charset = charset
def setup(self, app):
'''
Make sure that other installed plugins don't affect the same keyword argument.
'''
for other in app.plugins:
if not isinstance(other, MySQLPlugin):
continue
if other.keyword == self.keyword:
raise PluginError("Found another mysql plugin with conflicting settings (non-unique keyword).")
elif other.name == self.name:
self.name += '_%s' % self.keyword
def apply(self, callback, route):
# Override global configuration with route-specific values.
if 'mysql' in route.config:
g = lambda key, default: route.config.get('mysql', {}).get(key, default)
else:
g = lambda key, default: route.config.get('mysql.' + key, default)
dbuser = g('dbuser', self.dbuser)
dbpass = g('dbpass', self.dbpass)
dbname = g('dbname', self.dbname)
dbhost = g('dbhost', self.dbhost)
dbport = g('dbport', self.dbport)
keyword = g('keyword', self.keyword)
dictrows = g('dictrows', self.dictrows)
autocommit = g('autocommit', self.autocommit)
charset = g('charset', self.charset)
# Test if the original callback accepts a 'db' keyword.
# Ignore it if it does not need a database handle.
_args = inspect.getargspec(route.callback)
if keyword not in _args.args:
return callback
def wrapper(*args, **kwargs):
con = None
try:
kw = {
'host': dbhost,
'user': dbuser,
'password': dbpass,
'db': dbname,
'charset': charset,
'port': dbport
}
if dictrows:
kw['cursorclass'] = pymysql.cursors.DictCursor
con = pymysql.connect(**kw)
cur = con.cursor()
except bottle.HTTPResponse as e:
raise bottle.HTTPError(500, 'Database Error', e)
# Add the connection handle as a keyword argument.
kwargs[keyword] = cur
try:
rv = callback(*args, **kwargs)
if autocommit:
con.commit()
except pymysql.IntegrityError as e:
con.rollback()
raise bottle.HTTPError(500, 'Database Error', e)
except bottle.HTTPError:
raise
except bottle.HTTPResponse:
if autocommit:
con.commit()
raise
finally:
if con:
con.close()
return rv
return wrapper
Plugin = MySQLPlugin