Reputation: 158
I am new to Python and I stumbled upon a problem with exception handling. I'm writing a simple tornado + momoko application. In summary, I have a global (?) object that is created within the main function, the object is of class QueryExecutor. It's a simple class that handles SQL query execution using momoko.
class QueryExecutor:
def __init__(self, database):
self.db = database
@gen.engine
def _run(self, query):
self.db.execute(query, callback = (yield gen.Callback('q')))
try:
cursor = yield momoko.WaitOp('q')
except Exception as error:
print(str(error))
def save(self, tablename, data):
fields = "(" + ", ".join(map(str, list(data.keys()))) + ")"
values = "(" + "\'" + '\', \''.join(map(str, list(data.values()))) + "\'" + ")"
query = "INSERT INTO " + tablename + " " + fields + " VALUES " + values + ";"
self._run(query)
What I want to achieve is to use an object of this class inside request handlers and to somehow be able to tell when an exception has occurred:
class RegistrationHandler(tornado.web.RequestHandler):
def get(self):
self.render("templates/register.html")
def post(self):
#...handle post arguments, retrieve user data, check if correct etc., do stuff...
#...if everything ok:
queryExec.save("users", userdata)
#what to do if save threw an exception?
application.db = momoko.Pool(dsn=dbsetup.dsn, size=1)
if __name__ == "__main__":
queryExec = dbsetup.QueryExecutor(database=application.db)
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
queryExec.save() throws an exception when the query fails, and I would like to know inside the request handler function if it happens. Embedding queryExec.save() in try and except blocks does not work. Obviously I can probably pass additional parameters (references?) to queryExec.save(), or add some kind of a state parameter to the QueryExecutor class itself, but I'm wondering if there is a more elegant way to solve this?
EDIT: After some modifications:
class TestEx(Exception): pass
and:
@gen.engine
def _run(self, query):
self.db.execute(query, callback = (yield gen.Callback('q')))
try:
cursor = yield momoko.WaitOp('q')
except Exception as error:
print(str(error))
raise TestEx("test exception")
and:
try:
queryExec.save("users", userdata)
except dbsetup.TestEx as ex:
print("exception caught in caller function")
self.redirect("templates/login.html")
I get in the console:
/usr/bin/python3.3 /home/tybur/PycharmProjects/tornadochat/main.py
duplicate key value violates unique constraint "unique_names"
DETAIL: Key (name)=(testuser) already exists.
ERROR:tornado.application:Exception in callback None
Traceback (most recent call last):
File "/home/tybur/PycharmProjects/tornadochat/dbsetup.py", line 46, in _run
cursor = yield momoko.WaitOp('q')
File "/usr/local/lib/python3.3/dist-packages/tornado/gen.py", line 520, in run
next = self.yield_point.get_result()
File "/usr/local/lib/python3.3/dist-packages/momoko/utils.py", line 59, in get_result
raise error
File "/usr/local/lib/python3.3/dist-packages/momoko/connection.py", line 244, in io_callback
state = self.connection.poll()
psycopg2.IntegrityError: duplicate key value violates unique constraint "unique_names"
DETAIL: Key (name)=(testuser) already exists.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.3/dist-packages/tornado/ioloop.py", line 688, in start
self._handlers[fd](fd, events)
File "/usr/local/lib/python3.3/dist-packages/tornado/stack_context.py", line 331, in wrapped
raise_exc_info(exc)
File "<string>", line 3, in raise_exc_info
File "/usr/local/lib/python3.3/dist-packages/tornado/stack_context.py", line 302, in wrapped
ret = fn(*args, **kwargs)
File "/usr/local/lib/python3.3/dist-packages/momoko/connection.py", line 248, in io_callback
self.callback(error)
File "/usr/local/lib/python3.3/dist-packages/tornado/stack_context.py", line 331, in wrapped
raise_exc_info(exc)
File "<string>", line 3, in raise_exc_info
File "/usr/local/lib/python3.3/dist-packages/tornado/stack_context.py", line 302, in wrapped
ret = fn(*args, **kwargs)
File "/usr/local/lib/python3.3/dist-packages/tornado/gen.py", line 574, in inner
self.set_result(key, result)
File "/usr/local/lib/python3.3/dist-packages/tornado/gen.py", line 500, in set_result
self.run()
File "/usr/local/lib/python3.3/dist-packages/tornado/gen.py", line 529, in run
yielded = self.gen.throw(*exc_info)
File "/home/tybur/PycharmProjects/tornadochat/dbsetup.py", line 49, in _run
raise TestEx("test exception")
dbsetup.TestEx: test exception
Upvotes: 0
Views: 417
Reputation: 22154
save()
does not raise an exception; it starts a call to _run
but does not wait for it, so there is nowhere for the exception to go except to be logged. To fix this you should follow three rules:
@gen.engine
, you should use @gen.coroutine
instead, which makes things work a little more like normal functions.Future
, which is a placeholder for its actual result) and yield it later (you might want to do this to start several coroutines in parallel and wait for them all at once).So in this case, make save()
, _run()
and post()
coroutines, and use the yield keyword when calling _run()
and save()
.
Upvotes: 1