├── README.rst ├── example.py ├── sanicdb.py ├── setup.py ├── test.py └── upload-to-pypi /README.rst: -------------------------------------------------------------------------------- 1 | SanicDB - Archived, please check [veelion/ezmysql](https://github.com/veelion/ezmysql) for new implementation. 2 | ====== 3 | 4 | Description 5 | ----------- 6 | 7 | SanicDB is a simple wrapper around `aiomysql` that refer to torndb which 8 | is in old Tornado (http://www.tornadoweb.org). 9 | 10 | Limitations 11 | ----------- 12 | 13 | SanicDB only supports Python 3.5 or newer which support asyncio 14 | 15 | Installation 16 | ------------ 17 | 18 | ``pip3 install sanicdb`` 19 | 20 | Documentation 21 | ------------- 22 | 23 | to check example.py 24 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | # Author: veelion@ebuinfo.com 4 | 5 | from sanic import Sanic 6 | from sanic import response 7 | from sanicdb import SanicDB 8 | 9 | app = Sanic('demo') 10 | db = SanicDB('localhost', 'the_db', 'the_user', 'the_password', sanic=app) 11 | 12 | 13 | @app.route('/') 14 | async def index(request): 15 | sql = 'select * from test where id=1' 16 | data = await app.db.query(sql) 17 | return response.json(data) 18 | 19 | 20 | if __name__ == '__main__': 21 | app.run() 22 | -------------------------------------------------------------------------------- /sanicdb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """A lightweight wrapper around aiomysql.Pool for easy to use 3 | """ 4 | 5 | import traceback 6 | import aiomysql 7 | import pymysql 8 | 9 | version = "0.3" 10 | version_info = (0, 3, 0, 0) 11 | 12 | 13 | class SanicDB: 14 | """A lightweight wrapper around aiomysql.Pool for easy to use 15 | """ 16 | def __init__(self, host, database, user, password, 17 | loop=None, sanic=None, 18 | minsize=3, maxsize=5, 19 | return_dict=True, 20 | pool_recycle=7*3600, 21 | autocommit=True, 22 | charset = "utf8mb4", **kwargs): 23 | ''' 24 | kwargs: all parameters that aiomysql.connect() accept. 25 | ''' 26 | self.db_args = { 27 | 'host': host, 28 | 'db': database, 29 | 'user': user, 30 | 'password': password, 31 | 'minsize': minsize, 32 | 'maxsize': maxsize, 33 | 'charset': charset, 34 | 'loop': loop, 35 | 'autocommit': autocommit, 36 | 'pool_recycle': pool_recycle, 37 | } 38 | self.sanic = sanic 39 | if sanic: 40 | sanic.db = self 41 | if return_dict: 42 | self.db_args['cursorclass']=aiomysql.cursors.DictCursor 43 | if kwargs: 44 | self.db_args.update(kwargs) 45 | self.pool = None 46 | 47 | async def init_pool(self): 48 | if self.sanic: 49 | self.db_args['loop'] = self.sanic.loop 50 | self.pool = await aiomysql.create_pool(**self.db_args) 51 | 52 | async def query_many(self, queries): 53 | """query man SQL, Returns all result.""" 54 | if not self.pool: 55 | await self.init_pool() 56 | async with self.pool.acquire() as conn: 57 | async with conn.cursor() as cur: 58 | results = [] 59 | for query in queries: 60 | try: 61 | await cur.execute(query) 62 | ret = await cur.fetchall() 63 | except pymysql.err.InternalError: 64 | await conn.ping() 65 | await cur.execute(query) 66 | ret = await cur.fetchall() 67 | results.append(ret) 68 | return results 69 | 70 | async def query(self, query, *parameters, **kwparameters): 71 | """Returns a row list for the given query and parameters.""" 72 | if not self.pool: 73 | await self.init_pool() 74 | async with self.pool.acquire() as conn: 75 | async with conn.cursor() as cur: 76 | try: 77 | await cur.execute(query, kwparameters or parameters) 78 | ret = await cur.fetchall() 79 | except pymysql.err.InternalError: 80 | await conn.ping() 81 | await cur.execute(query, kwparameters or parameters) 82 | ret = await cur.fetchall() 83 | return ret 84 | 85 | async def get(self, query, *parameters, **kwparameters): 86 | """Returns the (singular) row returned by the given query. 87 | """ 88 | if not self.pool: 89 | await self.init_pool() 90 | async with self.pool.acquire() as conn: 91 | async with conn.cursor() as cur: 92 | try: 93 | await cur.execute(query, kwparameters or parameters) 94 | ret = await cur.fetchone() 95 | except pymysql.err.InternalError: 96 | await conn.ping() 97 | await cur.execute(query, kwparameters or parameters) 98 | ret = await cur.fetchone() 99 | return ret 100 | 101 | async def execute(self, query, *parameters, **kwparameters): 102 | """Executes the given query, returning the lastrowid from the query.""" 103 | if not self.pool: 104 | await self.init_pool() 105 | async with self.pool.acquire() as conn: 106 | async with conn.cursor() as cur: 107 | try: 108 | await cur.execute(query, kwparameters or parameters) 109 | except Exception: 110 | # https://github.com/aio-libs/aiomysql/issues/340 111 | await conn.ping() 112 | await cur.execute(query, kwparameters or parameters) 113 | return cur.lastrowid 114 | 115 | # high level interface 116 | async def table_has(self, table_name, field, value): 117 | sql = 'SELECT {} FROM {} WHERE {}=%s limit 1'.format(field, table_name, field) 118 | d = await self.get(sql, value) 119 | return d 120 | 121 | async def table_insert(self, table_name, item, ignore_duplicated=True): 122 | '''item is a dict : key is mysql table field''' 123 | fields = list(item.keys()) 124 | values = list(item.values()) 125 | fieldstr = ','.join(fields) 126 | valstr = ','.join(['%s'] * len(item)) 127 | sql = 'INSERT INTO %s (%s) VALUES(%s)' % (table_name, fieldstr, valstr) 128 | try: 129 | last_id = await self.execute(sql, *values) 130 | return last_id 131 | except Exception as e: 132 | if ignore_duplicated and e.args[0] == 1062: 133 | # just skip duplicated item 134 | return 0 135 | traceback.print_exc() 136 | print('sql:', sql) 137 | print('item:') 138 | for i in range(len(fields)): 139 | vs = str(values[i]) 140 | if len(vs) > 300: 141 | print(fields[i], ' : ', len(vs), type(values[i])) 142 | else: 143 | print(fields[i], ' : ', vs, type(values[i])) 144 | raise e 145 | 146 | async def table_update(self, table_name, updates, 147 | field_where, value_where): 148 | '''updates is a dict of {field_update:value_update}''' 149 | upsets = [] 150 | values = [] 151 | for k, v in updates.items(): 152 | s = '%s=%%s' % k 153 | upsets.append(s) 154 | values.append(v) 155 | upsets = ','.join(upsets) 156 | sql = 'UPDATE %s SET %s WHERE %s="%s"' % ( 157 | table_name, 158 | upsets, 159 | field_where, value_where, 160 | ) 161 | await self.execute(sql, *(values)) 162 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import sanicdb 3 | 4 | setup( 5 | name="sanicdb", 6 | version=sanicdb.version, 7 | description=sanicdb.__doc__, 8 | author="veelion", 9 | author_email="veelion@gmail.com", 10 | url='https://github.com/veelion/sanicdb', 11 | classifiers=[ 12 | 'Development Status :: 5 - Production/Stable', 13 | 'Intended Audience :: Developers', 14 | 'License :: BSD', 15 | 'Programming Language :: Python :: 3.5', 16 | 'Programming Language :: Python :: 3.6', 17 | 'Programming Language :: Python :: 3.7', 18 | ], 19 | install_requires=[ 20 | 'pymysql', 21 | 'aiomysql', 22 | ], 23 | license='BSD', 24 | py_modules=['sanicdb'], 25 | include_package_data=True, 26 | zip_safe=False 27 | ) 28 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | # Author: veelion@ebuinfo.com 4 | 5 | 6 | import asyncio 7 | from sanicdb import SanicDB 8 | 9 | 10 | async def test(loop): 11 | db = SanicDB('localhost', 'testdb', 'root', 'the_password', 12 | minsize=3, maxsize=5, 13 | connect_timeout=5, 14 | loop=loop) 15 | sql = 'Drop table test' 16 | await db.execute(sql) 17 | 18 | sql = """CREATE TABLE `test` ( 19 | `id` int(8) NOT NULL AUTO_INCREMENT, 20 | `name` varchar(16) NOT NULL, 21 | `content` varchar(255) NOT NULL, 22 | PRIMARY KEY (`id`), 23 | UNIQUE KEY `name` (`name`) 24 | ) ENGINE=MyISAM ;""" 25 | await db.execute(sql) 26 | 27 | sql = 'select * from test where name = %s' 28 | data = await db.query(sql, 'abc') 29 | print('query():', data) 30 | 31 | sql += ' limit 1' 32 | d = await db.get(sql, 'abc') 33 | print('get():', d) 34 | 35 | sql = 'delete from test where name=%s' 36 | lastrowid = await db.execute(sql, 'xyz') 37 | print('execute(delete...):', lastrowid) 38 | sql = 'insert into test set name=%s, content=%s' 39 | lastrowid = await db.execute(sql, 'xyz', '456') 40 | print('execute(insert...):', lastrowid) 41 | 42 | ret = await db.table_has('test', 'name', 'abc') 43 | print('has(): ', ret) 44 | 45 | ret = await db.table_update('test', {'content': 'updated'}, 46 | 'name', 'abc') 47 | print('update():', ret) 48 | sql = 'select * from test where name = %s' 49 | data = await db.query(sql, 'abc') 50 | print('query():', data) 51 | 52 | item = { 53 | 'name': 'abc', 54 | 'content': '123' 55 | } 56 | i = 0 57 | while 1: 58 | i += 1 59 | if i % 2 == 0: 60 | lastid = await db.table_insert('test', item, ignore_duplicated=False) 61 | else: 62 | lastid = await db.table_insert('test', item) 63 | print('insert():', lastid) 64 | await asyncio.sleep(1) 65 | 66 | 67 | if __name__ == '__main__': 68 | loop = asyncio.get_event_loop() 69 | loop.run_until_complete(test(loop)) 70 | -------------------------------------------------------------------------------- /upload-to-pypi: -------------------------------------------------------------------------------- 1 | 0. pip install twine 2 | 1. python setup.py sdist bdist_wheel 3 | 2. twine upload dist/* 4 | --------------------------------------------------------------------------------