Skip to content

gh-99180: Remove traceback anchors in return and assign statements that cover all the displayed range #112670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 205 additions & 28 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,6 @@ def f_with_multiline():
' ~~~~~~~~^^\n'
f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n'
' return compile(code, "?", "exec")\n'
' ~~~~~~~^^^^^^^^^^^^^^^^^^^\n'
' File "?", line 7\n'
' foo(a, z\n'
' ^'
Expand Down Expand Up @@ -775,8 +774,8 @@ def f_with_binary_operator():
def test_caret_for_binary_operators_with_spaces_and_parenthesis(self):
def f_with_binary_operator():
a = 1
b = ""
return ( a ) +b
b = c = ""
return ( a ) +b + c

lineno_f = f_with_binary_operator.__code__.co_firstlineno
expected_error = (
Expand All @@ -785,7 +784,7 @@ def f_with_binary_operator():
' callable()\n'
' ~~~~~~~~^^\n'
f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n'
' return ( a ) +b\n'
' return ( a ) +b + c\n'
' ~~~~~~~~~~^~\n'
)
result_lines = self.get_exception(f_with_binary_operator)
Expand Down Expand Up @@ -973,7 +972,7 @@ def f1(a):
def f2(b):
raise RuntimeError("fail")
return f2
return f1("x")("y")
return f1("x")("y")("z")

lineno_f = f_with_call.__code__.co_firstlineno
expected_error = (
Expand All @@ -982,7 +981,7 @@ def f2(b):
' callable()\n'
' ~~~~~~~~^^\n'
f' File "{__file__}", line {lineno_f+5}, in f_with_call\n'
' return f1("x")("y")\n'
' return f1("x")("y")("z")\n'
' ~~~~~~~^^^^^\n'
f' File "{__file__}", line {lineno_f+3}, in f2\n'
' raise RuntimeError("fail")\n'
Expand Down Expand Up @@ -1497,6 +1496,184 @@ def f():
' raise MemoryError()']
self.assertEqual(actual, expected)

def test_anchors_for_simple_return_statements_are_elided(self):
def g():
1/0

def f():
return g()

result_lines = self.get_exception(f)
expected = ['Traceback (most recent call last):',
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
" callable()",
" ~~~~~~~~^^",
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
" return g()",
f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g",
" 1/0",
" ~^~"
]
self.assertEqual(result_lines, expected)

def g():
1/0

def f():
return g() + 1

result_lines = self.get_exception(f)
expected = ['Traceback (most recent call last):',
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
" callable()",
" ~~~~~~~~^^",
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
" return g() + 1",
" ~^^",
f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g",
" 1/0",
" ~^~"
]
self.assertEqual(result_lines, expected)

def g(*args):
1/0

def f():
return g(1,
2, 4,
5)

result_lines = self.get_exception(f)
expected = ['Traceback (most recent call last):',
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
" callable()",
" ~~~~~~~~^^",
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
" return g(1,",
" 2, 4,",
" 5)",
f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g",
" 1/0",
" ~^~"
]
self.assertEqual(result_lines, expected)

def g(*args):
1/0

def f():
return g(1,
2, 4,
5) + 1

result_lines = self.get_exception(f)
expected = ['Traceback (most recent call last):',
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
" callable()",
" ~~~~~~~~^^",
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
" return g(1,",
" ~^^^",
" 2, 4,",
" ^^^^^",
" 5) + 1",
" ^^",
f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g",
" 1/0",
" ~^~"
]
self.assertEqual(result_lines, expected)

def test_anchors_for_simple_assign_statements_are_elided(self):
def g():
1/0

def f():
x = g()

result_lines = self.get_exception(f)
expected = ['Traceback (most recent call last):',
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
" callable()",
" ~~~~~~~~^^",
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
" x = g()",
f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g",
" 1/0",
" ~^~"
]
self.assertEqual(result_lines, expected)

def g(*args):
1/0

def f():
x = g(1,
2, 3,
4)

result_lines = self.get_exception(f)
expected = ['Traceback (most recent call last):',
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
" callable()",
" ~~~~~~~~^^",
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
" x = g(1,",
" 2, 3,",
" 4)",
f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g",
" 1/0",
" ~^~"
]
self.assertEqual(result_lines, expected)

def g():
1/0

def f():
x = y = g()

result_lines = self.get_exception(f)
expected = ['Traceback (most recent call last):',
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
" callable()",
" ~~~~~~~~^^",
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
" x = y = g()",
" ~^^",
f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g",
" 1/0",
" ~^~"
]
self.assertEqual(result_lines, expected)

