Add validation of base exception type(s) in failure type
All python exceptions should originate from either base exception or exception, so in the failure validation logic check and verify that these are the root exception type names that have been captured and raise and invalid format error if anything else is found. Change-Id: I8ba1b68799703d6a857c51867d9e6cb0f884397c
This commit is contained in:
committed by
Thomas Goirand
parent
1af72e18e5
commit
a8641d51be
@@ -168,6 +168,19 @@ class FailureObjectTestCase(test.TestCase):
|
||||
f2 = failure.Failure.from_dict(d_f)
|
||||
self.assertTrue(f.matches(f2))
|
||||
|
||||
def test_bad_root_exception(self):
|
||||
f = _captured_failure('Woot!')
|
||||
d_f = f.to_dict()
|
||||
d_f['exc_type_names'] = ['Junk']
|
||||
self.assertRaises(exceptions.InvalidFormat,
|
||||
failure.Failure.validate, d_f)
|
||||
|
||||
def test_valid_from_dict_to_dict_2(self):
|
||||
f = _captured_failure('Woot!')
|
||||
d_f = f.to_dict()
|
||||
d_f['exc_type_names'] = ['RuntimeError', 'Exception', 'BaseException']
|
||||
failure.Failure.validate(d_f)
|
||||
|
||||
def test_dont_catch_base_exception(self):
|
||||
try:
|
||||
raise SystemExit()
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
@@ -28,6 +29,7 @@ from taskflow.utils import iter_utils
|
||||
from taskflow.utils import mixins
|
||||
from taskflow.utils import schema_utils as su
|
||||
|
||||
|
||||
_exception_message = encodeutils.exception_to_unicode
|
||||
|
||||
|
||||
@@ -121,6 +123,13 @@ class Failure(mixins.StrMixin):
|
||||
"""
|
||||
DICT_VERSION = 1
|
||||
|
||||
BASE_EXCEPTIONS = ('BaseException', 'Exception')
|
||||
"""
|
||||
Root exceptions of all other python exceptions.
|
||||
|
||||
See: https://docs.python.org/2/library/exceptions.html
|
||||
"""
|
||||
|
||||
#: Expected failure schema (in json schema format).
|
||||
SCHEMA = {
|
||||
"$ref": "#/definitions/cause",
|
||||
@@ -206,11 +215,29 @@ class Failure(mixins.StrMixin):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data):
|
||||
"""Validate input data matches expected failure ``dict`` format."""
|
||||
try:
|
||||
su.schema_validate(data, cls.SCHEMA)
|
||||
except su.ValidationError as e:
|
||||
raise exc.InvalidFormat("Failure data not of the"
|
||||
" expected format: %s" % (e.message), e)
|
||||
else:
|
||||
# Ensure that all 'exc_type_names' originate from one of
|
||||
# BASE_EXCEPTIONS, because those are the root exceptions that
|
||||
# python mandates/provides and anything else is invalid...
|
||||
causes = collections.deque([data])
|
||||
while causes:
|
||||
cause = causes.popleft()
|
||||
root_exc_type = cause['exc_type_names'][-1]
|
||||
if root_exc_type not in cls.BASE_EXCEPTIONS:
|
||||
raise exc.InvalidFormat(
|
||||
"Failure data 'exc_type_names' must"
|
||||
" have an initial exception type that is one"
|
||||
" of %s types: '%s' is not one of those"
|
||||
" types" % (cls.BASE_EXCEPTIONS, root_exc_type))
|
||||
sub_causes = cause.get('causes')
|
||||
if sub_causes:
|
||||
causes.extend(sub_causes)
|
||||
|
||||
def _matches(self, other):
|
||||
if self is other:
|
||||
|
||||
Reference in New Issue
Block a user