8000 [3.13] gh-82378 fix sys.tracebacklimit in pyrepl, approach 2 (GH-123062) · cfbolz/cpython@6723780 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6723780

Browse files
committed
[3.13] pythongh-82378 fix sys.tracebacklimit in pyrepl, approach 2 (pythonGH-123062)
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) Co-authored-by: CF Bolz-Tereick <cfbolz@gmx.de>
1 parent b5bba3b commit 6723780

File tree

4 files changed

+89
-56
lines changed

4 files changed

+89
-56
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: 47 additions & 50 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,66 +106,63 @@ def showsyntaxerror(self, filename=None, **kwargs):
106106
The output is written by self.write(), below.
107107
108108
"""
109-
colorize = kwargs.pop('colorize', False)
110-
type, value, tb = sys.exc_info()
111-
sys.last_exc = value
112-
sys.last_type = type
113-
sys.last_value = value
114-
sys.last_traceback = tb
115-
if filename and type is SyntaxError:
116-
# Work hard to stuff the correct filename in the exception
117-
try:
118-
msg, (dummy_filename, lineno, offset, line) = value.args
119-
except ValueError:
120-
# Not the format we expect; leave it alone
121-
pass
122-
else:
123-
# Stuff in the right filename
124-
value = SyntaxError(msg, (filename, lineno, offset, line))
125-
sys.last_exc = sys.last_value = value
126-
if sys.excepthook is sys.__excepthook__:
127-
lines = traceback.format_exception_only(type, value, colorize=colorize)
128-
self.write(''.join(lines))
129-
else:
130-
# If someone has set sys.excepthook, we let that take precedence
131-
# over self.write
132-
self._call_excepthook(type, value, tb)
109+
try:
110+
typ, value, tb = sys.exc_info()
111+
if filename and typ is SyntaxError:
112+
# Work hard to stuff the correct filename in the exception
113+
try:
114+
msg, (dummy_filename, lineno, offset, line) = value.args
115+
except ValueError:
116+
# Not the format we expect; leave it alone
117+
pass
118+
else:
119+
# Stuff in the right filename
120+
value = SyntaxError(msg, (filename, lineno, offset, line))
121+
self._showtraceback(typ, value, None)
122+
finally:
123+
typ = value = tb = None
133124

134-
def showtraceback(self, **kwargs):
125+
def showtraceback(self):
135126
"""Display the exception that just occurred.
136127
137128
We remove the first stack item because it is our own code.
138129
139130
The output is written by self.write(), below.
140131
141132
"""
142-
colorize = kwargs.pop('colorize', False)
143-
sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
144-
sys.last_traceback = last_tb
145-
sys.last_exc = ei[1]
146133
try:
147-
if sys.excepthook is sys.__excepthook__:
148-
lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next, colorize=colorize)
149-
self.write(''.join(lines))
150-
else:
151-
# If someone has set sys.excepthook, we let that take precedence
152-
# over self.write
153-
self._call_excepthook(ei[0], ei[1], last_tb)
134+
typ, value, tb = sys.exc_info()
135+
self._showtraceback(typ, value, tb.tb_next)
154136
finally:
155-
last_tb = ei = None
137+
typ = value = tb = None
156138

157-
def _call_excepthook(self, typ, value, tb):
158-
try:
159-
sys.excepthook(typ, value, tb)
160-
except SystemExit:
161-
raise
162-
except BaseException as e:
163-
e.__context__ = None
164-
print('Error in sys.excepthook:', file=sys.stderr)
165-
sys.__excepthook__(type(e), e, e.__traceback__.tb_next)
166-
print(file=sys.stderr)
167-
print('Original exception was:', file=sys.stderr)
168-
sys.__excepthook__(typ, value, tb)
139+
def _showtraceback(self, typ, value, tb):
140+
sys.last_type = typ
141+
sys.last_traceback = tb
142+
sys.last_exc = sys.last_value = value = value.with_traceback(tb)
143+
if sys.excepthook is sys.__excepthook__:
144+
self._excepthook(typ, value, tb)
145+
else:
146+
# If someone has set sys.excepthook, we let that take precedence
147+
# over self.write
148+
try:
149+
sys.excepthook(typ, value, tb)
150+
except SystemExit:
151+
raise
152+
except BaseException as e:
153+
e.__context__ = None
154+
e = e.with_traceback(e.__traceback__.tb_next)
155+
print('Error in sys.excepthook:', file=sys.stderr)
156+
sys.__excepthook__(type(e), e, e.__traceback__)
157+
print(file=sys.stderr)
158+
print('Original exception was:', file=sys.stderr)
159+
sys.__excepthook__(typ, value, tb)
160+
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))
169166

170167
def write(self, data):
171168
"""Write a string.

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)
0