Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Improve exception handling for gen module.

  • Loading branch information...
commit 5997f411d5cdf00b16c05be70dbe1c6ad19f2cbc 1 parent 2f91460
@bdarnell bdarnell authored
Showing with 61 additions and 10 deletions.
  1. +26 −10 tornado/gen.py
  2. +35 −0 tornado/test/gen_test.py
View
36 tornado/gen.py
@@ -49,6 +49,7 @@ def get(self):
"""
import functools
+import sys
import types
class KeyReuseError(Exception): pass
@@ -215,8 +216,9 @@ def __init__(self, gen):
self.yield_point = _NullYieldPoint()
self.pending_callbacks = set()
self.results = {}
- self.waiting = None
self.running = False
+ self.finished = False
+ self.exc_info = None
def register_callback(self, key):
"""Adds ``key`` to the list of callbacks."""
@@ -244,26 +246,40 @@ def run(self):
"""Starts or resumes the generator, running until it reaches a
yield point that is not ready.
"""
- if self.running:
+ if self.running or self.finished:
return
try:
self.running = True
while True:
- if not self.yield_point.is_ready():
- return
- next = self.yield_point.get_result()
+ if self.exc_info is None:
+ try:
+ if not self.yield_point.is_ready():
+ return
+ next = self.yield_point.get_result()
+ except Exception:
+ self.exc_info = sys.exc_info()
try:
- yielded = self.gen.send(next)
+ if self.exc_info is not None:
+ exc_info = self.exc_info
+ self.exc_info = None
+ yielded = self.gen.throw(*exc_info)
+ else:
+ yielded = self.gen.send(next)
except StopIteration:
+ self.finished = True
if self.pending_callbacks:
raise LeakedCallbackError(
"finished without waiting for callbacks %r" %
self.pending_callbacks)
return
- if not isinstance(yielded, YieldPoint):
- raise BadYieldError("yielded unknown object %r" % yielded)
- self.yield_point = yielded
- self.yield_point.start(self)
+ except Exception:
+ self.finished = True
+ raise
+ if isinstance(yielded, YieldPoint):
+ self.yield_point = yielded
+ self.yield_point.start(self)
+ else:
+ self.exc_info = (BadYieldError("yielded unknown object %r" % yielded),)
finally:
self.running = False
View
35 tornado/test/gen_test.py
@@ -125,6 +125,41 @@ def f():
self.stop()
self.run_gen(f)
+ def test_exception_in_yield(self):
+ @gen.engine
+ def f():
+ try:
+ yield gen.Wait("k1")
+ raise "did not get expected exception"
+ except gen.UnknownKeyError:
+ pass
+ self.stop()
+ self.run_gen(f)
+
+ def test_resume_after_exception_in_yield(self):
+ @gen.engine
+ def f():
+ try:
+ yield gen.Wait("k1")
+ raise "did not get expected exception"
+ except gen.UnknownKeyError:
+ pass
+ (yield gen.Callback("k2"))("v2")
+ self.assertEqual((yield gen.Wait("k2")), "v2")
+ self.stop()
+ self.run_gen(f)
+
+ def test_orphaned_callback(self):
+ @gen.engine
+ def f():
+ self.orphaned_callback = yield gen.Callback(1)
+ try:
+ self.run_gen(f)
+ raise "did not get expected exception"
+ except gen.LeakedCallbackError:
+ pass
+ self.orphaned_callback()
+
class GenSequenceHandler(RequestHandler):
@asynchronous
Please sign in to comment.
Something went wrong with that request. Please try again.