Use encodeutils for exception -> string function
The oslo.utils library now provides a better version of this that always returns a unicode exception message, so update our usage to use it (and remove our own local function). This guarantee of unicode also means we have to update a few other places to make sure we get back bytes or unicode as needed. Change-Id: I924380408aaf6d2aec418ceaaf623c75900268f7
This commit is contained in:
parent
27baaf46ad
commit
0d884a2fc5
|
@ -38,6 +38,11 @@ Miscellaneous
|
|||
|
||||
.. automodule:: taskflow.utils.misc
|
||||
|
||||
Mixins
|
||||
~~~~~~
|
||||
|
||||
.. automodule:: taskflow.utils.mixins
|
||||
|
||||
Persistence
|
||||
~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import traceback
|
|||
from oslo_utils import excutils
|
||||
from oslo_utils import reflection
|
||||
import six
|
||||
from taskflow.utils import mixins
|
||||
|
||||
|
||||
def raise_with_cause(exc_cls, message, *args, **kwargs):
|
||||
|
@ -231,7 +232,7 @@ class NotImplementedError(NotImplementedError):
|
|||
"""
|
||||
|
||||
|
||||
class WrappedFailure(Exception):
|
||||
class WrappedFailure(mixins.StrMixin, Exception):
|
||||
"""Wraps one or several failure objects.
|
||||
|
||||
When exception/s cannot be re-raised (for example, because the value and
|
||||
|
@ -284,20 +285,18 @@ class WrappedFailure(Exception):
|
|||
return result
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
causes = [exception_message(cause) for cause in self._causes]
|
||||
return 'WrappedFailure: %s' % causes
|
||||
def __bytes__(self):
|
||||
buf = six.BytesIO()
|
||||
buf.write(b'WrappedFailure: [')
|
||||
causes_gen = (six.binary_type(cause) for cause in self._causes)
|
||||
buf.write(b", ".join(causes_gen))
|
||||
buf.write(b']')
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def exception_message(exc):
|
||||
"""Return the string representation of exception.
|
||||
|
||||
:param exc: exception object to get a string representation of.
|
||||
"""
|
||||
# NOTE(imelnikov): Dealing with non-ascii data in python is difficult:
|
||||
# https://bugs.launchpad.net/taskflow/+bug/1275895
|
||||
# https://bugs.launchpad.net/taskflow/+bug/1276053
|
||||
try:
|
||||
return six.text_type(exc)
|
||||
except UnicodeError:
|
||||
return str(exc)
|
||||
def __unicode__(self):
|
||||
buf = six.StringIO()
|
||||
buf.write(u'WrappedFailure: [')
|
||||
causes_gen = (six.text_type(cause) for cause in self._causes)
|
||||
buf.write(u", ".join(causes_gen))
|
||||
buf.write(u']')
|
||||
return buf.getvalue()
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import sys
|
||||
|
||||
from oslo_utils import encodeutils
|
||||
import six
|
||||
from six.moves import cPickle as pickle
|
||||
import testtools
|
||||
|
@ -301,9 +302,19 @@ class NonAsciiExceptionsTestCase(test.TestCase):
|
|||
|
||||
def test_exception_with_non_ascii_str(self):
|
||||
bad_string = chr(200)
|
||||
fail = failure.Failure.from_exception(ValueError(bad_string))
|
||||
self.assertEqual(fail.exception_str, bad_string)
|
||||
self.assertEqual(str(fail), 'Failure: ValueError: %s' % bad_string)
|
||||
excp = ValueError(bad_string)
|
||||
fail = failure.Failure.from_exception(excp)
|
||||
self.assertEqual(fail.exception_str,
|
||||
encodeutils.exception_to_unicode(excp))
|
||||
# This is slightly different on py2 vs py3... due to how
|
||||
# __str__ or __unicode__ is called and what is expected from
|
||||
# both...
|
||||
if six.PY2:
|
||||
msg = encodeutils.exception_to_unicode(excp)
|
||||
expected = 'Failure: ValueError: %s' % msg.encode('utf-8')
|
||||
else:
|
||||
expected = u'Failure: ValueError: \xc8'
|
||||
self.assertEqual(str(fail), expected)
|
||||
|
||||
def test_exception_non_ascii_unicode(self):
|
||||
hi_ru = u'привет'
|
||||
|
@ -316,18 +327,11 @@ class NonAsciiExceptionsTestCase(test.TestCase):
|
|||
def test_wrapped_failure_non_ascii_unicode(self):
|
||||
hi_cn = u'嗨'
|
||||
fail = ValueError(hi_cn)
|
||||
self.assertEqual(hi_cn, exceptions.exception_message(fail))
|
||||
self.assertEqual(hi_cn, encodeutils.exception_to_unicode(fail))
|
||||
fail = failure.Failure.from_exception(fail)
|
||||
wrapped_fail = exceptions.WrappedFailure([fail])
|
||||
if six.PY2:
|
||||
# Python 2.x will unicode escape it, while python 3.3+ will not,
|
||||
# so we sadly have to differentiate between these two...
|
||||
expected_result = (u"WrappedFailure: "
|
||||
"[u'Failure: ValueError: %s']"
|
||||
% (hi_cn.encode("unicode-escape")))
|
||||
else:
|
||||
expected_result = (u"WrappedFailure: "
|
||||
"['Failure: ValueError: %s']" % (hi_cn))
|
||||
expected_result = (u"WrappedFailure: "
|
||||
"[Failure: ValueError: %s]" % (hi_cn))
|
||||
self.assertEqual(expected_result, six.text_type(wrapped_fail))
|
||||
|
||||
def test_failure_equality_with_non_ascii_str(self):
|
||||
|
|
|
@ -19,12 +19,16 @@ import os
|
|||
import sys
|
||||
import traceback
|
||||
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import reflection
|
||||
import six
|
||||
|
||||
from taskflow import exceptions as exc
|
||||
from taskflow.utils import mixins
|
||||
from taskflow.utils import schema_utils as su
|
||||
|
||||
_exception_message = encodeutils.exception_to_unicode
|
||||
|
||||
|
||||
def _copy_exc_info(exc_info):
|
||||
if exc_info is None:
|
||||
|
@ -65,7 +69,7 @@ def _are_equal_exc_info_tuples(ei1, ei2):
|
|||
if ei1[0] is not ei2[0]:
|
||||
return False
|
||||
if not all((type(ei1[1]) == type(ei2[1]),
|
||||
exc.exception_message(ei1[1]) == exc.exception_message(ei2[1]),
|
||||
_exception_message(ei1[1]) == _exception_message(ei2[1]),
|
||||
repr(ei1[1]) == repr(ei2[1]))):
|
||||
return False
|
||||
if ei1[2] == ei2[2]:
|
||||
|
@ -75,7 +79,7 @@ def _are_equal_exc_info_tuples(ei1, ei2):
|
|||
return tb1 == tb2
|
||||
|
||||
|
||||
class Failure(object):
|
||||
class Failure(mixins.StrMixin):
|
||||
"""An immutable object that represents failure.
|
||||
|
||||
Failure objects encapsulate exception information so that they can be
|
||||
|
@ -191,7 +195,7 @@ class Failure(object):
|
|||
if not self._exc_type_names:
|
||||
raise TypeError("Invalid exception type '%s' (%s)"
|
||||
% (exc_info[0], type(exc_info[0])))
|
||||
self._exception_str = exc.exception_message(self._exc_info[1])
|
||||
self._exception_str = _exception_message(self._exc_info[1])
|
||||
self._traceback_str = ''.join(
|
||||
traceback.format_tb(self._exc_info[2]))
|
||||
self._causes = kwargs.pop('causes', None)
|
||||
|
@ -387,7 +391,7 @@ class Failure(object):
|
|||
self._causes = tuple(self._extract_causes_iter(self.exception))
|
||||
return self._causes
|
||||
|
||||
def __str__(self):
|
||||
def __unicode__(self):
|
||||
return self.pformat()
|
||||
|
||||
def pformat(self, traceback=False):
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2015 Yahoo! Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class StrMixin(object):
|
||||
"""Mixin that helps deal with the PY2 and PY3 method differences.
|
||||
|
||||
http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/ explains
|
||||
why this is quite useful...
|
||||
"""
|
||||
|
||||
if six.PY2:
|
||||
def __str__(self):
|
||||
try:
|
||||
return self.__bytes__()
|
||||
except AttributeError:
|
||||
return self.__unicode__().encode('utf-8')
|
||||
else:
|
||||
def __str__(self):
|
||||
return self.__unicode__()
|
Loading…
Reference in New Issue