Alex Rembish
Slightly Retarded Senior Python Zombie Evangelist
Prostoráčci, 2017
Road of Shame
Earlier this year (actually last year December or something like that)
py3k
So we reused it at project start
python3-tornado-fastrpc
python3-tornado-mysql
Provides almost 1:1 interface as you know from sync Python programming. Don't forget your yields.
@tornado.gen.coroutine def get(self): try: res = yield proxy.getData(123) except Exception as e: self.write('Error: {}'.format(e)) else: self.write('Data: {}'.format(res.value))
Repack of Tornado-MySQL. Current status: frozen with discontinued support (author lost motivation because of reasons).
But it was the only one async library for MySQL at our project start, so we doesn't check library status and used it in our server.
with (yield cursor_for(self.context.db_master)) as cursor:
entity = yield EntityModel.by_id(cursor, eid)
That code looks great and readable, but let's look deeper into it's implementation
@coroutine
def cursor_for(pool):
transaction = yield pool.begin()
@contextmanager
def cursor_manager(transaction):
try:
yield transaction._conn.cursor()
except:
transaction.rollback()
raise
finally:
transaction.commit()
raise Return(cursor_manager(transaction))
Smaller: transaction have to be commited in any case (because of finally).
Bigger: neither commit nor rollback will be called (they are both coroutines).
Side problem: python 3.4 doesn't have async __exit__ and __enter__.
self.options["autocommit"] = True
In Python earlier that 3.5 you can't use asynchronous context managers (even @tornado.gen.coroutines won't work).
We have had 4 MR that may fix that problem, but all one them were rejected after discussion
There are few working solutions, what to do. All of them have some issues and side problems:
Also it's completely sync and brutally slow in Python
cerberus, schema, colander,
marshmallow/webargs, voluptuous
class DistributionSchema(Schema):
web_id = Integer(
dump_to="webId",
load_from="webId",
help="Web ID")
installation_count = Float(
dump_to="installationCount",
load_from="installationCount",
help="Average installation count")
class DistributionResponse(Response):
distributions = List(
Nested(DistributionSchema),
help="Known distributions")
class WebDistributionHandler(RequestHandler):
@coroutine
@schema(
"web.distributions", uri="/web/distributions",
request=GetWebDistributionRequest,
response=GetWebDistributionResponse)
def get(self, watcher_id, from_date, to_date, web_ids=None):
...
By Alex Rembish