Handle objects with broken __unicode__
Because Python 2 objects may have __unicode__ broken, and we need unicode output to avoid implicit decodes, explicitly handle __unicode__.
This commit is contained in:
parent
7ec70e3de3
commit
652c95d267
|
@ -6,7 +6,12 @@ Profit.
|
|||
|
||||
Things to be aware of!
|
||||
|
||||
In Python 2.x, unlike traceback, traceback2 creates unicode output.
|
||||
In Python 2.x, unlike traceback, traceback2 creates unicode output (because it
|
||||
depends on the linecache2 module).
|
||||
|
||||
Exception frame clearing silently does nothing if the interpreter in use does
|
||||
not support it.
|
||||
|
||||
traceback2._some_str, which while not an official API is so old its likely in
|
||||
use behaves similarly to the Python3 version - objects where unicode(obj) fails
|
||||
but str(object) works will be shown as b'thestrvaluerepr'.
|
||||
|
|
|
@ -4,7 +4,7 @@ import sys
|
|||
import operator
|
||||
|
||||
import linecache2 as linecache
|
||||
from six import u
|
||||
from six import u, PY2
|
||||
|
||||
__all__ = ['extract_stack', 'extract_tb', 'format_exception',
|
||||
'format_exception_only', 'format_list', 'format_stack',
|
||||
|
@ -141,14 +141,24 @@ def format_exception_only(etype, value):
|
|||
def _format_final_exc_line(etype, value):
|
||||
valuestr = _some_str(value)
|
||||
if value == 'None' or value is None or not valuestr:
|
||||
line = "%s\n" % etype
|
||||
line = u("%s\n") % etype
|
||||
else:
|
||||
line = "%s: %s\n" % (etype, valuestr)
|
||||
line = u("%s: %s\n") % (etype, valuestr)
|
||||
return line
|
||||
|
||||
def _some_str(value):
|
||||
try:
|
||||
return str(value)
|
||||
if PY2:
|
||||
# If there is a working __unicode__, great.
|
||||
# Otherwise see if we can get a bytestring...
|
||||
# Otherwise we fallback to unprintable.
|
||||
try:
|
||||
return unicode(value)
|
||||
except:
|
||||
return "b%s" % repr(str(value))
|
||||
else:
|
||||
# For Python3, bytestrings don't implicit decode, so its trivial.
|
||||
return str(value)
|
||||
except:
|
||||
return '<unprintable %s object>' % type(value).__name__
|
||||
|
||||
|
@ -508,7 +518,7 @@ class TracebackException:
|
|||
|
||||
stype = getattr(self.exc_type, '__qualname__', self.exc_type.__name__)
|
||||
smod = u(self.exc_type.__module__)
|
||||
if smod not in ("__main__", "builtins"):
|
||||
if smod not in ("__main__", "builtins", "exceptions"):
|
||||
stype = smod + u('.') + stype
|
||||
|
||||
if not issubclass(self.exc_type, SyntaxError):
|
||||
|
|
|
@ -12,7 +12,7 @@ import contextlib2 as contextlib
|
|||
import fixtures
|
||||
import linecache2 as linecache
|
||||
import six
|
||||
from six import text_type, u
|
||||
from six import b, text_type, u
|
||||
try:
|
||||
from six import raise_from
|
||||
except ImportError:
|
||||
|
@ -153,6 +153,14 @@ class SyntaxTracebackCases(testtools.TestCase):
|
|||
str_name = '.'.join([X.__module__, qualname(X)])
|
||||
self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
|
||||
|
||||
def test_format_exception_only_undecodable__str__(self):
|
||||
# This won't decode via the ascii codec.
|
||||
X = Exception(u('\u5341').encode('shift-jis'))
|
||||
err = traceback.format_exception_only(type(X), X)
|
||||
self.assertEqual(len(err), 1)
|
||||
str_value = "b'\\x8f\\\\'"
|
||||
self.assertEqual(err[0], "Exception: %s\n" % str_value)
|
||||
|
||||
def test_without_exception(self):
|
||||
err = traceback.format_exception_only(None, None)
|
||||
self.assertEqual(err, ['None\n'])
|
||||
|
@ -619,7 +627,7 @@ class TestStack(unittest.TestCase):
|
|||
traceback.walk_stack(None), capture_locals=True, limit=1)
|
||||
s = some_inner(3, 4)
|
||||
self.assertEqual(
|
||||
[' File "' + FNAME + '", line 619, '
|
||||
[' File "' + FNAME + '", line 627, '
|
||||
'in some_inner\n'
|
||||
' traceback.walk_stack(None), capture_locals=True, limit=1)\n'
|
||||
' a = 1\n'
|
||||
|
|
Loading…
Reference in New Issue