Skip to content

Commit

Permalink
Add a quiet_exceptions argument to gen.Multi and gen.multi_future.
Browse files Browse the repository at this point in the history
  • Loading branch information
bdarnell committed Mar 29, 2015
1 parent c95843c commit 91f3f0f
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 15 deletions.
42 changes: 28 additions & 14 deletions tornado/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,11 +556,17 @@ class Multi(YieldPoint):
Futures, in which case a parallel dictionary is returned mapping the same
keys to their results.
It is not normally necessary to call this class directly, as it
will be created automatically as needed. However, calling it directly
allows you to use the ``quiet_exceptions`` argument to control
the logging of multiple exceptions.
.. versionchanged:: 4.2
If multiple ``YieldPoints`` fail, any exceptions after the first
(which is raised) will be logged.
(which is raised) will be logged. Added the ``quiet_exceptions``
argument to suppress this logging for selected exception types.
"""
def __init__(self, children):
def __init__(self, children, quiet_exceptions=()):
self.keys = None
if isinstance(children, dict):
self.keys = list(children.keys())
Expand All @@ -572,6 +578,7 @@ def __init__(self, children):
self.children.append(i)
assert all(isinstance(i, YieldPoint) for i in self.children)
self.unfinished_children = set(self.children)
self.quiet_exceptions = quiet_exceptions

def start(self, runner):
for i in self.children:
Expand All @@ -589,12 +596,13 @@ def get_result(self):
for f in self.children:
try:
result_list.append(f.get_result())
except Exception:
except Exception as e:
if exc_info is None:
exc_info = sys.exc_info()
else:
app_log.error("Multiple exceptions in yield list",
exc_info=True)
if not isinstance(e, self.quiet_exceptions):
app_log.error("Multiple exceptions in yield list",
exc_info=True)
if exc_info is not None:
raise_exc_info(exc_info)
if self.keys is not None:
Expand All @@ -603,7 +611,7 @@ def get_result(self):
return list(result_list)


def multi_future(children):
def multi_future(children, quiet_exceptions=()):
"""Wait for multiple asynchronous futures in parallel.
Takes a list of ``Futures`` (but *not* other ``YieldPoints``) and returns
Expand All @@ -616,16 +624,21 @@ def multi_future(children):
Futures, in which case a parallel dictionary is returned mapping the same
keys to their results.
It is not necessary to call `multi_future` explcitly, since the engine will
do so automatically when the generator yields a list of `Futures`.
This function is faster than the `Multi` `YieldPoint` because it does not
require the creation of a stack context.
It is not normally necessary to call `multi_future` explcitly,
since the engine will do so automatically when the generator
yields a list of `Futures`. However, calling it directly
allows you to use the ``quiet_exceptions`` argument to control
the logging of multiple exceptions.
This function is faster than the `Multi` `YieldPoint` because it
does not require the creation of a stack context.
.. versionadded:: 4.0
.. versionchanged:: 4.2
If multiple ``Futures`` fail, any exceptions after the first (which is
raised) will be logged.
raised) will be logged. Added the ``quiet_exceptions``
argument to suppress this logging for selected exception types.
"""
if isinstance(children, dict):
keys = list(children.keys())
Expand All @@ -646,10 +659,11 @@ def callback(f):
for f in children:
try:
result_list.append(f.result())
except Exception:
except Exception as e:
if future.done():
app_log.error("Multiple exceptions in yield list",
exc_info=True)
if not isinstance(e, quiet_exceptions):
app_log.error("Multiple exceptions in yield list",
exc_info=True)
else:
future.set_exc_info(sys.exc_info())
if not future.done():
Expand Down
14 changes: 13 additions & 1 deletion tornado/test/gen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,12 +403,17 @@ def test_multi_exceptions(self):
self.async_exception(RuntimeError("error 2"))])
self.assertEqual(str(cm.exception), "error 1")

return
# With only one exception, no error is logged.
with self.assertRaises(RuntimeError):
yield gen.Multi([self.async_exception(RuntimeError("error 1")),
self.async_future(2)])

# Exception logging may be explicitly quieted.
with self.assertRaises(RuntimeError):
yield gen.Multi([self.async_exception(RuntimeError("error 1")),
self.async_exception(RuntimeError("error 2"))],
quiet_exceptions=RuntimeError)

@gen_test
def test_multi_future_exceptions(self):
with ExpectLog(app_log, "Multiple exceptions in yield list"):
Expand All @@ -422,6 +427,13 @@ def test_multi_future_exceptions(self):
yield [self.async_exception(RuntimeError("error 1")),
self.async_future(2)]

# Exception logging may be explicitly quieted.
with self.assertRaises(RuntimeError):
yield gen.multi_future(
[self.async_exception(RuntimeError("error 1")),
self.async_exception(RuntimeError("error 2"))],
quiet_exceptions=RuntimeError)

def test_arguments(self):
@gen.engine
def f():
Expand Down

0 comments on commit 91f3f0f

Please sign in to comment.