mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Fix traceback (#6569)
* Fix traceback * Update traceback from CPython 3.13.11 * unmark test_traceback * fix code * fix debug range * fix tests
This commit is contained in:
1 parent
6bd1d90b6a
commit
e79a1a1a66
16 files changed
+1200
-305
No files matched your search
@@ -178,6 +178,7 @@ PYTHONHASHSEED
|
||||
PYTHONHOME
|
||||
PYTHONINSPECT
|
||||
PYTHONINTMAXSTRDIGITS
|
||||
PYTHONNODEBUGRANGES
|
||||
PYTHONNOUSERSITE
|
||||
PYTHONOPTIMIZE
|
||||
PYTHONPATH
|
||||
|
||||
Vendored
-4
@@ -425,8 +425,6 @@ class CodeTest(unittest.TestCase):
|
||||
]
|
||||
)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_endline_and_columntable_none_when_no_debug_ranges(self):
|
||||
# Make sure that if `-X no_debug_ranges` is used, there is
|
||||
# minimal debug info
|
||||
@@ -442,8 +440,6 @@ class CodeTest(unittest.TestCase):
|
||||
""")
|
||||
assert_python_ok('-X', 'no_debug_ranges', '-c', code)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_endline_and_columntable_none_when_no_debug_ranges_env(self):
|
||||
# Same as above but using the environment variable opt out.
|
||||
code = textwrap.dedent("""
|
||||
|
||||
Vendored
-110
@@ -218,8 +218,6 @@ class TracebackCases(unittest.TestCase):
|
||||
lst = traceback.format_exception_only(e.__class__, e)
|
||||
self.assertEqual(lst, ['KeyboardInterrupt\n'])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_only_bad__str__(self):
|
||||
class X(Exception):
|
||||
def __str__(self):
|
||||
@@ -238,8 +236,6 @@ class TracebackCases(unittest.TestCase):
|
||||
err = traceback.format_exception_only(eg)
|
||||
self.assertEqual(err, ['ExceptionGroup: A (1 sub-exception)\n'])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group(self):
|
||||
eg = ExceptionGroup('A', [ValueError('B')])
|
||||
err = traceback.format_exception_only(eg, show_group=True)
|
||||
@@ -248,8 +244,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' ValueError: B\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_base_exception_group(self):
|
||||
eg = BaseExceptionGroup('A', [BaseException('B')])
|
||||
err = traceback.format_exception_only(eg, show_group=True)
|
||||
@@ -258,8 +252,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' BaseException: B\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_with_note(self):
|
||||
exc = ValueError('B')
|
||||
exc.add_note('Note')
|
||||
@@ -271,8 +263,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' Note\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_explicit_class(self):
|
||||
eg = ExceptionGroup('A', [ValueError('B')])
|
||||
err = traceback.format_exception_only(ExceptionGroup, eg, show_group=True)
|
||||
@@ -281,8 +271,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' ValueError: B\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_multiple_exceptions(self):
|
||||
eg = ExceptionGroup('A', [ValueError('B'), TypeError('C')])
|
||||
err = traceback.format_exception_only(eg, show_group=True)
|
||||
@@ -292,8 +280,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' TypeError: C\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_multiline_messages(self):
|
||||
eg = ExceptionGroup('A\n1', [ValueError('B\n2')])
|
||||
err = traceback.format_exception_only(eg, show_group=True)
|
||||
@@ -303,8 +289,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' 2\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_multiline2_messages(self):
|
||||
exc = ValueError('B\n\n2\n')
|
||||
exc.add_note('\nC\n\n3')
|
||||
@@ -323,8 +307,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' IndexError: D\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_syntax_error(self):
|
||||
exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
|
||||
eg = ExceptionGroup('A\n1', [exc])
|
||||
@@ -336,8 +318,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' SyntaxError: error\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_nested_with_notes(self):
|
||||
exc = IndexError('D')
|
||||
exc.add_note('Note\nmultiline')
|
||||
@@ -358,8 +338,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' TypeError: F\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_with_tracebacks(self):
|
||||
def f():
|
||||
try:
|
||||
@@ -385,8 +363,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' TypeError: g\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_with_cause(self):
|
||||
def f():
|
||||
try:
|
||||
@@ -404,8 +380,6 @@ class TracebackCases(unittest.TestCase):
|
||||
' ValueError: 0\n',
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_group_syntax_error_with_custom_values(self):
|
||||
# See https://github.com/python/cpython/issues/128894
|
||||
for exc in [
|
||||
@@ -550,16 +524,12 @@ class TracebackCases(unittest.TestCase):
|
||||
traceback.print_exception(Exception("projector"), file=output)
|
||||
self.assertEqual(output.getvalue(), "Exception: projector\n")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_print_last(self):
|
||||
with support.swap_attr(sys, 'last_exc', ValueError(42)):
|
||||
output = StringIO()
|
||||
traceback.print_last(file=output)
|
||||
self.assertEqual(output.getvalue(), "ValueError: 42\n")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_format_exception_exc(self):
|
||||
e = Exception("projector")
|
||||
output = traceback.format_exception(e)
|
||||
@@ -598,8 +568,6 @@ class TracebackCases(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
traceback.format_exception_only(None, None), [NONE_EXC_STRING])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_signatures(self):
|
||||
self.assertEqual(
|
||||
str(inspect.signature(traceback.print_exception)),
|
||||
@@ -2286,8 +2254,6 @@ class TracebackFormatMixin:
|
||||
'Exception expected for value, int found\n')
|
||||
)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_print_exception_bad_type_python(self):
|
||||
msg = "Exception expected for value, int found"
|
||||
with self.assertRaisesRegex(TypeError, msg):
|
||||
@@ -2366,8 +2332,6 @@ class BaseExceptionReportingTests:
|
||||
self.assertTrue(lines[1].startswith(' File'))
|
||||
self.assertIn('1/0 # Marker', lines[2])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_cause(self):
|
||||
def inner_raise():
|
||||
try:
|
||||
@@ -2501,8 +2465,6 @@ class BaseExceptionReportingTests:
|
||||
err = self.get_report(Exception(''))
|
||||
self.assertIn('Exception\n', err)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_syntax_error_various_offsets(self):
|
||||
for offset in range(-5, 10):
|
||||
for add in [0, 2]:
|
||||
@@ -2525,8 +2487,6 @@ class BaseExceptionReportingTests:
|
||||
exp = "\n".join(expected)
|
||||
self.assertEqual(exp, err)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_with_note(self):
|
||||
e = ValueError(123)
|
||||
vanilla = self.get_report(e)
|
||||
@@ -2545,8 +2505,6 @@ class BaseExceptionReportingTests:
|
||||
del e.__notes__
|
||||
self.assertEqual(self.get_report(e), vanilla)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_with_invalid_notes(self):
|
||||
e = ValueError(123)
|
||||
vanilla = self.get_report(e)
|
||||
@@ -2604,8 +2562,6 @@ class BaseExceptionReportingTests:
|
||||
self.get_report(e),
|
||||
vanilla + "Ignored error getting __notes__: ValueError('no __notes__')\n")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_with_multiple_notes(self):
|
||||
for e in [ValueError(42), SyntaxError('bad syntax')]:
|
||||
with self.subTest(e=e):
|
||||
@@ -2685,8 +2641,6 @@ class BaseExceptionReportingTests:
|
||||
exp = f'<unknown>.{X.__qualname__}: I am X\n'
|
||||
self.assertEqual(exp, err)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_bad__str__(self):
|
||||
class X(Exception):
|
||||
def __str__(self):
|
||||
@@ -2861,8 +2815,6 @@ class BaseExceptionReportingTests:
|
||||
report = self.get_report(exc)
|
||||
self.assertEqual(report, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_width_limit(self):
|
||||
excs = []
|
||||
for i in range(1000):
|
||||
@@ -2907,8 +2859,6 @@ class BaseExceptionReportingTests:
|
||||
report = self.get_report(eg)
|
||||
self.assertEqual(report, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_depth_limit(self):
|
||||
exc = TypeError('bad type')
|
||||
for i in range(1000):
|
||||
@@ -3371,8 +3321,6 @@ class TestFrame(unittest.TestCase):
|
||||
self.assertNotEqual(f, object())
|
||||
self.assertEqual(f, ALWAYS_EQ)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_lazy_lines(self):
|
||||
linecache.clearcache()
|
||||
f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
|
||||
@@ -3509,8 +3457,6 @@ class TestStack(unittest.TestCase):
|
||||
s.format(),
|
||||
[f'{__file__}:{some_inner.__code__.co_firstlineno + 1}'])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_dropping_frames(self):
|
||||
def f():
|
||||
1/0
|
||||
@@ -3611,13 +3557,9 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(expected_type_str, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_smoke_builtin(self):
|
||||
self.do_test_smoke(ValueError(42), 'ValueError')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_smoke_user_exception(self):
|
||||
class MyException(Exception):
|
||||
pass
|
||||
@@ -3630,8 +3572,6 @@ class TestTracebackException(unittest.TestCase):
|
||||
'test_smoke_user_exception.<locals>.MyException')
|
||||
self.do_test_smoke(MyException('bad things happened'), expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_from_exception(self):
|
||||
# Check all the parameters are accepted.
|
||||
def foo():
|
||||
@@ -3657,8 +3597,6 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_cause(self):
|
||||
try:
|
||||
try:
|
||||
@@ -3683,8 +3621,6 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_context(self):
|
||||
try:
|
||||
try:
|
||||
@@ -3734,8 +3670,6 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertIn(
|
||||
"RecursionError: maximum recursion depth exceeded", res[-1])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_compact_with_cause(self):
|
||||
try:
|
||||
try:
|
||||
@@ -3758,8 +3692,6 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_compact_no_cause(self):
|
||||
try:
|
||||
try:
|
||||
@@ -3782,8 +3714,6 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_no_save_exc_type(self):
|
||||
try:
|
||||
1/0
|
||||
@@ -3912,8 +3842,6 @@ class TestTracebackException(unittest.TestCase):
|
||||
linecache.updatecache('/foo.py', globals())
|
||||
self.assertEqual(exc.stack[0].line, "import sys")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_locals(self):
|
||||
linecache.updatecache('/foo.py', globals())
|
||||
e = Exception("uh oh")
|
||||
@@ -3962,8 +3890,6 @@ class TestTracebackException(unittest.TestCase):
|
||||
'ZeroDivisionError: division by zero',
|
||||
''])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_dont_swallow_cause_or_context_of_falsey_exception(self):
|
||||
# see gh-132308: Ensure that __cause__ or __context__ attributes of exceptions
|
||||
# that evaluate as falsey are included in the output. For falsey term,
|
||||
@@ -4034,8 +3960,6 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
|
||||
|
||||
self.assertEqual(formatted, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_format_exception_onlyi_recursive(self):
|
||||
teg = traceback.TracebackException.from_exception(self.eg)
|
||||
formatted = ''.join(teg.format_exception_only(show_group=True)).split('\n')
|
||||
@@ -4100,8 +4024,6 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
|
||||
|
||||
self.assertEqual(formatted, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_max_group_width(self):
|
||||
excs1 = []
|
||||
excs2 = []
|
||||
@@ -4140,8 +4062,6 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
|
||||
|
||||
self.assertEqual(formatted, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_max_group_depth(self):
|
||||
exc = TypeError('bad type')
|
||||
for i in range(3):
|
||||
@@ -4191,8 +4111,6 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
|
||||
self.assertNotEqual(exc, object())
|
||||
self.assertEqual(exc, ALWAYS_EQ)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_dont_swallow_subexceptions_of_falsey_exceptiongroup(self):
|
||||
# see gh-132308: Ensure that subexceptions of exception groups
|
||||
# that evaluate as falsey are displayed in the output. For falsey term,
|
||||
@@ -4230,8 +4148,6 @@ class SuggestionFormattingTestBase:
|
||||
)
|
||||
return result_lines[0]
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_getattr_suggestions(self):
|
||||
class Substitution:
|
||||
noise = more_noise = a = bc = None
|
||||
@@ -4275,8 +4191,6 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_suggestion(cls(), 'bluch')
|
||||
self.assertIn(suggestion, actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_getattr_suggestions_underscored(self):
|
||||
class A:
|
||||
bluch = None
|
||||
@@ -4330,8 +4244,6 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_suggestion(A(), 'bluch')
|
||||
self.assertNotIn("blech", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_getattr_suggestions_no_args(self):
|
||||
class A:
|
||||
blech = None
|
||||
@@ -4349,8 +4261,6 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_suggestion(A(), 'bluch')
|
||||
self.assertIn("blech", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_getattr_suggestions_invalid_args(self):
|
||||
class NonStringifyClass:
|
||||
__str__ = None
|
||||
@@ -4392,8 +4302,6 @@ class SuggestionFormattingTestBase:
|
||||
self.assertNotIn("blech", actual)
|
||||
self.assertNotIn("oh no!", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_attribute_error_with_non_string_candidates(self):
|
||||
class T:
|
||||
bluch = 1
|
||||
@@ -4567,8 +4475,6 @@ class SuggestionFormattingTestBase:
|
||||
)
|
||||
self.assertNotIn("?", result_lines[-1])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name_error_suggestions(self):
|
||||
def Substitution():
|
||||
noise = more_noise = a = bc = None
|
||||
@@ -4609,24 +4515,18 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_suggestion(func)
|
||||
self.assertIn(suggestion, actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name_error_suggestions_from_globals(self):
|
||||
def func():
|
||||
print(global_for_suggestio)
|
||||
actual = self.get_suggestion(func)
|
||||
self.assertIn("'global_for_suggestions'?", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name_error_suggestions_from_builtins(self):
|
||||
def func():
|
||||
print(ZeroDivisionErrrrr)
|
||||
actual = self.get_suggestion(func)
|
||||
self.assertIn("'ZeroDivisionError'?", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name_error_suggestions_from_builtins_when_builtins_is_module(self):
|
||||
def func():
|
||||
custom_globals = globals().copy()
|
||||
@@ -4635,8 +4535,6 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_suggestion(func)
|
||||
self.assertIn("'ZeroDivisionError'?", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name_error_suggestions_with_non_string_candidates(self):
|
||||
def func():
|
||||
abc = 1
|
||||
@@ -4785,8 +4683,6 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_suggestion(func)
|
||||
self.assertNotIn("blech", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name_error_with_instance(self):
|
||||
class A:
|
||||
def __init__(self):
|
||||
@@ -4843,8 +4739,6 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_suggestion(func)
|
||||
self.assertNotIn("something", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name_error_for_stdlib_modules(self):
|
||||
def func():
|
||||
stream = io.StringIO()
|
||||
@@ -4852,8 +4746,6 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_suggestion(func)
|
||||
self.assertIn("forget to import 'io'", actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name_error_for_private_stdlib_modules(self):
|
||||
def func():
|
||||
stream = _io.StringIO()
|
||||
@@ -4898,8 +4790,6 @@ class MiscTest(unittest.TestCase):
|
||||
expected.add(name)
|
||||
self.assertCountEqual(traceback.__all__, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_levenshtein_distance(self):
|
||||
# copied from _testinternalcapi.test_edit_cost
|
||||
# to also exercise the Python implementation
|
||||
|
||||
Vendored
+2
@@ -724,6 +724,8 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
|
||||
else:
|
||||
raise AssertionError("This ought to be impossible")
|
||||
|
||||
# TODO: RUSTPYTHON; empty caret lines from equal col/end_col
|
||||
@unittest.expectedFailure
|
||||
def testTraceback(self):
|
||||
files = {TESTMOD + ".py": (NOW, raise_src)}
|
||||
self.doTest(None, files, TESTMOD, call=self.doTraceback)
|
||||
|
||||
Vendored
+1070
-124
File diff suppressed because it is too large.
Load diff
@@ -115,11 +115,22 @@ enum DoneWithFuture {
|
||||
Yes,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CompileOpts {
|
||||
/// How optimized the bytecode output should be; any optimize > 0 does
|
||||
/// not emit assert statements
|
||||
pub optimize: u8,
|
||||
/// Include column info in bytecode (-X no_debug_ranges disables)
|
||||
pub debug_ranges: bool,
|
||||
}
|
||||
|
||||
impl Default for CompileOpts {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
optimize: 0,
|
||||
debug_ranges: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -859,7 +870,7 @@ impl Compiler {
|
||||
let pop = self.code_stack.pop();
|
||||
let stack_top = compiler_unwrap_option(self, pop);
|
||||
// No parent scope stack to maintain
|
||||
unwrap_internal(self, stack_top.finalize_code(self.opts.optimize))
|
||||
unwrap_internal(self, stack_top.finalize_code(&self.opts))
|
||||
}
|
||||
|
||||
/// Push a new fblock
|
||||
@@ -1486,7 +1497,9 @@ impl Compiler {
|
||||
..
|
||||
}) => self.compile_for(target, iter, body, orelse, *is_async)?,
|
||||
Stmt::Match(StmtMatch { subject, cases, .. }) => self.compile_match(subject, cases)?,
|
||||
Stmt::Raise(StmtRaise { exc, cause, .. }) => {
|
||||
Stmt::Raise(StmtRaise {
|
||||
exc, cause, range, ..
|
||||
}) => {
|
||||
let kind = match exc {
|
||||
Some(value) => {
|
||||
self.compile_expression(value)?;
|
||||
@@ -1500,6 +1513,7 @@ impl Compiler {
|
||||
}
|
||||
None => bytecode::RaiseKind::Reraise,
|
||||
};
|
||||
self.set_source_range(*range);
|
||||
emit!(self, Instruction::Raise { kind });
|
||||
}
|
||||
Stmt::Try(StmtTry {
|
||||
@@ -5639,17 +5653,15 @@ impl Compiler {
|
||||
// Low level helper functions:
|
||||
fn _emit(&mut self, instr: Instruction, arg: OpArg, target: BlockIdx) {
|
||||
let range = self.current_source_range;
|
||||
let location = self
|
||||
.source_file
|
||||
.to_source_code()
|
||||
.source_location(range.start(), PositionEncoding::Utf8);
|
||||
// TODO: insert source filename
|
||||
let source = self.source_file.to_source_code();
|
||||
let location = source.source_location(range.start(), PositionEncoding::Utf8);
|
||||
let end_location = source.source_location(range.end(), PositionEncoding::Utf8);
|
||||
self.current_block().instructions.push(ir::InstructionInfo {
|
||||
instr,
|
||||
arg,
|
||||
target,
|
||||
location,
|
||||
// range,
|
||||
end_location,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+55
-32
@@ -86,9 +86,8 @@ pub struct InstructionInfo {
|
||||
pub instr: Instruction,
|
||||
pub arg: OpArg,
|
||||
pub target: BlockIdx,
|
||||
// pub range: TextRange,
|
||||
pub location: SourceLocation,
|
||||
// TODO: end_location for debug ranges
|
||||
pub end_location: SourceLocation,
|
||||
}
|
||||
|
||||
// spell-checker:ignore petgraph
|
||||
@@ -133,8 +132,11 @@ pub struct CodeInfo {
|
||||
}
|
||||
|
||||
impl CodeInfo {
|
||||
pub fn finalize_code(mut self, optimize: u8) -> crate::InternalResult<CodeObject> {
|
||||
if optimize > 0 {
|
||||
pub fn finalize_code(
|
||||
mut self,
|
||||
opts: &crate::compile::CompileOpts,
|
||||
) -> crate::InternalResult<CodeObject> {
|
||||
if opts.optimize > 0 {
|
||||
self.dce();
|
||||
}
|
||||
|
||||
@@ -198,7 +200,10 @@ impl CodeInfo {
|
||||
*arg = new_arg;
|
||||
}
|
||||
let (extras, lo_arg) = arg.split();
|
||||
locations.extend(core::iter::repeat_n(info.location, arg.instr_size()));
|
||||
locations.extend(core::iter::repeat_n(
|
||||
(info.location, info.end_location),
|
||||
arg.instr_size(),
|
||||
));
|
||||
instructions.extend(
|
||||
extras
|
||||
.map(|byte| CodeUnit::new(Instruction::ExtendedArg, byte))
|
||||
@@ -217,7 +222,11 @@ impl CodeInfo {
|
||||
}
|
||||
|
||||
// Generate linetable from locations
|
||||
let linetable = generate_linetable(&locations, first_line_number.get() as i32);
|
||||
let linetable = generate_linetable(
|
||||
&locations,
|
||||
first_line_number.get() as i32,
|
||||
opts.debug_ranges,
|
||||
);
|
||||
|
||||
Ok(CodeObject {
|
||||
flags,
|
||||
@@ -412,7 +421,11 @@ fn iter_blocks(blocks: &[Block]) -> impl Iterator<Item = (BlockIdx, &Block)> + '
|
||||
}
|
||||
|
||||
/// Generate CPython 3.11+ format linetable from source locations
|
||||
fn generate_linetable(locations: &[SourceLocation], first_line: i32) -> Box<[u8]> {
|
||||
fn generate_linetable(
|
||||
locations: &[(SourceLocation, SourceLocation)],
|
||||
first_line: i32,
|
||||
debug_ranges: bool,
|
||||
) -> Box<[u8]> {
|
||||
if locations.is_empty() {
|
||||
return Box::new([]);
|
||||
}
|
||||
@@ -424,7 +437,7 @@ fn generate_linetable(locations: &[SourceLocation], first_line: i32) -> Box<[u8]
|
||||
let mut i = 0;
|
||||
|
||||
while i < locations.len() {
|
||||
let loc = &locations[i];
|
||||
let (loc, end_loc) = &locations[i];
|
||||
|
||||
// Count consecutive instructions with the same location
|
||||
let mut length = 1;
|
||||
@@ -436,18 +449,33 @@ fn generate_linetable(locations: &[SourceLocation], first_line: i32) -> Box<[u8]
|
||||
while length > 0 {
|
||||
let entry_length = length.min(8);
|
||||
|
||||
// Get line and column information
|
||||
// SourceLocation always has row and column (both are OneIndexed)
|
||||
// Get line information
|
||||
let line = loc.line.get() as i32;
|
||||
let col = loc.character_offset.to_zero_indexed() as i32;
|
||||
|
||||
let end_line = end_loc.line.get() as i32;
|
||||
let line_delta = line - prev_line;
|
||||
let end_line_delta = end_line - line;
|
||||
|
||||
// When debug_ranges is disabled, only emit line info (NoColumns format)
|
||||
if !debug_ranges {
|
||||
// NoColumns format (code 13): line info only, no column data
|
||||
linetable.push(
|
||||
0x80 | ((PyCodeLocationInfoKind::NoColumns as u8) << 3)
|
||||
| ((entry_length - 1) as u8),
|
||||
);
|
||||
write_signed_varint(&mut linetable, line_delta);
|
||||
|
||||
prev_line = line;
|
||||
length -= entry_length;
|
||||
i += entry_length;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get column information (only when debug_ranges is enabled)
|
||||
let col = loc.character_offset.to_zero_indexed() as i32;
|
||||
let end_col = end_loc.character_offset.to_zero_indexed() as i32;
|
||||
|
||||
// Choose the appropriate encoding based on line delta and column info
|
||||
// Note: SourceLocation always has valid column, so we never get NO_COLUMNS case
|
||||
if line_delta == 0 {
|
||||
let end_col = col; // Use same column for end (no range info available)
|
||||
|
||||
if line_delta == 0 && end_line_delta == 0 {
|
||||
if col < 80 && end_col - col < 16 && end_col >= col {
|
||||
// Short form (codes 0-9) for common cases
|
||||
let code = (col / 8).min(9) as u8; // Short0 to Short9
|
||||
@@ -470,42 +498,37 @@ fn generate_linetable(locations: &[SourceLocation], first_line: i32) -> Box<[u8]
|
||||
);
|
||||
write_signed_varint(&mut linetable, 0); // line_delta = 0
|
||||
write_varint(&mut linetable, 0); // end_line delta = 0
|
||||
write_varint(&mut linetable, (col as u32) + 1); // column + 1 for encoding
|
||||
write_varint(&mut linetable, (end_col as u32) + 1); // end_col + 1
|
||||
write_varint(&mut linetable, (col as u32) + 1);
|
||||
write_varint(&mut linetable, (end_col as u32) + 1);
|
||||
}
|
||||
} else if line_delta > 0 && line_delta < 3
|
||||
/* && column.is_some() */
|
||||
{
|
||||
} else if line_delta > 0 && line_delta < 3 && end_line_delta == 0 {
|
||||
// One-line form (codes 11-12) for line deltas 1-2
|
||||
let end_col = col; // Use same column for end
|
||||
|
||||
if col < 128 && end_col < 128 {
|
||||
let code = (PyCodeLocationInfoKind::OneLine0 as u8) + (line_delta as u8); // 11 for delta=1, 12 for delta=2
|
||||
let code = (PyCodeLocationInfoKind::OneLine0 as u8) + (line_delta as u8);
|
||||
linetable.push(0x80 | (code << 3) | ((entry_length - 1) as u8));
|
||||
linetable.push(col as u8);
|
||||
linetable.push(end_col as u8);
|
||||
} else {
|
||||
// Long form for columns >= 128 or negative line delta
|
||||
// Long form for columns >= 128
|
||||
linetable.push(
|
||||
0x80 | ((PyCodeLocationInfoKind::Long as u8) << 3)
|
||||
| ((entry_length - 1) as u8),
|
||||
);
|
||||
write_signed_varint(&mut linetable, line_delta);
|
||||
write_varint(&mut linetable, 0); // end_line delta = 0
|
||||
write_varint(&mut linetable, (col as u32) + 1); // column + 1 for encoding
|
||||
write_varint(&mut linetable, (end_col as u32) + 1); // end_col + 1
|
||||
write_varint(&mut linetable, (col as u32) + 1);
|
||||
write_varint(&mut linetable, (end_col as u32) + 1);
|
||||
}
|
||||
} else {
|
||||
// Long form (code 14) for all other cases
|
||||
// This handles: line_delta < 0, line_delta >= 3, or columns >= 128
|
||||
let end_col = col; // Use same column for end
|
||||
// Handles: line_delta < 0, line_delta >= 3, multi-line spans, or columns >= 128
|
||||
linetable.push(
|
||||
0x80 | ((PyCodeLocationInfoKind::Long as u8) << 3) | ((entry_length - 1) as u8),
|
||||
);
|
||||
write_signed_varint(&mut linetable, line_delta);
|
||||
write_varint(&mut linetable, 0); // end_line delta = 0
|
||||
write_varint(&mut linetable, (col as u32) + 1); // column + 1 for encoding
|
||||
write_varint(&mut linetable, (end_col as u32) + 1); // end_col + 1
|
||||
write_varint(&mut linetable, end_line_delta as u32);
|
||||
write_varint(&mut linetable, (col as u32) + 1);
|
||||
write_varint(&mut linetable, (end_col as u32) + 1);
|
||||
}
|
||||
|
||||
prev_line = line;
|
||||
|
||||
@@ -258,7 +258,7 @@ impl ConstantBag for BasicBag {
|
||||
#[derive(Clone)]
|
||||
pub struct CodeObject<C: Constant = ConstantData> {
|
||||
pub instructions: CodeUnits,
|
||||
pub locations: Box<[SourceLocation]>,
|
||||
pub locations: Box<[(SourceLocation, SourceLocation)]>,
|
||||
pub flags: CodeFlags,
|
||||
/// Number of positional-only arguments
|
||||
pub posonlyarg_count: u32,
|
||||
@@ -1483,14 +1483,14 @@ impl<C: Constant> CodeObject<C> {
|
||||
level: usize,
|
||||
) -> fmt::Result {
|
||||
let label_targets = self.label_targets();
|
||||
let line_digits = (3).max(self.locations.last().unwrap().line.digits().get());
|
||||
let line_digits = (3).max(self.locations.last().unwrap().0.line.digits().get());
|
||||
let offset_digits = (4).max(1 + self.instructions.len().ilog10() as usize);
|
||||
let mut last_line = OneIndexed::MAX;
|
||||
let mut arg_state = OpArgState::default();
|
||||
for (offset, &instruction) in self.instructions.iter().enumerate() {
|
||||
let (instruction, arg) = arg_state.get(instruction);
|
||||
// optional line number
|
||||
let line = self.locations[offset].line;
|
||||
let line = self.locations[offset].0.line;
|
||||
if line != last_line {
|
||||
if last_line != OneIndexed::MAX {
|
||||
writeln!(f)?;
|
||||
|
||||
@@ -190,12 +190,17 @@ pub fn deserialize_code<R: Read, Bag: ConstantBag>(
|
||||
let len = rdr.read_u32()?;
|
||||
let locations = (0..len)
|
||||
.map(|_| {
|
||||
Ok(SourceLocation {
|
||||
let start = SourceLocation {
|
||||
line: OneIndexed::new(rdr.read_u32()? as _).ok_or(MarshalError::InvalidLocation)?,
|
||||
character_offset: OneIndexed::from_zero_indexed(rdr.read_u32()? as _),
|
||||
})
|
||||
};
|
||||
let end = SourceLocation {
|
||||
line: OneIndexed::new(rdr.read_u32()? as _).ok_or(MarshalError::InvalidLocation)?,
|
||||
character_offset: OneIndexed::from_zero_indexed(rdr.read_u32()? as _),
|
||||
};
|
||||
Ok((start, end))
|
||||
})
|
||||
.collect::<Result<Box<[SourceLocation]>>>()?;
|
||||
.collect::<Result<Box<[(SourceLocation, SourceLocation)]>>>()?;
|
||||
|
||||
let flags = CodeFlags::from_bits_truncate(rdr.read_u16()?);
|
||||
|
||||
@@ -648,9 +653,11 @@ pub fn serialize_code<W: Write, C: Constant>(buf: &mut W, code: &CodeObject<C>)
|
||||
buf.write_slice(instructions_bytes);
|
||||
|
||||
write_len(buf, code.locations.len());
|
||||
for loc in &*code.locations {
|
||||
buf.write_u32(loc.line.get() as _);
|
||||
buf.write_u32(loc.character_offset.to_zero_indexed() as _);
|
||||
for (start, end) in &*code.locations {
|
||||
buf.write_u32(start.line.get() as _);
|
||||
buf.write_u32(start.character_offset.to_zero_indexed() as _);
|
||||
buf.write_u32(end.line.get() as _);
|
||||
buf.write_u32(end.character_offset.to_zero_indexed() as _);
|
||||
}
|
||||
|
||||
buf.write_u16(code.flags.bits());
|
||||
|
||||
@@ -467,20 +467,22 @@ impl Constructor for PyCode {
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
// Create locations
|
||||
// Create locations (start and end pairs)
|
||||
let row = if args.firstlineno > 0 {
|
||||
OneIndexed::new(args.firstlineno as usize).unwrap_or(OneIndexed::MIN)
|
||||
} else {
|
||||
OneIndexed::MIN
|
||||
};
|
||||
let locations: Box<[rustpython_compiler_core::SourceLocation]> = vec![
|
||||
rustpython_compiler_core::SourceLocation {
|
||||
line: row,
|
||||
character_offset: OneIndexed::from_zero_indexed(0),
|
||||
};
|
||||
instructions.len()
|
||||
]
|
||||
.into_boxed_slice();
|
||||
let loc = rustpython_compiler_core::SourceLocation {
|
||||
line: row,
|
||||
character_offset: OneIndexed::from_zero_indexed(0),
|
||||
};
|
||||
let locations: Box<
|
||||
[(
|
||||
rustpython_compiler_core::SourceLocation,
|
||||
rustpython_compiler_core::SourceLocation,
|
||||
)],
|
||||
> = vec![(loc, loc); instructions.len()].into_boxed_slice();
|
||||
|
||||
// Build the CodeObject
|
||||
let code = CodeObject {
|
||||
@@ -809,7 +811,6 @@ impl PyCode {
|
||||
Some(line + end_line_delta)
|
||||
};
|
||||
|
||||
// Convert Option to PyObject (None or int)
|
||||
let line_obj = final_line.to_pyobject(vm);
|
||||
let end_line_obj = final_endline.to_pyobject(vm);
|
||||
let column_obj = column.to_pyobject(vm);
|
||||
|
||||
@@ -58,12 +58,19 @@ impl Frame {
|
||||
|
||||
#[pygetset]
|
||||
fn f_lasti(&self) -> u32 {
|
||||
self.lasti()
|
||||
// Return byte offset (each instruction is 2 bytes) for compatibility
|
||||
self.lasti() * 2
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn f_lineno(&self) -> usize {
|
||||
self.current_location().line.get()
|
||||
// If lasti is 0, execution hasn't started yet - use first line number
|
||||
// Similar to PyCode_Addr2Line which returns co_firstlineno for addr_q < 0
|
||||
if self.lasti() == 0 {
|
||||
self.code.first_line_number.map(|n| n.get()).unwrap_or(1)
|
||||
} else {
|
||||
self.current_location().line.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
|
||||
@@ -172,7 +172,7 @@ impl Frame {
|
||||
}
|
||||
|
||||
pub fn current_location(&self) -> SourceLocation {
|
||||
self.code.locations[self.lasti() as usize - 1]
|
||||
self.code.locations[self.lasti() as usize - 1].0
|
||||
}
|
||||
|
||||
pub fn lasti(&self) -> u32 {
|
||||
@@ -385,12 +385,12 @@ impl ExecutingFrame<'_> {
|
||||
// 2. Add new entry with current execution position (filename, lineno, code_object) to traceback.
|
||||
// 3. Unwind block stack till appropriate handler is found.
|
||||
|
||||
let loc = frame.code.locations[idx];
|
||||
let (loc, _end_loc) = frame.code.locations[idx];
|
||||
let next = exception.__traceback__();
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
frame.object.to_owned(),
|
||||
frame.lasti(),
|
||||
frame.lasti() * 2,
|
||||
loc.line,
|
||||
);
|
||||
vm_trace!("Adding to traceback: {:?} {:?}", new_traceback, loc.line);
|
||||
|
||||
@@ -508,6 +508,7 @@ impl VirtualMachine {
|
||||
pub fn compile_opts(&self) -> crate::compiler::CompileOpts {
|
||||
crate::compiler::CompileOpts {
|
||||
optimize: self.state.config.settings.optimize,
|
||||
debug_ranges: self.state.config.settings.code_debug_ranges,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ pub struct Settings {
|
||||
// int tracemalloc;
|
||||
// int perf_profiling;
|
||||
// int import_time;
|
||||
// int code_debug_ranges;
|
||||
/// -X no_debug_ranges: disable column info in bytecode
|
||||
pub code_debug_ranges: bool,
|
||||
// int show_ref_count;
|
||||
// int dump_refs;
|
||||
// wchar_t *dump_refs_file;
|
||||
@@ -192,6 +193,7 @@ impl Default for Settings {
|
||||
argv: vec![],
|
||||
hash_seed: None,
|
||||
faulthandler: false,
|
||||
code_debug_ranges: true,
|
||||
buffered_stdio: true,
|
||||
check_hash_pycs_mode: CheckHashPycsMode::Default,
|
||||
allow_external_library: cfg!(feature = "importlib"),
|
||||
|
||||
+4
-1
@@ -53,7 +53,10 @@ fn main() -> Result<(), lexopt::Error> {
|
||||
return Err("expected at least one argument".into());
|
||||
}
|
||||
|
||||
let opts = compiler::CompileOpts { optimize };
|
||||
let opts = compiler::CompileOpts {
|
||||
optimize,
|
||||
debug_ranges: true,
|
||||
};
|
||||
|
||||
for script in &scripts {
|
||||
if script.exists() && script.is_file() {
|
||||
|
||||
@@ -270,6 +270,7 @@ pub fn parse_opts() -> Result<(Settings, RunMode), lexopt::Error> {
|
||||
"faulthandler" => settings.faulthandler = true,
|
||||
"warn_default_encoding" => settings.warn_default_encoding = true,
|
||||
"no_sig_int" => settings.install_signal_handlers = false,
|
||||
"no_debug_ranges" => settings.code_debug_ranges = false,
|
||||
"int_max_str_digits" => {
|
||||
settings.int_max_str_digits = match value.unwrap().parse() {
|
||||
Ok(digits) if digits == 0 || digits >= 640 => digits,
|
||||
@@ -293,6 +294,9 @@ pub fn parse_opts() -> Result<(Settings, RunMode), lexopt::Error> {
|
||||
settings.warn_default_encoding =
|
||||
settings.warn_default_encoding || env_bool("PYTHONWARNDEFAULTENCODING");
|
||||
settings.faulthandler = settings.faulthandler || env_bool("PYTHONFAULTHANDLER");
|
||||
if env_bool("PYTHONNODEBUGRANGES") {
|
||||
settings.code_debug_ranges = false;
|
||||
}
|
||||
|
||||
if settings.dev_mode {
|
||||
settings.warnoptions.push("default".to_owned());
|
||||
|
||||
Reference in new issue
Block a user