def g(*args):
1/0

def f():
x = y = g(1,
2, 3,
4)

result_lines = self.get_exception(f)
expected = ['Traceback (most recent call last):',
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
" callable()",
" ~~~~~~~~^^",
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
" x = y = g(1,",
" ~^^^",
" 2, 3,",
" ^^^^^",
" 4)",
" ^^",
f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g",
" 1/0",
" ~^~"
]
self.assertEqual(result_lines, expected)


@requires_debug_ranges()
class PurePythonTracebackErrorCaretTests(
Expand Down Expand Up @@ -1691,7 +1868,7 @@ def f():
# Check a known (limited) number of recursive invocations
def g(count=10):
if count:
return g(count-1)
return g(count-1) + 1
raise ValueError

with captured_output("stderr") as stderr_g:
Expand All @@ -1705,13 +1882,13 @@ def g(count=10):
lineno_g = g.__code__.co_firstlineno
result_g = (
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
' [Previous line repeated 7 more times]\n'
f' File "{__file__}", line {lineno_g+3}, in g\n'
Expand Down Expand Up @@ -1750,13 +1927,10 @@ def h(count=10):
' ~^^\n'
f' File "{__file__}", line {lineno_h+2}, in h\n'
' return h(count-1)\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_h+2}, in h\n'
' return h(count-1)\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_h+2}, in h\n'
' return h(count-1)\n'
' ~^^^^^^^^^\n'
' [Previous line repeated 7 more times]\n'
f' File "{__file__}", line {lineno_h+3}, in h\n'
' g()\n'
Expand All @@ -1776,21 +1950,21 @@ def h(count=10):
self.fail("no error raised")
result_g = (
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_g+3}, in g\n'
' raise ValueError\n'
'ValueError\n'
)
tb_line = (
'Traceback (most recent call last):\n'
f' File "{__file__}", line {lineno_g+80}, in _check_recursive_traceback_display\n'
f' File "{__file__}", line {lineno_g+77}, in _check_recursive_traceback_display\n'
' g(traceback._RECURSIVE_CUTOFF)\n'
' ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
)
Expand All @@ -1808,13 +1982,13 @@ def h(count=10):
self.fail("no error raised")
result_g = (
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' return g(count-1) + 1\n'
' ~^^^^^^^^^\n'
' [Previous line repeated 1 more time]\n'
f' File "{__file__}", line {lineno_g+3}, in g\n'
Expand All @@ -1823,7 +1997,7 @@ def h(count=10):
)
tb_line = (
'Traceback (most recent call last):\n'
f' File "{__file__}", line {lineno_g+112}, in _check_recursive_traceback_display\n'
f' File "{__file__}", line {lineno_g+109}, in _check_recursive_traceback_display\n'
' g(traceback._RECURSIVE_CUTOFF + 1)\n'
' ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
)
Expand Down Expand Up @@ -4260,11 +4434,14 @@ def foo(*args):
x = {'a':{'b': None}}
y = x['a']['b']['c']

def baz(*args):
return foo(1,2,3,4)
def baz2(*args):
return (lambda *args: foo(*args))(1,2,3,4)

def baz1(*args):
return baz2(1,2,3,4)

def bar():
return baz(1,
return baz1(1,
2,3
,4)
try:
Expand All @@ -4278,10 +4455,10 @@ def bar():
boldr = traceback._ANSIColors.BOLD_RED
reset = traceback._ANSIColors.RESET
self.assertIn("y = " + red + "x['a']['b']" + reset + boldr + "['c']" + reset, lines)
self.assertIn("return " + red + "foo" + reset + boldr + "(1,2,3,4)" + reset, lines)
self.assertIn("return " + red + "baz" + reset + boldr + "(1," + reset, lines)
self.assertIn(boldr + "2,3" + reset, lines)
self.assertIn(boldr + ",4)" + reset, lines)
self.assertIn("return " + red + "(lambda *args: foo(*args))" + reset + boldr + "(1,2,3,4)" + reset, lines)
self.assertIn("return (lambda *args: " + red + "foo" + reset + boldr + "(*args)" + reset + ")(1,2,3,4)", lines)
self.assertIn("return baz2(1,2,3,4)", lines)
self.assertIn("return baz1(1,\n 2,3\n ,4)", lines)
self.assertIn(red + "bar" + reset + boldr + "()" + reset, lines)

def test_colorized_syntax_error(self):
Expand Down
Loading