We have a Python 2 project where we actively use coroutines. We can't find any guidelines on exceptions handling inside coroutines.
For example, here lead developer of Tornado mentioned that coroutines should never raise an exception
, but it is not clear why. Looks like this approach works and heavily used in Tornado.web itself:
https://github.com/tornadoweb/tornado/blob/master/demos/blog/blog.py#L180
class AuthCreateHandler(BaseHandler):
def get(self):
self.render("create_author.html")
@gen.coroutine
def post(self):
if self.any_author_exists():
raise tornado.web.HTTPError(400, "author already created")
hashed_password = yield executor.submit(
bcrypt.hashpw, tornado.escape.utf8(self.get_argument("password")),
bcrypt.gensalt())
tornado.web.HTTPError just extends base Exception class. Also, discussion here https://github.com/tornadoweb/tornado/issues/759#issuecomment-91817197 suggests that raising exception inside coroutine is appropriate.
Also here, active Tornado contributor suggests that raising exceptions is fine:
class PostHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self, slug):
post = yield db.posts.find_one({'slug': slug})
if not post:
raise tornado.web.HTTPError(404)
self.render('post.html', post=post)
Is there any downsides to raising exceptions inside Tornado coroutines or should we raise gen.Return(exception_object)
?
In Python 2, only use raise gen.Return(value)
to return a normal value, not to raise an exception. It is exactly the equivalent of return value
in a coroutine in Python 3.
To raise an exception from a coroutine, a normal raise Exception()
is correct. The wonderful thing about coroutines is their exception-handling semantics are pretty much the same as regular functions.