gh-82378 fix sys.tracebacklimit in pyrepl, approach 2 (#123062)

Make sure that pyrepl uses the same logic for sys.tracebacklimit as both
the basic repl and the standard sys.excepthook
This commit is contained in:
CF Bolz-Tereick 2024-08-18 13:28:23 +02:00 committed by GitHub
parent 79c542b5cc
commit 63603bca35
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 54 additions and 16 deletions

View file

@ -161,11 +161,13 @@ class InteractiveColoredConsole(code.InteractiveConsole):
super().__init__(locals=locals, filename=filename, local_exit=local_exit) # type: ignore[call-arg] super().__init__(locals=locals, filename=filename, local_exit=local_exit) # type: ignore[call-arg]
self.can_colorize = _colorize.can_colorize() self.can_colorize = _colorize.can_colorize()
def showsyntaxerror(self, filename=None): def _excepthook(self, typ, value, tb):
super().showsyntaxerror(colorize=self.can_colorize) import traceback
lines = traceback.format_exception(
def showtraceback(self): typ, value, tb,
super().showtraceback(colorize=self.can_colorize) colorize=self.can_colorize,
limit=traceback.BUILTIN_EXCEPTION_LIMIT)
self.write(''.join(lines))
def runsource(self, source, filename="<input>", symbol="single"): def runsource(self, source, filename="<input>", symbol="single"):
try: try:

View file

@ -94,7 +94,7 @@ class InteractiveInterpreter:
except: except:
self.showtraceback() self.showtraceback()
def showsyntaxerror(self, filename=None, **kwargs): def showsyntaxerror(self, filename=None):
"""Display the syntax error that just occurred. """Display the syntax error that just occurred.
This doesn't display a stack trace because there isn't one. This doesn't display a stack trace because there isn't one.
@ -106,7 +106,6 @@ class InteractiveInterpreter:
The output is written by self.write(), below. The output is written by self.write(), below.
""" """
colorize = kwargs.pop('colorize', False)
try: try:
typ, value, tb = sys.exc_info() typ, value, tb = sys.exc_info()
if filename and typ is SyntaxError: if filename and typ is SyntaxError:
@ -119,11 +118,11 @@ class InteractiveInterpreter:
else: else:
# Stuff in the right filename # Stuff in the right filename
value = SyntaxError(msg, (filename, lineno, offset, line)) value = SyntaxError(msg, (filename, lineno, offset, line))
self._showtraceback(typ, value, None, colorize) self._showtraceback(typ, value, None)
finally: finally:
typ = value = tb = None typ = value = tb = None
def showtraceback(self, **kwargs): def showtraceback(self):
"""Display the exception that just occurred. """Display the exception that just occurred.
We remove the first stack item because it is our own code. We remove the first stack item because it is our own code.
@ -131,21 +130,18 @@ class InteractiveInterpreter:
The output is written by self.write(), below. The output is written by self.write(), below.
""" """
colorize = kwargs.pop('colorize', False)
try: try:
typ, value, tb = sys.exc_info() typ, value, tb = sys.exc_info()
self._showtraceback(typ, value, tb.tb_next, colorize) self._showtraceback(typ, value, tb.tb_next)
finally: finally:
typ = value = tb = None typ = value = tb = None
def _showtraceback(self, typ, value, tb, colorize): def _showtraceback(self, typ, value, tb):
sys.last_type = typ sys.last_type = typ
sys.last_traceback = tb sys.last_traceback = tb
sys.last_exc = sys.last_value = value = value.with_traceback(tb) sys.last_exc = sys.last_value = value = value.with_traceback(tb)
if sys.excepthook is sys.__excepthook__: if sys.excepthook is sys.__excepthook__:
lines = traceback.format_exception(typ, value, tb, self._excepthook(typ, value, tb)
colorize=colorize)
self.write(''.join(lines))
else: else:
# If someone has set sys.excepthook, we let that take precedence # If someone has set sys.excepthook, we let that take precedence
# over self.write # over self.write
@ -162,6 +158,12 @@ class InteractiveInterpreter:
print('Original exception was:', file=sys.stderr) print('Original exception was:', file=sys.stderr)
sys.__excepthook__(typ, value, tb) 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): def write(self, data):
"""Write a string. """Write a string.

View file

@ -1020,7 +1020,7 @@ class TestMain(TestCase):
env.update({"TERM": "dumb"}) env.update({"TERM": "dumb"})
output, exit_code = self.run_repl("exit()\n", env=env) output, exit_code = self.run_repl("exit()\n", env=env)
self.assertEqual(exit_code, 0) 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("Exception", output)
self.assertNotIn("Traceback", output) self.assertNotIn("Traceback", output)
@ -1100,6 +1100,38 @@ class TestMain(TestCase):
self.assertIn("spam", output) self.assertIn("spam", output)
self.assertNotEqual(pathlib.Path(hfile.name).stat().st_size, 0) self.assertNotEqual(pathlib.Path(hfile.name).stat().st_size, 0)
@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( def run_repl(
self, self,
repl_input: str | list[str], repl_input: str | list[str],

View file

@ -0,0 +1,2 @@
Make sure that the new :term:`REPL` interprets :data:`sys.tracebacklimit` in
the same way that the classic REPL did.