GeezerVison is showing github.com — gh-122478: Remove internal frames from tracebacks in REPL · serhiy-storchaka/cpython@8382122
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Lib/code.py +44-46Lines changed: 44 additions & 46 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -107,29 +107,21 @@ def showsyntaxerror(self, filename=None, **kwargs): | 107 | 107 |
| 108 | 108 | """ | 109 | 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) | | 110 | + try: | | 111 | + typ, value, tb = sys.exc_info() | | 112 | + if filename and typ is SyntaxError: | | 113 | + # Work hard to stuff the correct filename in the exception | | 114 | + try: | | 115 | + msg, (dummy_filename, lineno, offset, line) = value.args | | 116 | + except ValueError: | | 117 | + # Not the format we expect; leave it alone | | 118 | + pass | | 119 | + else: | | 120 | + # Stuff in the right filename | | 121 | + value = SyntaxError(msg, (filename, lineno, offset, line)) | | 122 | + self._showtraceback(typ, value, None, colorize) | | 123 | + finally: | | 124 | + typ = value = tb = None | 133 | 125 |
| 134 | 126 | def showtraceback(self, **kwargs): | 135 | 127 | """Display the exception that just occurred. | @@ -140,32 +132,38 @@ def showtraceback(self, **kwargs): | 140 | 132 |
| 141 | 133 | """ | 142 | 134 | 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] | 146 | 135 | 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) | | 136 | + typ, value, tb = sys.exc_info() | | 137 | + # Remove internal frames (in modules code and codeop) from traceback. | | 138 | + while tb and tb.tb_frame.f_globals.get('__name__') in ('code', 'codeop'): | | 139 | + tb = tb.tb_next | | 140 | + self._showtraceback(typ, value, tb, colorize) | 154 | 141 | finally: | 155 | | - last_tb = ei = None | | 142 | + typ = value = tb = None | 156 | 143 |
| 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) | | 144 | + def _showtraceback(self, typ, value, tb, colorize): | | 145 | + sys.last_type = typ | | 146 | + sys.last_traceback = tb | | 147 | + sys.last_exc = sys.last_value = value = value.with_traceback(tb) | | 148 | + if sys.excepthook is sys.__excepthook__: | | 149 | + lines = traceback.format_exception(typ, value, tb, | | 150 | + colorize=colorize) | | 151 | + self.write(''.join(lines)) | | 152 | + else: | | 153 | + # If someone has set sys.excepthook, we let that take precedence | | 154 | + # over self.write | | 155 | + try: | | 156 | + sys.excepthook(typ, value, tb) | | 157 | + except SystemExit: | | 158 | + raise | | 159 | + except BaseException as e: | | 160 | + e.__context__ = None | | 161 | + e = e.with_traceback(e.__traceback__.tb_next) | | 162 | + print('Error in sys.excepthook:', file=sys.stderr) | | 163 | + sys.__excepthook__(type(e), e, e.__traceback__) | | 164 | + print(file=sys.stderr) | | 165 | + print('Original exception was:', file=sys.stderr) | | 166 | + sys.__excepthook__(typ, value, tb) | 169 | 167 |
| 170 | 168 | def write(self, data): | 171 | 169 | """Write a string. | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_code_module.py +92-8Lines changed: 92 additions & 8 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | "Test InteractiveConsole and InteractiveInterpreter from code module" | 2 | 2 | import sys | | 3 | +import traceback | 3 | 4 | import unittest | 4 | 5 | from textwrap import dedent | 5 | 6 | from contextlib import ExitStack | @@ -30,6 +31,7 @@ def mock_sys(self): | 30 | 31 |
| 31 | 32 |
| 32 | 33 | class TestInteractiveConsole(unittest.TestCase, MockSys): | | 34 | + maxDiff = None | 33 | 35 |
| 34 | 36 | def setUp(self): | 35 | 37 | self.console = code.InteractiveConsole() | @@ -61,21 +63,103 @@ def test_console_stderr(self): | 61 | 63 | raise AssertionError("no console stdout") | 62 | 64 |
| 63 | 65 | def test_syntax_error(self): | 64 | | - self.infunc.side_effect = ["undefined", EOFError('Finished')] | | 66 | + self.infunc.side_effect = ["def f():", | | 67 | + " x = ?", | | 68 | + "", | | 69 | + EOFError('Finished')] | 65 | 70 | self.console.interact() | 66 | | - for call in self.stderr.method_calls: | 67 | | - if 'NameError' in ''.join(call[1]): | 68 | | - break | 69 | | - else: | 70 | | - raise AssertionError("No syntax error from console") | | 71 | + output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) | | 72 | + output = output[output.index('(InteractiveConsole)'):] | | 73 | + output = output[:output.index('\nnow exiting')] | | 74 | + self.assertEqual(output.splitlines()[1:], [ | | 75 | + ' File "<console>", line 2', | | 76 | + ' x = ?', | | 77 | + ' ^', | | 78 | + 'SyntaxError: invalid syntax']) | | 79 | + | | 80 | + def test_indentation_error(self): | | 81 | + self.infunc.side_effect = [" 1", EOFError('Finished')] | | 82 | + self.console.interact() | | 83 | + output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) | | 84 | + output = output[output.index('(InteractiveConsole)'):] | | 85 | + output = output[:output.index('\nnow exiting')] | | 86 | + self.assertEqual(output.splitlines()[1:], [ | | 87 | + ' File "<console>", line 1', | | 88 | + ' 1', | | 89 | + 'IndentationError: unexpected indent']) | | 90 | + | | 91 | + def test_unicode_error(self): | | 92 | + self.infunc.side_effect = ["'\ud800'", EOFError('Finished')] | | 93 | + self.console.interact() | | 94 | + output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) | | 95 | + output = output[output.index('(InteractiveConsole)'):] | | 96 | + output = output[output.index('\n') + 1:] | | 97 | + self.assertTrue(output.startswith('UnicodeEncodeError: '), output) | 71 | 98 |
| 72 | 99 | def test_sysexcepthook(self): | 73 | | - self.infunc.side_effect = ["raise ValueError('')", | | 100 | + self.infunc.side_effect = ["def f():", | | 101 | + " raise ValueError('BOOM!')", | | 102 | + "", | | 103 | + "f()", | 74 | 104 | EOFError('Finished')] | 75 | 105 | hook = mock.Mock() | 76 | 106 | self.sysmod.excepthook = hook | 77 | 107 | self.console.interact() | 78 | | - self.assertTrue(hook.called) | | 108 | + hook.assert_called() | | 109 | + hook.assert_called_with(self.sysmod.last_type, | | 110 | + self.sysmod.last_value, | | 111 | + self.sysmod.last_traceback) | | 112 | + self.assertIs(self.sysmod.last_type, ValueError) | | 113 | + self.assertIs(type(self.sysmod.last_value), ValueError) | | 114 | + self.assertIs(self.sysmod.last_traceback, self.sysmod.last_value.__traceback__) | | 115 | + self.assertIs(self.sysmod.last_exc, self.sysmod.last_value) | | 116 | + self.assertEqual(traceback.format_exception(self.sysmod.last_exc), [ | | 117 | + 'Traceback (most recent call last):\n', | | 118 | + ' File "<console>", line 1, in <module>\n', | | 119 | + ' File "<console>", line 2, in f\n', | | 120 | + 'ValueError: BOOM!\n']) | | 121 | + | | 122 | + def test_sysexcepthook_syntax_error(self): | | 123 | + self.infunc.side_effect = ["def f():", | | 124 | + " x = ?", | | 125 | + "", | | 126 | + EOFError('Finished')] | | 127 | + hook = mock.Mock() | | 128 | + self.sysmod.excepthook = hook | | 129 | + self.console.interact() | | 130 | + hook.assert_called() | | 131 | + hook.assert_called_with(self.sysmod.last_type, | | 132 | + self.sysmod.last_value, | | 133 | + self.sysmod.last_traceback) | | 134 | + self.assertIs(self.sysmod.last_type, SyntaxError) | | 135 | + self.assertIs(type(self.sysmod.last_value), SyntaxError) | | 136 | + self.assertIsNone(self.sysmod.last_traceback) | | 137 | + self.assertIsNone(self.sysmod.last_value.__traceback__) | | 138 | + self.assertIs(self.sysmod.last_exc, self.sysmod.last_value) | | 139 | + self.assertEqual(traceback.format_exception(self.sysmod.last_exc), [ | | 140 | + ' File "<console>", line 2\n', | | 141 | + ' x = ?\n', | | 142 | + ' ^\n', | | 143 | + 'SyntaxError: invalid syntax\n']) | | 144 | + | | 145 | + def test_sysexcepthook_indentation_error(self): | | 146 | + self.infunc.side_effect = [" 1", EOFError('Finished')] | | 147 | + hook = mock.Mock() | | 148 | + self.sysmod.excepthook = hook | | 149 | + self.console.interact() | | 150 | + hook.assert_called() | | 151 | + hook.assert_called_with(self.sysmod.last_type, | | 152 | + self.sysmod.last_value, | | 153 | + self.sysmod.last_traceback) | | 154 | + self.assertIs(self.sysmod.last_type, IndentationError) | | 155 | + self.assertIs(type(self.sysmod.last_value), IndentationError) | | 156 | + self.assertIsNone(self.sysmod.last_traceback) | | 157 | + self.assertIsNone(self.sysmod.last_value.__traceback__) | | 158 | + self.assertIs(self.sysmod.last_exc, self.sysmod.last_value) | | 159 | + self.assertEqual(traceback.format_exception(self.sysmod.last_exc), [ | | 160 | + ' File "<console>", line 1\n', | | 161 | + ' 1\n', | | 162 | + 'IndentationError: unexpected indent\n']) | 79 | 163 |
| 80 | 164 | def test_sysexcepthook_crashing_doesnt_close_repl(self): | 81 | 165 | self.infunc.side_effect = ["1/0", "a = 123", "print(a)", EOFError('Finished')] | |
Collapse file Copy file name to clipboard +3Lines changed: 3 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +Remove internal frames from tracebacks shown in | | 2 | +:class:`code.InteractiveInterpreter` with non-default :func:`sys.excepthook`. | | 3 | +Save correct tracebacks in :attr:`sys.last_traceback` and update ``__traceback__`` attribute of :attr:`sys.last_value` and :attr:`sys.last_exc`. |
|