diff --git a/oslo_utils/encodeutils.py b/oslo_utils/encodeutils.py index 7d8af13b..52da98aa 100644 --- a/oslo_utils/encodeutils.py +++ b/oslo_utils/encodeutils.py @@ -107,13 +107,23 @@ def exception_to_unicode(exc): """ msg = None if six.PY2: - # Don't call directly unicode(exc), because it fails with - # UnicodeDecodeError on Python 2 if exc.__unicode__() returns a bytes - # string not decodable from the default encoding (ASCII) + # First try by calling the unicode type constructor. We should try + # unicode() before exc.__unicode__() because subclasses of unicode can + # be easily casted to unicode, whereas they have no __unicode__() + # method. try: - msg = exc.__unicode__() + msg = unicode(exc) except UnicodeError: - pass + # unicode(exc) fail with UnicodeDecodeError on Python 2 if + # exc.__unicode__() or exc.__str__() returns a bytes string not + # decodable from the default encoding (ASCII) + if hasattr(exc, '__unicode__'): + # Call directly the __unicode__() method to avoid + # the implicit decoding from the default encoding + try: + msg = exc.__unicode__() + except UnicodeError: + pass if msg is None: # Don't call directly str(exc), because it fails with diff --git a/oslo_utils/tests/tests_encodeutils.py b/oslo_utils/tests/tests_encodeutils.py index d0f696ed..84cbd49d 100644 --- a/oslo_utils/tests/tests_encodeutils.py +++ b/oslo_utils/tests/tests_encodeutils.py @@ -20,6 +20,7 @@ from oslotest import base as test_base import six import testtools +import oslo_i18n.fixture from oslo_utils import encodeutils @@ -231,3 +232,9 @@ class ExceptionToUnicodeTest(test_base.BaseTestCase): exc = UnicodeOnlyException(b'utf-8 \xc3\xa9\xe2\x82\xac') self.assertEqual(encodeutils.exception_to_unicode(exc), u'utf-8 \xe9\u20ac') + + def test_oslo_i18n_message(self): + # use the lazy translation to get a Message instance of oslo_i18n + exc = oslo_i18n.fixture.Translation().lazy("test") + self.assertEqual(encodeutils.exception_to_unicode(exc), + u"test")