Fix handling of MultipleException
Change-Id: I187c364db9e41c437e9d6dbcbc6df7bfadba43bb
This commit is contained in:
parent
d41a755991
commit
5fe0a7d610
@ -33,6 +33,8 @@ fail = _asserts.fail
|
|||||||
TobikoException = _exception.TobikoException
|
TobikoException = _exception.TobikoException
|
||||||
check_valid_type = _exception.check_valid_type
|
check_valid_type = _exception.check_valid_type
|
||||||
exc_info = _exception.exc_info
|
exc_info = _exception.exc_info
|
||||||
|
handle_multiple_exceptions = _exception.handle_multiple_exceptions
|
||||||
|
list_exc_infos = _exception.list_exc_infos
|
||||||
|
|
||||||
is_fixture = _fixture.is_fixture
|
is_fixture = _fixture.is_fixture
|
||||||
get_fixture = _fixture.get_fixture
|
get_fixture = _fixture.get_fixture
|
||||||
|
@ -13,11 +13,13 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import collections
|
import collections
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
import six
|
import six
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -107,3 +109,36 @@ def exc_info(reraise=True):
|
|||||||
info = ExceptionInfo(*sys.exc_info())
|
info = ExceptionInfo(*sys.exc_info())
|
||||||
info.reraise_on_exit = reraise
|
info.reraise_on_exit = reraise
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def handle_multiple_exceptions():
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
except testtools.MultipleExceptions as exc:
|
||||||
|
exc_infos = list_exc_infos()
|
||||||
|
if exc_infos:
|
||||||
|
for info in exc_infos[1:]:
|
||||||
|
LOG.exception("Unhandled exception:", exc_info=info)
|
||||||
|
six.reraise(*exc_infos[0])
|
||||||
|
else:
|
||||||
|
LOG.debug('empty MultipleExceptions: %s', str(exc))
|
||||||
|
|
||||||
|
|
||||||
|
def list_exc_infos(exc_info=None):
|
||||||
|
# pylint: disable=redefined-outer-name
|
||||||
|
exc_info = exc_info or sys.exc_info()
|
||||||
|
result = []
|
||||||
|
if exc_info[0]:
|
||||||
|
visited = set()
|
||||||
|
visiting = [exc_info]
|
||||||
|
while visiting:
|
||||||
|
exc_info = visiting.pop()
|
||||||
|
_, exc, _ = exc_info
|
||||||
|
if exc not in visited:
|
||||||
|
visited.add(exc)
|
||||||
|
if isinstance(exc, testtools.MultipleExceptions):
|
||||||
|
visiting.extend(reversed(exc.args))
|
||||||
|
else:
|
||||||
|
result.append(exc_info)
|
||||||
|
return result
|
||||||
|
@ -23,6 +23,7 @@ import testtools
|
|||||||
|
|
||||||
import tobiko
|
import tobiko
|
||||||
from tobiko.common import _detail
|
from tobiko.common import _detail
|
||||||
|
from tobiko.common import _exception
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -98,33 +99,24 @@ def remove_fixture(obj, manager=None):
|
|||||||
def setup_fixture(obj, manager=None):
|
def setup_fixture(obj, manager=None):
|
||||||
'''Get registered fixture and setup it up'''
|
'''Get registered fixture and setup it up'''
|
||||||
fixture = get_fixture(obj, manager=manager)
|
fixture = get_fixture(obj, manager=manager)
|
||||||
try:
|
with _exception.handle_multiple_exceptions():
|
||||||
fixture.setUp()
|
fixture.setUp()
|
||||||
except testtools.MultipleExceptions as ex:
|
|
||||||
for exc_info in ex.args[1:]:
|
|
||||||
LOG.exception("Error setting up fixture %r",
|
|
||||||
fixture.fixture_name, exc_info=exc_info)
|
|
||||||
six.reraise(*ex.args[0])
|
|
||||||
return fixture
|
return fixture
|
||||||
|
|
||||||
|
|
||||||
def reset_fixture(obj, manager=None):
|
def reset_fixture(obj, manager=None):
|
||||||
'''Get registered fixture and setup it up'''
|
'''Get registered fixture and reset it'''
|
||||||
fixture = get_fixture(obj, manager=manager)
|
fixture = get_fixture(obj, manager=manager)
|
||||||
try:
|
with _exception.handle_multiple_exceptions():
|
||||||
fixture.reset()
|
fixture.reset()
|
||||||
except testtools.MultipleExceptions as ex:
|
|
||||||
for exc_info in ex.args[1:]:
|
|
||||||
LOG.exception("Error reseting fixture %r",
|
|
||||||
fixture.fixture_name, exc_info=exc_info)
|
|
||||||
six.reraise(*ex.args[0])
|
|
||||||
return fixture
|
return fixture
|
||||||
|
|
||||||
|
|
||||||
def cleanup_fixture(obj, manager=None):
|
def cleanup_fixture(obj, manager=None):
|
||||||
'''Get registered fixture and clean it up'''
|
'''Get registered fixture and clean it up'''
|
||||||
fixture = get_fixture(obj, manager=manager)
|
fixture = get_fixture(obj, manager=manager)
|
||||||
fixture.cleanUp()
|
with _exception.handle_multiple_exceptions():
|
||||||
|
fixture.cleanUp()
|
||||||
return fixture
|
return fixture
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import sys
|
|||||||
|
|
||||||
import tobiko
|
import tobiko
|
||||||
from tobiko.tests import unit
|
from tobiko.tests import unit
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
|
||||||
class SomeException(tobiko.TobikoException):
|
class SomeException(tobiko.TobikoException):
|
||||||
@ -122,3 +123,95 @@ class TestExcInfo(unit.TobikoUnitTest):
|
|||||||
|
|
||||||
reraised = self.assertRaises(RuntimeError, exc_info.reraise)
|
reraised = self.assertRaises(RuntimeError, exc_info.reraise)
|
||||||
self.assertIs(exc_value, reraised)
|
self.assertIs(exc_value, reraised)
|
||||||
|
|
||||||
|
|
||||||
|
class TestListExcInfo(unit.TobikoUnitTest):
|
||||||
|
|
||||||
|
def test_list_exc_info(self):
|
||||||
|
result = tobiko.list_exc_infos()
|
||||||
|
self.assertEqual([], result)
|
||||||
|
|
||||||
|
def test_list_exc_info_with_info(self):
|
||||||
|
error = make_exception(RuntimeError, 'error')
|
||||||
|
result = tobiko.list_exc_infos(exc_info=error)
|
||||||
|
self.assertEqual([error], result)
|
||||||
|
|
||||||
|
def test_list_exc_info_handling_exception(self):
|
||||||
|
try:
|
||||||
|
raise RuntimeError('error')
|
||||||
|
except RuntimeError:
|
||||||
|
result = tobiko.list_exc_infos()
|
||||||
|
self.assertEqual([sys.exc_info()], result)
|
||||||
|
|
||||||
|
def test_list_exc_info_handling_empty_multiple_exceptions(self):
|
||||||
|
error = make_exception(testtools.MultipleExceptions)
|
||||||
|
result = tobiko.list_exc_infos(exc_info=error)
|
||||||
|
self.assertEqual([], result)
|
||||||
|
|
||||||
|
def test_list_exc_info_handling_multiple_exceptions(self):
|
||||||
|
a = make_exception(RuntimeError, 'a')
|
||||||
|
b = make_exception(ValueError, 'b')
|
||||||
|
c = make_exception(TypeError, 'c')
|
||||||
|
multi = make_exception(testtools.MultipleExceptions, a, b, c)
|
||||||
|
result = tobiko.list_exc_infos(exc_info=multi)
|
||||||
|
self.assertEqual([a, b, c], result)
|
||||||
|
|
||||||
|
def test_list_exc_info_handling_nested_multiple_exceptions(self):
|
||||||
|
a = make_exception(RuntimeError, 'a')
|
||||||
|
b = make_exception(ValueError, 'b')
|
||||||
|
c = make_exception(TypeError, 'c')
|
||||||
|
d = make_exception(IndexError, 'd')
|
||||||
|
inner = make_exception(testtools.MultipleExceptions, b, c)
|
||||||
|
multi = make_exception(testtools.MultipleExceptions, a, inner, d)
|
||||||
|
result = tobiko.list_exc_infos(exc_info=multi)
|
||||||
|
self.assertEqual([a, b, c, d], result)
|
||||||
|
|
||||||
|
|
||||||
|
class TestHandleMultipleExceptions(unit.TobikoUnitTest):
|
||||||
|
|
||||||
|
def test_handle_multiple_exceptions(self):
|
||||||
|
with tobiko.handle_multiple_exceptions():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_handle_multiple_exceptions_with_exception(self):
|
||||||
|
def run():
|
||||||
|
with tobiko.handle_multiple_exceptions():
|
||||||
|
raise RuntimeError('error')
|
||||||
|
self.assertRaises(RuntimeError, run)
|
||||||
|
|
||||||
|
def test_handle_multiple_exceptions_with_empty_multiple_exception(self):
|
||||||
|
with tobiko.handle_multiple_exceptions():
|
||||||
|
raise testtools.MultipleExceptions()
|
||||||
|
|
||||||
|
def test_handle_multiple_exceptions_with_multiple_exceptions(self):
|
||||||
|
a = make_exception(TypeError, 'a')
|
||||||
|
b = make_exception(ValueError, 'b')
|
||||||
|
c = make_exception(RuntimeError, 'c')
|
||||||
|
|
||||||
|
def run():
|
||||||
|
with tobiko.handle_multiple_exceptions():
|
||||||
|
raise testtools.MultipleExceptions(a, b, c)
|
||||||
|
|
||||||
|
ex = self.assertRaises(TypeError, run)
|
||||||
|
self.assertEqual(a[1], ex)
|
||||||
|
|
||||||
|
def test_handle_multiple_exceptions_with_nested_multiple_exceptions(self):
|
||||||
|
a = make_exception(RuntimeError, 'a')
|
||||||
|
b = make_exception(ValueError, 'b')
|
||||||
|
c = make_exception(TypeError, 'c')
|
||||||
|
d = make_exception(IndexError, 'd')
|
||||||
|
inner = make_exception(testtools.MultipleExceptions, b, c)
|
||||||
|
|
||||||
|
def run():
|
||||||
|
with tobiko.handle_multiple_exceptions():
|
||||||
|
raise testtools.MultipleExceptions(a, inner, d)
|
||||||
|
|
||||||
|
ex = self.assertRaises(RuntimeError, run)
|
||||||
|
self.assertEqual(a[1], ex)
|
||||||
|
|
||||||
|
|
||||||
|
def make_exception(cls, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
raise cls(*args, **kwargs)
|
||||||
|
except cls:
|
||||||
|
return sys.exc_info()
|
||||||
|
Loading…
Reference in New Issue
Block a user