From 7b13bc1888ff1fd1fc56869cf2870830a4f8431b Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Tue, 4 Feb 2014 09:18:35 -0800 Subject: [PATCH] Update ExpectedException handling Catch expected exceptions in a way that supports subclasses also being seen as "expected." Log a little more detail about unexpected exceptions in the ExecutorBase so it is easier to add them to the list of expected exceptions. Change-Id: I1bdd628eba717308f0afe1a889efd8711bad6296 Closes-bug: #1276163 --- oslo/messaging/_executors/base.py | 8 ++++++-- oslo/messaging/rpc/server.py | 13 ++++++++----- tests/test_expected_exceptions.py | 13 +++++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/oslo/messaging/_executors/base.py b/oslo/messaging/_executors/base.py index 79ab6223e..897097d8c 100644 --- a/oslo/messaging/_executors/base.py +++ b/oslo/messaging/_executors/base.py @@ -38,12 +38,16 @@ class ExecutorBase(object): _LOG.debug('Expected exception during message handling (%s)' % e.exc_info[1]) incoming.reply(failure=e.exc_info, log_failure=False) - except Exception: + except Exception as e: # sys.exc_info() is deleted by LOG.exception(). exc_info = sys.exc_info() - _LOG.error('Exception during message handling', + _LOG.error('Exception during message handling: %s', e, exc_info=exc_info) incoming.reply(failure=exc_info) + # NOTE(dhellmann): Remove circular object reference + # between the current stack frame and the traceback in + # exc_info. + del exc_info @abc.abstractmethod def start(self): diff --git a/oslo/messaging/rpc/server.py b/oslo/messaging/rpc/server.py index aaa071671..2a1f238c7 100644 --- a/oslo/messaging/rpc/server.py +++ b/oslo/messaging/rpc/server.py @@ -151,10 +151,13 @@ def expected_exceptions(*exceptions): def inner(*args, **kwargs): try: return func(*args, **kwargs) - except Exception as e: - if type(e) in exceptions: - raise ExpectedException() - else: - raise + # Take advantage of the fact that we can catch + # multiple exception types using a tuple of + # exception classes, with subclass detection + # for free. Any exception that is not in or + # derived from the args passed to us will be + # ignored and thrown as normal. + except exceptions: + raise ExpectedException() return inner return outer diff --git a/tests/test_expected_exceptions.py b/tests/test_expected_exceptions.py index 1585e4b0d..8ec94a149 100644 --- a/tests/test_expected_exceptions.py +++ b/tests/test_expected_exceptions.py @@ -43,6 +43,19 @@ class TestExpectedExceptions(test_utils.BaseTestCase): self.assertRaises(messaging.ExpectedException, naughty) + def test_decorator_expected_subclass(self): + class FooException(Exception): + pass + + class BarException(FooException): + pass + + @messaging.expected_exceptions(FooException) + def naughty(): + raise BarException() + + self.assertRaises(messaging.ExpectedException, naughty) + def test_decorator_unexpected(self): class FooException(Exception): pass