Skip to content

Commit

Permalink
[3.13] gh-82378 fix sys.tracebacklimit in pyrepl, approach 2 (GH-123062
Browse files Browse the repository at this point in the history
…) (#123252)

Make sure that pyrepl uses the same logic for sys.tracebacklimit as both
the basic repl and the standard sys.excepthook
(cherry picked from commit 63603bc)
  • Loading branch information
cfbolz authored Aug 23, 2024
1 parent 95b4f9c commit 0955db1
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 12 deletions.
9 changes: 7 additions & 2 deletions Lib/_pyrepl/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,13 @@ def __init__(
def showsyntaxerror(self, filename=None, **kwargs):
super().showsyntaxerror(filename=filename, **kwargs)

def showtraceback(self):
super().showtraceback(colorize=self.can_colorize)
def _excepthook(self, typ, value, tb):
import traceback
lines = traceback.format_exception(
typ, value, tb,
colorize=self.can_colorize,
limit=traceback.BUILTIN_EXCEPTION_LIMIT)
self.write(''.join(lines))

def runsource(self, source, filename="<input>", symbol="single"):
try:
Expand Down
20 changes: 11 additions & 9 deletions Lib/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,32 +107,30 @@ def showsyntaxerror(self, filename=None, **kwargs):
The output is written by self.write(), below.
"""
colorize = kwargs.pop('colorize', False)
try:
typ, value, tb = sys.exc_info()
if filename and issubclass(typ, SyntaxError):
value.filename = filename
source = kwargs.pop('source', "")
self._showtraceback(typ, value, None, colorize, source)
self._showtraceback(typ, value, None, source)
finally:
typ = value = tb = None

def showtraceback(self, **kwargs):
def showtraceback(self):
"""Display the exception that just occurred.
We remove the first stack item because it is our own code.
The output is written by self.write(), below.
"""
colorize = kwargs.pop('colorize', False)
try:
typ, value, tb = sys.exc_info()
self._showtraceback(typ, value, tb.tb_next, colorize, '')
self._showtraceback(typ, value, tb.tb_next, '')
finally:
typ = value = tb = None

def _showtraceback(self, typ, value, tb, colorize, source):
def _showtraceback(self, typ, value, tb, source):
sys.last_type = typ
sys.last_traceback = tb
value = value.with_traceback(tb)
Expand All @@ -143,9 +141,7 @@ def _showtraceback(self, typ, value, tb, colorize, source):
value.text = lines[value.lineno - 1]
sys.last_exc = sys.last_value = value = value.with_traceback(tb)
if sys.excepthook is sys.__excepthook__:
lines = traceback.format_exception(typ, value, tb,
colorize=colorize)
self.write(''.join(lines))
self._excepthook(typ, value, tb)
else:
# If someone has set sys.excepthook, we let that take precedence
# over self.write
Expand All @@ -162,6 +158,12 @@ def _showtraceback(self, typ, value, tb, colorize, source):
print('Original exception was:', file=sys.stderr)
sys.__excepthook__(typ, value, tb)

def _excepthook(self, typ, value, tb):
# This method is being overwritten in
# _pyrepl.console.InteractiveColoredConsole
lines = traceback.format_exception(typ, value, tb)
self.write(''.join(lines))

def write(self, data):
"""Write a string.
Expand Down
34 changes: 33 additions & 1 deletion Lib/test/test_pyrepl/test_pyrepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ def test_dumb_terminal_exits_cleanly(self):
env.update({"TERM": "dumb"})
output, exit_code = self.run_repl("exit()\n", env=env)
self.assertEqual(exit_code, 0)
self.assertIn("warning: can\'t use pyrepl", output)
self.assertIn("warning: can't use pyrepl", output)
self.assertNotIn("Exception", output)
self.assertNotIn("Traceback", output)

Expand Down Expand Up @@ -1114,6 +1114,38 @@ def test_correct_filename_in_syntaxerrors(self):
self.assertIn("IndentationError: unexpected indent", output)
self.assertIn("<python-input-0>", output)

@force_not_colorized
def test_proper_tracebacklimit(self):
env = os.environ.copy()
for set_tracebacklimit in [True, False]:
commands = ("import sys\n" +
("sys.tracebacklimit = 1\n" if set_tracebacklimit else "") +
"def x1(): 1/0\n\n"
"def x2(): x1()\n\n"
"def x3(): x2()\n\n"
"x3()\n"
"exit()\n")

for basic_repl in [True, False]:
if basic_repl:
env["PYTHON_BASIC_REPL"] = "1"
else:
env.pop("PYTHON_BASIC_REPL", None)
with self.subTest(set_tracebacklimit=set_tracebacklimit,
basic_repl=basic_repl):
output, exit_code = self.run_repl(commands, env=env)
if "can't use pyrepl" in output:
self.skipTest("pyrepl not available")
self.assertIn("in x1", output)
if set_tracebacklimit:
self.assertNotIn("in x2", output)
self.assertNotIn("in x3", output)
self.assertNotIn("in <module>", output)
else:
self.assertIn("in x2", output)
self.assertIn("in x3", output)
self.assertIn("in <module>", output)

def run_repl(
self,
repl_input: str | list[str],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Make sure that the new :term:`REPL` interprets :data:`sys.tracebacklimit` in
the same way that the classic REPL did.

0 comments on commit 0955db1

Please sign in to comment.