Skip to content

Commit 63603bc

Browse files
authored
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
1 parent 79c542b commit 63603bc

File tree

4 files changed

+54
-16
lines changed

4 files changed

+54
-16
lines changed

Lib/_pyrepl/console.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,13 @@ def __init__(
161161
super().__init__(locals=locals, filename=filename, local_exit=local_exit) # type: ignore[call-arg]
162162
self.can_colorize = _colorize.can_colorize()
163163

164-
def showsyntaxerror(self, filename=None):
165-
super().showsyntaxerror(colorize=self.can_colorize)
166-
167-
def showtraceback(self):
168-
super().showtraceback(colorize=self.can_colorize)
164+
def _excepthook(self, typ, value, tb):
165+
import traceback
166+
lines = traceback.format_exception(
167+
typ, value, tb,
168+
colorize=self.can_colorize,
169+
limit=traceback.BUILTIN_EXCEPTION_LIMIT)
170+
self.write(''.join(lines))
169171

170172
def runsource(self, source, filename="<input>", symbol="single"):
171173
try:

Lib/code.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def runcode(self, code):
9494
except:
9595
self.showtraceback()
9696

97-
def showsyntaxerror(self, filename=None, **kwargs):
97+
def showsyntaxerror(self, filename=None):
9898
"""Display the syntax error that just occurred.
9999
100100
This doesn't display a stack trace because there isn't one.
@@ -106,7 +106,6 @@ def showsyntaxerror(self, filename=None, **kwargs):
106106
The output is written by self.write(), below.
107107
108108
"""
109-
colorize = kwargs.pop('colorize', False)
110109
try:
111110
typ, value, tb = sys.exc_info()
112111
if filename and typ is SyntaxError:
@@ -119,33 +118,30 @@ def showsyntaxerror(self, filename=None, **kwargs):
119118
else:
120119
# Stuff in the right filename
121120
value = SyntaxError(msg, (filename, lineno, offset, line))
122-
self._showtraceback(typ, value, None, colorize)
121+
self._showtraceback(typ, value, None)
123122
finally:
124123
typ = value = tb = None
125124

126-
def showtraceback(self, **kwargs):
125+
def showtraceback(self):
127126
"""Display the exception that just occurred.
128127
129128
We remove the first stack item because it is our own code.
130129
131130
The output is written by self.write(), below.
132131
133132
"""
134-
colorize = kwargs.pop('colorize', False)
135133
try:
136134
typ, value, tb = sys.exc_info()
137-
self._showtraceback(typ, value, tb.tb_next, colorize)
135+
self._showtraceback(typ, value, tb.tb_next)
138136
finally:
139137
typ = value = tb = None
140138

141-
def _showtraceback(self, typ, value, tb, colorize):
139+
def _showtraceback(self, typ, value, tb):
142140
sys.last_type = typ
143141
sys.last_traceback = tb
144142
sys.last_exc = sys.last_value = value = value.with_traceback(tb)
145143
if sys.excepthook is sys.__excepthook__:
146-
lines = traceback.format_exception(typ, value, tb,
147-
colorize=colorize)
148-
self.write(''.join(lines))
144+
self._excepthook(typ, value, tb)
149145
else:
150146
# If someone has set sys.excepthook, we let that take precedence
151147
# over self.write
@@ -162,6 +158,12 @@ def _showtraceback(self, typ, value, tb, colorize):
162158
print('Original exception was:', file=sys.stderr)
163159
sys.__excepthook__(typ, value, tb)
164160

161+
def _excepthook(self, typ, value, tb):
162+
# This method is being overwritten in
163+
# _pyrepl.console.InteractiveColoredConsole
164+
lines = traceback.format_exception(typ, value, tb)
165+
self.write(''.join(lines))
166+
165167
def write(self, data):
166168
"""Write a string.
167169

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,7 @@ def test_dumb_terminal_exits_cleanly(self):
10201020
env.update({"TERM": "dumb"})
10211021
output, exit_code = self.run_repl("exit()\n", env=env)
10221022
self.assertEqual(exit_code, 0)
1023-
self.assertIn("warning: can\'t use pyrepl", output)
1023+
self.assertIn("warning: can't use pyrepl", output)
10241024
self.assertNotIn("Exception", output)
10251025
self.assertNotIn("Traceback", output)
10261026

@@ -1100,6 +1100,38 @@ def test_not_wiping_history_file(self):
11001100
self.assertIn("spam", output)
11011101
self.assertNotEqual(pathlib.Path(hfile.name).stat().st_size, 0)
11021102

1103+
@force_not_colorized
1104+
def test_proper_tracebacklimit(self):
1105+
env = os.environ.copy()
1106+
for set_tracebacklimit in [True, False]:
1107+
commands = ("import sys\n" +
1108+
("sys.tracebacklimit = 1\n" if set_tracebacklimit else "") +
1109+
"def x1(): 1/0\n\n"
1110+
"def x2(): x1()\n\n"
1111+
"def x3(): x2()\n\n"
1112+
"x3()\n"
1113+
"exit()\n")
1114+
1115+
for basic_repl in [True, False]:
1116+
if basic_repl:
1117+
env["PYTHON_BASIC_REPL"] = "1"
1118+
else:
1119+
env.pop("PYTHON_BASIC_REPL", None)
1120+
with self.subTest(set_tracebacklimit=set_tracebacklimit,
1121+
basic_repl=basic_repl):
1122+
output, exit_code = self.run_repl(commands, env=env)
1123+
if "can't use pyrepl" in output:
1124+
self.skipTest("pyrepl not available")
1125+
self.assertIn("in x1", output)
1126+
if set_tracebacklimit:
1127+
self.assertNotIn("in x2", output)
1128+
self.assertNotIn("in x3", output)
1129+
self.assertNotIn("in <module>", output)
1130+
else:
1131+
self.assertIn("in x2", output)
1132+
self.assertIn("in x3", output)
1133+
self.assertIn("in <module>", output)
1134+
11031135
def run_repl(
11041136
self,
11051137
repl_input: str | list[str],
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make sure that the new :term:`REPL` interprets :data:`sys.tracebacklimit` in
2+
the same way that the classic REPL did.

0 commit comments

Comments
 (0)