mirror of https://github.com/python/cpython
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:
parent
79c542b5cc
commit
63603bca35
|
@ -161,11 +161,13 @@ class InteractiveColoredConsole(code.InteractiveConsole):
|
|||
super().__init__(locals=locals, filename=filename, local_exit=local_exit) # type: ignore[call-arg]
|
||||
self.can_colorize = _colorize.can_colorize()
|
||||
|
||||
def showsyntaxerror(self, filename=None):
|
||||
super().showsyntaxerror(colorize=self.can_colorize)
|
||||
|
||||
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:
|
||||
|
|
22
Lib/code.py
22
Lib/code.py
|
@ -94,7 +94,7 @@ class InteractiveInterpreter:
|
|||
except:
|
||||
self.showtraceback()
|
||||
|
||||
def showsyntaxerror(self, filename=None, **kwargs):
|
||||
def showsyntaxerror(self, filename=None):
|
||||
"""Display the syntax error that just occurred.
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
colorize = kwargs.pop('colorize', False)
|
||||
try:
|
||||
typ, value, tb = sys.exc_info()
|
||||
if filename and typ is SyntaxError:
|
||||
|
@ -119,11 +118,11 @@ class InteractiveInterpreter:
|
|||
else:
|
||||
# Stuff in the right filename
|
||||
value = SyntaxError(msg, (filename, lineno, offset, line))
|
||||
self._showtraceback(typ, value, None, colorize)
|
||||
self._showtraceback(typ, value, None)
|
||||
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.
|
||||
|
@ -131,21 +130,18 @@ class InteractiveInterpreter:
|
|||
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):
|
||||
def _showtraceback(self, typ, value, tb):
|
||||
sys.last_type = typ
|
||||
sys.last_traceback = tb
|
||||
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
|
||||
|
@ -162,6 +158,12 @@ class InteractiveInterpreter:
|
|||
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.
|
||||
|
||||
|
|
|
@ -1020,7 +1020,7 @@ class TestMain(TestCase):
|
|||
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)
|
||||
|
||||
|
@ -1100,6 +1100,38 @@ class TestMain(TestCase):
|
|||
self.assertIn("spam", output)
|
||||
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(
|
||||
self,
|
||||
repl_input: str | list[str],
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Make sure that the new :term:`REPL` interprets :data:`sys.tracebacklimit` in
|
||||
the same way that the classic REPL did.
|
Loading…
Reference in New Issue