From 323e465718174299f392e1fb12726593712a2d34 Mon Sep 17 00:00:00 2001 From: Ben Nemec Date: Tue, 2 Jul 2013 10:59:02 -0500 Subject: Add conditional exception reraise In some cases we need to save an exception and reraise it only under certain conditions. As written, the save_and_reraise_exception context always reraises, which means it can't always be used. This change adds a flag that allows conditional reraising to address the limitation. Change-Id: Ib5c6406a1b91daff94cc4aa305dcb7d6262aecdc --- openstack/common/excutils.py | 38 +++++++++++++++++++++++++++----------- tests/unit/test_excutils.py | 8 ++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/openstack/common/excutils.py b/openstack/common/excutils.py index d40d46c..336e147 100644 --- a/openstack/common/excutils.py +++ b/openstack/common/excutils.py @@ -19,7 +19,6 @@ Exception related utilities. """ -import contextlib import logging import sys import time @@ -28,8 +27,7 @@ import traceback from openstack.common.gettextutils import _ -@contextlib.contextmanager -def save_and_reraise_exception(): +class save_and_reraise_exception(object): """Save current exception, run some code and then re-raise. In some cases the exception context can be cleared, resulting in None @@ -41,15 +39,33 @@ def save_and_reraise_exception(): To work around this, we save the exception state, run handler code, and then re-raise the original exception. If another exception occurs, the saved exception is logged and the new exception is re-raised. - """ - type_, value, tb = sys.exc_info() - try: - yield + + In some cases the caller may not want to re-raise the exception, and + for those circumstances this context provides a reraise flag that + can be used to suppress the exception. For example: + except Exception: - logging.error(_('Original exception being dropped: %s'), - traceback.format_exception(type_, value, tb)) - raise - raise type_, value, tb + with save_and_reraise_exception() as ctxt: + decide_if_need_reraise() + if not should_be_reraised: + ctxt.reraise = False + """ + def __init__(self): + self.reraise = True + + def __enter__(self): + self.type_, self.value, self.tb, = sys.exc_info() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type is not None: + logging.error(_('Original exception being dropped: %s'), + traceback.format_exception(self.type_, + self.value, + self.tb)) + return False + if self.reraise: + raise self.type_, self.value, self.tb def forever_retry_uncaught_exceptions(infunc): diff --git a/tests/unit/test_excutils.py b/tests/unit/test_excutils.py index b8f9b96..1386eaa 100644 --- a/tests/unit/test_excutils.py +++ b/tests/unit/test_excutils.py @@ -52,6 +52,14 @@ class SaveAndReraiseTest(utils.BaseTestCase): self.assertEqual(str(e), msg) + def test_save_and_reraise_exception_no_reraise(self): + """Test that suppressing the reraise works.""" + try: + raise Exception('foo') + except Exception: + with excutils.save_and_reraise_exception() as ctxt: + ctxt.reraise = False + class ForeverRetryUncaughtExceptionsTest(utils.BaseTestCase): -- cgit