Python2.xify the code base.
Skip a few tests that test the core interpreter and don't work on <3. Unicodify the core. Fix tests to deal with Python 2's behaviour (stdout not encoded at all) when stdout is a pipe.
This commit is contained in:
parent
43118d83bc
commit
9a9a29e95f
|
@ -3,3 +3,10 @@ A backport of traceback to older supported Pythons.
|
||||||
>>> import traceback2 as traceback
|
>>> import traceback2 as traceback
|
||||||
|
|
||||||
Profit.
|
Profit.
|
||||||
|
|
||||||
|
Things to be aware of!
|
||||||
|
|
||||||
|
In Python 2.x, unlike traceback, traceback2 creates unicode output.
|
||||||
|
|
||||||
|
Exception frame clearing silently does nothing if the interpreter in use does
|
||||||
|
not support it.
|
||||||
|
|
|
@ -4,6 +4,8 @@ import linecache
|
||||||
import sys
|
import sys
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
|
from six import u
|
||||||
|
|
||||||
__all__ = ['extract_stack', 'extract_tb', 'format_exception',
|
__all__ = ['extract_stack', 'extract_tb', 'format_exception',
|
||||||
'format_exception_only', 'format_list', 'format_stack',
|
'format_exception_only', 'format_list', 'format_stack',
|
||||||
'format_tb', 'print_exc', 'format_exc', 'print_exception',
|
'format_tb', 'print_exc', 'format_exc', 'print_exception',
|
||||||
|
@ -16,9 +18,9 @@ __all__ = ['extract_stack', 'extract_tb', 'format_exception',
|
||||||
|
|
||||||
def _format_list_iter(extracted_list):
|
def _format_list_iter(extracted_list):
|
||||||
for filename, lineno, name, line in extracted_list:
|
for filename, lineno, name, line in extracted_list:
|
||||||
item = ' File "{0}", line {1}, in {2}\n'.format(filename, lineno, name)
|
item = u(' File "{0}", line {1}, in {2}\n').format(filename, lineno, name)
|
||||||
if line:
|
if line:
|
||||||
item = item + ' {0}\n'.format(line.strip())
|
item = item + u(' {0}\n').format(line.strip())
|
||||||
yield item
|
yield item
|
||||||
|
|
||||||
def print_list(extracted_list, file=None):
|
def print_list(extracted_list, file=None):
|
||||||
|
@ -147,10 +149,10 @@ def _format_exception_iter(etype, value, tb, limit, chain):
|
||||||
for value, tb in values:
|
for value, tb in values:
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
# This is a cause/context message line
|
# This is a cause/context message line
|
||||||
yield value + '\n'
|
yield value + u('\n')
|
||||||
continue
|
continue
|
||||||
if tb:
|
if tb:
|
||||||
yield 'Traceback (most recent call last):\n'
|
yield u('Traceback (most recent call last):\n')
|
||||||
for it in _format_list_iter(_extract_tb_iter(tb, limit=limit)):
|
for it in _format_list_iter(_extract_tb_iter(tb, limit=limit)):
|
||||||
yield it
|
yield it
|
||||||
for it in _format_exception_only_iter(type(value), value):
|
for it in _format_exception_only_iter(type(value), value):
|
||||||
|
@ -209,32 +211,32 @@ def _format_exception_only_iter(etype, value):
|
||||||
return
|
return
|
||||||
|
|
||||||
stype = getattr(etype, '__qualname__', etype.__name__)
|
stype = getattr(etype, '__qualname__', etype.__name__)
|
||||||
smod = etype.__module__
|
smod = u(etype.__module__)
|
||||||
if smod not in ("__main__", "builtins"):
|
if smod not in ("__main__", "builtins"):
|
||||||
stype = smod + '.' + stype
|
stype = smod + u('.') + stype
|
||||||
|
|
||||||
if not issubclass(etype, SyntaxError):
|
if not issubclass(etype, SyntaxError):
|
||||||
yield _format_final_exc_line(stype, value)
|
yield _format_final_exc_line(stype, value)
|
||||||
return
|
return
|
||||||
|
|
||||||
# It was a syntax error; show exactly where the problem was found.
|
# It was a syntax error; show exactly where the problem was found.
|
||||||
filename = value.filename or "<string>"
|
filename = value.filename or u("<string>")
|
||||||
lineno = str(value.lineno) or '?'
|
lineno = str(value.lineno) or u('?')
|
||||||
yield ' File "{0}", line {1}\n'.format(filename, lineno)
|
yield u(' File "{0}", line {1}\n').format(filename, lineno)
|
||||||
|
|
||||||
badline = value.text
|
badline = value.text
|
||||||
offset = value.offset
|
offset = value.offset
|
||||||
if badline is not None:
|
if badline is not None:
|
||||||
yield ' {0}\n'.format(badline.strip())
|
yield u(' {0}\n').format(u(badline).strip())
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
caretspace = badline.rstrip('\n')
|
caretspace = u(badline).rstrip('\n')
|
||||||
offset = min(len(caretspace), offset) - 1
|
offset = min(len(caretspace), offset) - 1
|
||||||
caretspace = caretspace[:offset].lstrip()
|
caretspace = caretspace[:offset].lstrip()
|
||||||
# non-space whitespace (likes tabs) must be kept for alignment
|
# non-space whitespace (likes tabs) must be kept for alignment
|
||||||
caretspace = ((c.isspace() and c or ' ') for c in caretspace)
|
caretspace = ((c.isspace() and c or ' ') for c in caretspace)
|
||||||
yield ' {0}^\n'.format(''.join(caretspace))
|
yield u(' {0}^\n'.format(''.join(caretspace)))
|
||||||
msg = value.msg or "<no detail available>"
|
msg = value.msg or "<no detail available>"
|
||||||
yield "{0}: {1}\n".format(stype, msg)
|
yield u("{0}: {1}\n").format(stype, msg)
|
||||||
|
|
||||||
def _format_final_exc_line(etype, value):
|
def _format_final_exc_line(etype, value):
|
||||||
valuestr = _some_str(value)
|
valuestr = _some_str(value)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Test cases for traceback module"""
|
"""Test cases for traceback module"""
|
||||||
|
|
||||||
import doctest
|
import doctest
|
||||||
|
import io
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
@ -8,6 +9,7 @@ import re
|
||||||
import contextlib2 as contextlib
|
import contextlib2 as contextlib
|
||||||
import fixtures
|
import fixtures
|
||||||
import six
|
import six
|
||||||
|
from six import text_type, u
|
||||||
try:
|
try:
|
||||||
from six import raise_from
|
from six import raise_from
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -144,21 +146,23 @@ class SyntaxTracebackCases(testtools.TestCase):
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT)
|
||||||
stdout, stderr = process.communicate()
|
stdout, stderr = process.communicate()
|
||||||
output_encoding = str(stdout, 'ascii').splitlines()[0]
|
output_encoding = text_type(stdout, 'ascii').splitlines()[0]
|
||||||
|
|
||||||
def do_test(firstlines, message, charset, lineno):
|
def do_test(firstlines, message, charset, lineno, output_encoding):
|
||||||
# Raise the message in a subprocess, and catch the output
|
# Raise the message in a subprocess, and catch the output
|
||||||
with fixtures.TempDir() as d:
|
with fixtures.TempDir() as d:
|
||||||
TESTFN = d.path + '/fname'
|
TESTFN = d.path + '/fname'
|
||||||
output = open(TESTFN, "w", encoding=charset)
|
output = io.open(TESTFN, "w", encoding=charset)
|
||||||
output.write("""{0}if 1:
|
output.write(u("""{0}if 1:
|
||||||
import traceback;
|
import traceback;
|
||||||
raise RuntimeError('{1}')
|
raise RuntimeError('{1}')
|
||||||
""".format(firstlines, message))
|
""").format(firstlines, message))
|
||||||
output.close()
|
output.close()
|
||||||
process = subprocess.Popen([sys.executable, TESTFN],
|
process = subprocess.Popen([sys.executable, TESTFN],
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
stdout, stderr = process.communicate()
|
stdout, stderr = process.communicate()
|
||||||
|
if output_encoding == 'None':
|
||||||
|
output_encoding = charset
|
||||||
stdout = stdout.decode(output_encoding).splitlines()
|
stdout = stdout.decode(output_encoding).splitlines()
|
||||||
|
|
||||||
# The source lines are encoded with the 'backslashreplace' handler
|
# The source lines are encoded with the 'backslashreplace' handler
|
||||||
|
@ -167,8 +171,8 @@ class SyntaxTracebackCases(testtools.TestCase):
|
||||||
# and we just decoded them with the output_encoding.
|
# and we just decoded them with the output_encoding.
|
||||||
message_ascii = encoded_message.decode(output_encoding)
|
message_ascii = encoded_message.decode(output_encoding)
|
||||||
|
|
||||||
err_line = "raise RuntimeError('{0}')".format(message_ascii)
|
err_line = u("raise RuntimeError('{0}')").format(message_ascii)
|
||||||
err_msg = "RuntimeError: {0}".format(message_ascii)
|
err_msg = u("RuntimeError: {0}").format(message_ascii)
|
||||||
|
|
||||||
self.assertIn(("line %s" % lineno), stdout[1],
|
self.assertIn(("line %s" % lineno), stdout[1],
|
||||||
"Invalid line number: {0!r} instead of {1}".format(
|
"Invalid line number: {0!r} instead of {1}".format(
|
||||||
|
@ -180,24 +184,26 @@ class SyntaxTracebackCases(testtools.TestCase):
|
||||||
"Invalid error message: {0!r} instead of {1!r}".format(
|
"Invalid error message: {0!r} instead of {1!r}".format(
|
||||||
stdout[3], err_msg))
|
stdout[3], err_msg))
|
||||||
|
|
||||||
do_test("", "foo", "ascii", 3)
|
do_test("", "foo", "ascii", 3, output_encoding)
|
||||||
for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
|
for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
|
||||||
if charset == "ascii":
|
if charset == "ascii":
|
||||||
text = "foo"
|
text = u("foo")
|
||||||
elif charset == "GBK":
|
elif charset == "GBK":
|
||||||
text = "\u4E02\u5100"
|
text = u("\u4E02\u5100")
|
||||||
else:
|
else:
|
||||||
text = "h\xe9 ho"
|
text = u("h\xe9 ho")
|
||||||
do_test("# coding: {0}\n".format(charset),
|
do_test("# coding: {0}\n".format(charset),
|
||||||
text, charset, 4)
|
text, charset, 4, output_encoding)
|
||||||
do_test("#!shebang\n# coding: {0}\n".format(charset),
|
do_test("#!shebang\n# coding: {0}\n".format(charset),
|
||||||
text, charset, 5)
|
text, charset, 5, output_encoding)
|
||||||
do_test(" \t\f\n# coding: {0}\n".format(charset),
|
do_test(" \t\f\n# coding: {0}\n".format(charset),
|
||||||
text, charset, 5)
|
text, charset, 5, output_encoding)
|
||||||
# Issue #18960: coding spec should has no effect
|
# Issue #18960: coding spec should has no effect
|
||||||
# (Fixed in 3.4)
|
# (Fixed in 3.4)
|
||||||
if sys.version_info[:2] > (3, 3):
|
if sys.version_info[:2] > (3, 3):
|
||||||
do_test("0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
|
do_test(
|
||||||
|
"0\n# coding: GBK\n", u("h\xe9 ho"), 'utf-8', 5,
|
||||||
|
output_encoding)
|
||||||
|
|
||||||
|
|
||||||
class TracebackFormatTests(unittest.TestCase):
|
class TracebackFormatTests(unittest.TestCase):
|
||||||
|
@ -207,6 +213,14 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
|
|
||||||
def check_traceback_format(self, cleanup_func=None):
|
def check_traceback_format(self, cleanup_func=None):
|
||||||
try:
|
try:
|
||||||
|
if issubclass(six.binary_type, six.string_types):
|
||||||
|
# Python 2.6 or other platform where the interpreter
|
||||||
|
# is likely going to be spitting out bytes, which will
|
||||||
|
# then fail with io.StringIO(), so we skip the cross-
|
||||||
|
# checks with the C API there. Note that _testcapi
|
||||||
|
# is included in (at least) Ubuntu CPython packages, which
|
||||||
|
# makes the import check less effective than desired.
|
||||||
|
raise ImportError
|
||||||
from _testcapi import traceback_print
|
from _testcapi import traceback_print
|
||||||
except ImportError:
|
except ImportError:
|
||||||
traceback_print = None
|
traceback_print = None
|
||||||
|
@ -217,8 +231,8 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
if cleanup_func is not None:
|
if cleanup_func is not None:
|
||||||
# Clear the inner frames, not this one
|
# Clear the inner frames, not this one
|
||||||
cleanup_func(tb.tb_next)
|
cleanup_func(tb.tb_next)
|
||||||
traceback_fmt = 'Traceback (most recent call last):\n' + \
|
traceback_fmt = u('Traceback (most recent call last):\n') + \
|
||||||
''.join(traceback.format_tb(tb))
|
u('').join(traceback.format_tb(tb))
|
||||||
if traceback_print is not None:
|
if traceback_print is not None:
|
||||||
file_ = StringIO()
|
file_ = StringIO()
|
||||||
traceback_print(tb, file_)
|
traceback_print(tb, file_)
|
||||||
|
@ -323,6 +337,7 @@ class BaseExceptionReportingTests:
|
||||||
# < 3 show as exceptions.ZeroDivisionError.
|
# < 3 show as exceptions.ZeroDivisionError.
|
||||||
self.assertIn('ZeroDivisionError', lines[3])
|
self.assertIn('ZeroDivisionError', lines[3])
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.version_info[:2] < (3, 2), "Only applies to 3.2+")
|
||||||
def test_cause(self):
|
def test_cause(self):
|
||||||
def inner_raise():
|
def inner_raise():
|
||||||
try:
|
try:
|
||||||
|
@ -337,6 +352,7 @@ class BaseExceptionReportingTests:
|
||||||
self.check_zero_div(blocks[0])
|
self.check_zero_div(blocks[0])
|
||||||
self.assertIn('inner_raise() # Marker', blocks[2])
|
self.assertIn('inner_raise() # Marker', blocks[2])
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.version_info[:2] < (3, 2), "Only applies to 3.2+")
|
||||||
def test_context(self):
|
def test_context(self):
|
||||||
def inner_raise():
|
def inner_raise():
|
||||||
try:
|
try:
|
||||||
|
@ -369,6 +385,7 @@ Traceback (most recent call last):
|
||||||
ZeroDivisionError
|
ZeroDivisionError
|
||||||
""", doctest.ELLIPSIS))
|
""", doctest.ELLIPSIS))
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.version_info[:2] < (3, 2), "Only applies to 3.2+")
|
||||||
def test_cause_and_context(self):
|
def test_cause_and_context(self):
|
||||||
# When both a cause and a context are set, only the cause should be
|
# When both a cause and a context are set, only the cause should be
|
||||||
# displayed and the context should be muted.
|
# displayed and the context should be muted.
|
||||||
|
@ -389,6 +406,7 @@ ZeroDivisionError
|
||||||
self.check_zero_div(blocks[0])
|
self.check_zero_div(blocks[0])
|
||||||
self.assertIn('inner_raise() # Marker', blocks[2])
|
self.assertIn('inner_raise() # Marker', blocks[2])
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.version_info[:2] < (3, 2), "Only applies to 3.2+")
|
||||||
def test_cause_recursive(self):
|
def test_cause_recursive(self):
|
||||||
def inner_raise():
|
def inner_raise():
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue