##// END OF EJS Templates
Restore lineno's for Input mapped files (#13560)...
JD Smith -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,50 b''
1 Restore line numbers for Input
2 ==================================
3
4 Line number information in tracebacks from input are restored.
5 Line numbers from input were removed during the transition to v8 enhanced traceback reporting.
6
7 So, instead of::
8
9 ---------------------------------------------------------------------------
10 ZeroDivisionError Traceback (most recent call last)
11 Input In [3], in <cell line: 1>()
12 ----> 1 myfunc(2)
13
14 Input In [2], in myfunc(z)
15 1 def myfunc(z):
16 ----> 2 foo.boo(z-1)
17
18 File ~/code/python/ipython/foo.py:3, in boo(x)
19 2 def boo(x):
20 ----> 3 return 1/(1-x)
21
22 ZeroDivisionError: division by zero
23
24 The error traceback now looks like::
25
26 ---------------------------------------------------------------------------
27 ZeroDivisionError Traceback (most recent call last)
28 Cell In [3], line 1
29 ----> 1 myfunc(2)
30
31 Cell In [2], line 2, in myfunc(z)
32 1 def myfunc(z):
33 ----> 2 foo.boo(z-1)
34
35 File ~/code/python/ipython/foo.py:3, in boo(x)
36 2 def boo(x):
37 ----> 3 return 1/(1-x)
38
39 ZeroDivisionError: division by zero
40
41 or, with xmode=Plain::
42
43 Traceback (most recent call last):
44 Cell In [12], line 1
45 myfunc(2)
46 Cell In [6], line 2 in myfunc
47 foo.boo(z-1)
48 File ~/code/python/ipython/foo.py:3 in boo
49 return 1/(1-x)
50 ZeroDivisionError: division by zero
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,250 +1,250 b''
1 """Tests for the key interactiveshell module, where the main ipython class is defined.
1 """Tests for the key interactiveshell module, where the main ipython class is defined.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Module imports
4 # Module imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 # third party
7 # third party
8 import pytest
8 import pytest
9
9
10 # our own packages
10 # our own packages
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Test functions
13 # Test functions
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 def test_reset():
16 def test_reset():
17 """reset must clear most namespaces."""
17 """reset must clear most namespaces."""
18
18
19 # Check that reset runs without error
19 # Check that reset runs without error
20 ip.reset()
20 ip.reset()
21
21
22 # Once we've reset it (to clear of any junk that might have been there from
22 # Once we've reset it (to clear of any junk that might have been there from
23 # other tests, we can count how many variables are in the user's namespace
23 # other tests, we can count how many variables are in the user's namespace
24 nvars_user_ns = len(ip.user_ns)
24 nvars_user_ns = len(ip.user_ns)
25 nvars_hidden = len(ip.user_ns_hidden)
25 nvars_hidden = len(ip.user_ns_hidden)
26
26
27 # Now add a few variables to user_ns, and check that reset clears them
27 # Now add a few variables to user_ns, and check that reset clears them
28 ip.user_ns['x'] = 1
28 ip.user_ns['x'] = 1
29 ip.user_ns['y'] = 1
29 ip.user_ns['y'] = 1
30 ip.reset()
30 ip.reset()
31
31
32 # Finally, check that all namespaces have only as many variables as we
32 # Finally, check that all namespaces have only as many variables as we
33 # expect to find in them:
33 # expect to find in them:
34 assert len(ip.user_ns) == nvars_user_ns
34 assert len(ip.user_ns) == nvars_user_ns
35 assert len(ip.user_ns_hidden) == nvars_hidden
35 assert len(ip.user_ns_hidden) == nvars_hidden
36
36
37
37
38 # Tests for reporting of exceptions in various modes, handling of SystemExit,
38 # Tests for reporting of exceptions in various modes, handling of SystemExit,
39 # and %tb functionality. This is really a mix of testing ultraTB and interactiveshell.
39 # and %tb functionality. This is really a mix of testing ultraTB and interactiveshell.
40
40
41 def doctest_tb_plain():
41 def doctest_tb_plain():
42 """
42 """
43 In [18]: xmode plain
43 In [18]: xmode plain
44 Exception reporting mode: Plain
44 Exception reporting mode: Plain
45
45
46 In [19]: run simpleerr.py
46 In [19]: run simpleerr.py
47 Traceback (most recent call last):
47 Traceback (most recent call last):
48 File ...:... in <module>
48 File ...:...
49 bar(mode)
49 bar(mode)
50 File ...:... in bar
50 File ...:... in bar
51 div0()
51 div0()
52 File ...:... in div0
52 File ...:... in div0
53 x/y
53 x/y
54 ZeroDivisionError: ...
54 ZeroDivisionError: ...
55 """
55 """
56
56
57
57
58 def doctest_tb_context():
58 def doctest_tb_context():
59 """
59 """
60 In [3]: xmode context
60 In [3]: xmode context
61 Exception reporting mode: Context
61 Exception reporting mode: Context
62
62
63 In [4]: run simpleerr.py
63 In [4]: run simpleerr.py
64 ---------------------------------------------------------------------------
64 ---------------------------------------------------------------------------
65 ZeroDivisionError Traceback (most recent call last)
65 ZeroDivisionError Traceback (most recent call last)
66 <BLANKLINE>
66 <BLANKLINE>
67 ... in <module>
67 ...
68 30 except IndexError:
68 30 except IndexError:
69 31 mode = 'div'
69 31 mode = 'div'
70 ---> 33 bar(mode)
70 ---> 33 bar(mode)
71 <BLANKLINE>
71 <BLANKLINE>
72 ... in bar(mode)
72 ... in bar(mode)
73 15 "bar"
73 15 "bar"
74 16 if mode=='div':
74 16 if mode=='div':
75 ---> 17 div0()
75 ---> 17 div0()
76 18 elif mode=='exit':
76 18 elif mode=='exit':
77 19 try:
77 19 try:
78 <BLANKLINE>
78 <BLANKLINE>
79 ... in div0()
79 ... in div0()
80 6 x = 1
80 6 x = 1
81 7 y = 0
81 7 y = 0
82 ----> 8 x/y
82 ----> 8 x/y
83 <BLANKLINE>
83 <BLANKLINE>
84 ZeroDivisionError: ..."""
84 ZeroDivisionError: ..."""
85
85
86
86
87 def doctest_tb_verbose():
87 def doctest_tb_verbose():
88 """
88 """
89 In [5]: xmode verbose
89 In [5]: xmode verbose
90 Exception reporting mode: Verbose
90 Exception reporting mode: Verbose
91
91
92 In [6]: run simpleerr.py
92 In [6]: run simpleerr.py
93 ---------------------------------------------------------------------------
93 ---------------------------------------------------------------------------
94 ZeroDivisionError Traceback (most recent call last)
94 ZeroDivisionError Traceback (most recent call last)
95 <BLANKLINE>
95 <BLANKLINE>
96 ... in <module>
96 ...
97 30 except IndexError:
97 30 except IndexError:
98 31 mode = 'div'
98 31 mode = 'div'
99 ---> 33 bar(mode)
99 ---> 33 bar(mode)
100 mode = 'div'
100 mode = 'div'
101 <BLANKLINE>
101 <BLANKLINE>
102 ... in bar(mode='div')
102 ... in bar(mode='div')
103 15 "bar"
103 15 "bar"
104 16 if mode=='div':
104 16 if mode=='div':
105 ---> 17 div0()
105 ---> 17 div0()
106 18 elif mode=='exit':
106 18 elif mode=='exit':
107 19 try:
107 19 try:
108 <BLANKLINE>
108 <BLANKLINE>
109 ... in div0()
109 ... in div0()
110 6 x = 1
110 6 x = 1
111 7 y = 0
111 7 y = 0
112 ----> 8 x/y
112 ----> 8 x/y
113 x = 1
113 x = 1
114 y = 0
114 y = 0
115 <BLANKLINE>
115 <BLANKLINE>
116 ZeroDivisionError: ...
116 ZeroDivisionError: ...
117 """
117 """
118
118
119
119
120 def doctest_tb_sysexit():
120 def doctest_tb_sysexit():
121 """
121 """
122 In [17]: %xmode plain
122 In [17]: %xmode plain
123 Exception reporting mode: Plain
123 Exception reporting mode: Plain
124
124
125 In [18]: %run simpleerr.py exit
125 In [18]: %run simpleerr.py exit
126 An exception has occurred, use %tb to see the full traceback.
126 An exception has occurred, use %tb to see the full traceback.
127 SystemExit: (1, 'Mode = exit')
127 SystemExit: (1, 'Mode = exit')
128
128
129 In [19]: %run simpleerr.py exit 2
129 In [19]: %run simpleerr.py exit 2
130 An exception has occurred, use %tb to see the full traceback.
130 An exception has occurred, use %tb to see the full traceback.
131 SystemExit: (2, 'Mode = exit')
131 SystemExit: (2, 'Mode = exit')
132
132
133 In [20]: %tb
133 In [20]: %tb
134 Traceback (most recent call last):
134 Traceback (most recent call last):
135 File ...:... in execfile
135 File ...:... in execfile
136 exec(compiler(f.read(), fname, "exec"), glob, loc)
136 exec(compiler(f.read(), fname, "exec"), glob, loc)
137 File ...:... in <module>
137 File ...:...
138 bar(mode)
138 bar(mode)
139 File ...:... in bar
139 File ...:... in bar
140 sysexit(stat, mode)
140 sysexit(stat, mode)
141 File ...:... in sysexit
141 File ...:... in sysexit
142 raise SystemExit(stat, f"Mode = {mode}")
142 raise SystemExit(stat, f"Mode = {mode}")
143 SystemExit: (2, 'Mode = exit')
143 SystemExit: (2, 'Mode = exit')
144
144
145 In [21]: %xmode context
145 In [21]: %xmode context
146 Exception reporting mode: Context
146 Exception reporting mode: Context
147
147
148 In [22]: %tb
148 In [22]: %tb
149 ---------------------------------------------------------------------------
149 ---------------------------------------------------------------------------
150 SystemExit Traceback (most recent call last)
150 SystemExit Traceback (most recent call last)
151 File ..., in execfile(fname, glob, loc, compiler)
151 File ..., in execfile(fname, glob, loc, compiler)
152 ... with open(fname, "rb") as f:
152 ... with open(fname, "rb") as f:
153 ... compiler = compiler or compile
153 ... compiler = compiler or compile
154 ---> ... exec(compiler(f.read(), fname, "exec"), glob, loc)
154 ---> ... exec(compiler(f.read(), fname, "exec"), glob, loc)
155 ...<module>
155 ...
156 30 except IndexError:
156 30 except IndexError:
157 31 mode = 'div'
157 31 mode = 'div'
158 ---> 33 bar(mode)
158 ---> 33 bar(mode)
159 <BLANKLINE>
159 <BLANKLINE>
160 ...bar(mode)
160 ...bar(mode)
161 21 except:
161 21 except:
162 22 stat = 1
162 22 stat = 1
163 ---> 23 sysexit(stat, mode)
163 ---> 23 sysexit(stat, mode)
164 24 else:
164 24 else:
165 25 raise ValueError('Unknown mode')
165 25 raise ValueError('Unknown mode')
166 <BLANKLINE>
166 <BLANKLINE>
167 ...sysexit(stat, mode)
167 ...sysexit(stat, mode)
168 10 def sysexit(stat, mode):
168 10 def sysexit(stat, mode):
169 ---> 11 raise SystemExit(stat, f"Mode = {mode}")
169 ---> 11 raise SystemExit(stat, f"Mode = {mode}")
170 <BLANKLINE>
170 <BLANKLINE>
171 SystemExit: (2, 'Mode = exit')
171 SystemExit: (2, 'Mode = exit')
172 """
172 """
173
173
174
174
175 def doctest_tb_sysexit_verbose():
175 def doctest_tb_sysexit_verbose():
176 """
176 """
177 In [18]: %run simpleerr.py exit
177 In [18]: %run simpleerr.py exit
178 An exception has occurred, use %tb to see the full traceback.
178 An exception has occurred, use %tb to see the full traceback.
179 SystemExit: (1, 'Mode = exit')
179 SystemExit: (1, 'Mode = exit')
180
180
181 In [19]: %run simpleerr.py exit 2
181 In [19]: %run simpleerr.py exit 2
182 An exception has occurred, use %tb to see the full traceback.
182 An exception has occurred, use %tb to see the full traceback.
183 SystemExit: (2, 'Mode = exit')
183 SystemExit: (2, 'Mode = exit')
184
184
185 In [23]: %xmode verbose
185 In [23]: %xmode verbose
186 Exception reporting mode: Verbose
186 Exception reporting mode: Verbose
187
187
188 In [24]: %tb
188 In [24]: %tb
189 ---------------------------------------------------------------------------
189 ---------------------------------------------------------------------------
190 SystemExit Traceback (most recent call last)
190 SystemExit Traceback (most recent call last)
191 <BLANKLINE>
191 <BLANKLINE>
192 ... in <module>
192 ...
193 30 except IndexError:
193 30 except IndexError:
194 31 mode = 'div'
194 31 mode = 'div'
195 ---> 33 bar(mode)
195 ---> 33 bar(mode)
196 mode = 'exit'
196 mode = 'exit'
197 <BLANKLINE>
197 <BLANKLINE>
198 ... in bar(mode='exit')
198 ... in bar(mode='exit')
199 ... except:
199 ... except:
200 ... stat = 1
200 ... stat = 1
201 ---> ... sysexit(stat, mode)
201 ---> ... sysexit(stat, mode)
202 mode = 'exit'
202 mode = 'exit'
203 stat = 2
203 stat = 2
204 ... else:
204 ... else:
205 ... raise ValueError('Unknown mode')
205 ... raise ValueError('Unknown mode')
206 <BLANKLINE>
206 <BLANKLINE>
207 ... in sysexit(stat=2, mode='exit')
207 ... in sysexit(stat=2, mode='exit')
208 10 def sysexit(stat, mode):
208 10 def sysexit(stat, mode):
209 ---> 11 raise SystemExit(stat, f"Mode = {mode}")
209 ---> 11 raise SystemExit(stat, f"Mode = {mode}")
210 stat = 2
210 stat = 2
211 <BLANKLINE>
211 <BLANKLINE>
212 SystemExit: (2, 'Mode = exit')
212 SystemExit: (2, 'Mode = exit')
213 """
213 """
214
214
215
215
216 def test_run_cell():
216 def test_run_cell():
217 import textwrap
217 import textwrap
218
218
219 ip.run_cell("a = 10\na+=1")
219 ip.run_cell("a = 10\na+=1")
220 ip.run_cell("assert a == 11\nassert 1")
220 ip.run_cell("assert a == 11\nassert 1")
221
221
222 assert ip.user_ns["a"] == 11
222 assert ip.user_ns["a"] == 11
223 complex = textwrap.dedent(
223 complex = textwrap.dedent(
224 """
224 """
225 if 1:
225 if 1:
226 print "hello"
226 print "hello"
227 if 1:
227 if 1:
228 print "world"
228 print "world"
229
229
230 if 2:
230 if 2:
231 print "foo"
231 print "foo"
232
232
233 if 3:
233 if 3:
234 print "bar"
234 print "bar"
235
235
236 if 4:
236 if 4:
237 print "bar"
237 print "bar"
238
238
239 """
239 """
240 )
240 )
241 # Simply verifies that this kind of input is run
241 # Simply verifies that this kind of input is run
242 ip.run_cell(complex)
242 ip.run_cell(complex)
243
243
244
244
245 def test_db():
245 def test_db():
246 """Test the internal database used for variable persistence."""
246 """Test the internal database used for variable persistence."""
247 ip.db["__unittest_"] = 12
247 ip.db["__unittest_"] = 12
248 assert ip.db["__unittest_"] == 12
248 assert ip.db["__unittest_"] == 12
249 del ip.db["__unittest_"]
249 del ip.db["__unittest_"]
250 assert "__unittest_" not in ip.db
250 assert "__unittest_" not in ip.db
@@ -1,1207 +1,1201 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Verbose and colourful traceback formatting.
3 Verbose and colourful traceback formatting.
4
4
5 **ColorTB**
5 **ColorTB**
6
6
7 I've always found it a bit hard to visually parse tracebacks in Python. The
7 I've always found it a bit hard to visually parse tracebacks in Python. The
8 ColorTB class is a solution to that problem. It colors the different parts of a
8 ColorTB class is a solution to that problem. It colors the different parts of a
9 traceback in a manner similar to what you would expect from a syntax-highlighting
9 traceback in a manner similar to what you would expect from a syntax-highlighting
10 text editor.
10 text editor.
11
11
12 Installation instructions for ColorTB::
12 Installation instructions for ColorTB::
13
13
14 import sys,ultratb
14 import sys,ultratb
15 sys.excepthook = ultratb.ColorTB()
15 sys.excepthook = ultratb.ColorTB()
16
16
17 **VerboseTB**
17 **VerboseTB**
18
18
19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
20 of useful info when a traceback occurs. Ping originally had it spit out HTML
20 of useful info when a traceback occurs. Ping originally had it spit out HTML
21 and intended it for CGI programmers, but why should they have all the fun? I
21 and intended it for CGI programmers, but why should they have all the fun? I
22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
23 but kind of neat, and maybe useful for long-running programs that you believe
23 but kind of neat, and maybe useful for long-running programs that you believe
24 are bug-free. If a crash *does* occur in that type of program you want details.
24 are bug-free. If a crash *does* occur in that type of program you want details.
25 Give it a shot--you'll love it or you'll hate it.
25 Give it a shot--you'll love it or you'll hate it.
26
26
27 .. note::
27 .. note::
28
28
29 The Verbose mode prints the variables currently visible where the exception
29 The Verbose mode prints the variables currently visible where the exception
30 happened (shortening their strings if too long). This can potentially be
30 happened (shortening their strings if too long). This can potentially be
31 very slow, if you happen to have a huge data structure whose string
31 very slow, if you happen to have a huge data structure whose string
32 representation is complex to compute. Your computer may appear to freeze for
32 representation is complex to compute. Your computer may appear to freeze for
33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
34 with Ctrl-C (maybe hitting it more than once).
34 with Ctrl-C (maybe hitting it more than once).
35
35
36 If you encounter this kind of situation often, you may want to use the
36 If you encounter this kind of situation often, you may want to use the
37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
38 variables (but otherwise includes the information and context given by
38 variables (but otherwise includes the information and context given by
39 Verbose).
39 Verbose).
40
40
41 .. note::
41 .. note::
42
42
43 The verbose mode print all variables in the stack, which means it can
43 The verbose mode print all variables in the stack, which means it can
44 potentially leak sensitive information like access keys, or unencrypted
44 potentially leak sensitive information like access keys, or unencrypted
45 password.
45 password.
46
46
47 Installation instructions for VerboseTB::
47 Installation instructions for VerboseTB::
48
48
49 import sys,ultratb
49 import sys,ultratb
50 sys.excepthook = ultratb.VerboseTB()
50 sys.excepthook = ultratb.VerboseTB()
51
51
52 Note: Much of the code in this module was lifted verbatim from the standard
52 Note: Much of the code in this module was lifted verbatim from the standard
53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
54
54
55 Color schemes
55 Color schemes
56 -------------
56 -------------
57
57
58 The colors are defined in the class TBTools through the use of the
58 The colors are defined in the class TBTools through the use of the
59 ColorSchemeTable class. Currently the following exist:
59 ColorSchemeTable class. Currently the following exist:
60
60
61 - NoColor: allows all of this module to be used in any terminal (the color
61 - NoColor: allows all of this module to be used in any terminal (the color
62 escapes are just dummy blank strings).
62 escapes are just dummy blank strings).
63
63
64 - Linux: is meant to look good in a terminal like the Linux console (black
64 - Linux: is meant to look good in a terminal like the Linux console (black
65 or very dark background).
65 or very dark background).
66
66
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
68 in light background terminals.
68 in light background terminals.
69
69
70 - Neutral: a neutral color scheme that should be readable on both light and
70 - Neutral: a neutral color scheme that should be readable on both light and
71 dark background
71 dark background
72
72
73 You can implement other color schemes easily, the syntax is fairly
73 You can implement other color schemes easily, the syntax is fairly
74 self-explanatory. Please send back new schemes you develop to the author for
74 self-explanatory. Please send back new schemes you develop to the author for
75 possible inclusion in future releases.
75 possible inclusion in future releases.
76
76
77 Inheritance diagram:
77 Inheritance diagram:
78
78
79 .. inheritance-diagram:: IPython.core.ultratb
79 .. inheritance-diagram:: IPython.core.ultratb
80 :parts: 3
80 :parts: 3
81 """
81 """
82
82
83 #*****************************************************************************
83 #*****************************************************************************
84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
86 #
86 #
87 # Distributed under the terms of the BSD License. The full license is in
87 # Distributed under the terms of the BSD License. The full license is in
88 # the file COPYING, distributed as part of this software.
88 # the file COPYING, distributed as part of this software.
89 #*****************************************************************************
89 #*****************************************************************************
90
90
91
91
92 import inspect
92 import inspect
93 import linecache
93 import linecache
94 import pydoc
94 import pydoc
95 import sys
95 import sys
96 import time
96 import time
97 import traceback
97 import traceback
98 from types import TracebackType
98 from types import TracebackType
99 from typing import Tuple, List, Any, Optional
99 from typing import Tuple, List, Any, Optional
100
100
101 import stack_data
101 import stack_data
102 from pygments.formatters.terminal256 import Terminal256Formatter
102 from pygments.formatters.terminal256 import Terminal256Formatter
103 from pygments.styles import get_style_by_name
103 from pygments.styles import get_style_by_name
104
104
105 # IPython's own modules
105 # IPython's own modules
106 from IPython import get_ipython
106 from IPython import get_ipython
107 from IPython.core import debugger
107 from IPython.core import debugger
108 from IPython.core.display_trap import DisplayTrap
108 from IPython.core.display_trap import DisplayTrap
109 from IPython.core.excolors import exception_colors
109 from IPython.core.excolors import exception_colors
110 from IPython.utils import path as util_path
110 from IPython.utils import path as util_path
111 from IPython.utils import py3compat
111 from IPython.utils import py3compat
112 from IPython.utils.terminal import get_terminal_size
112 from IPython.utils.terminal import get_terminal_size
113
113
114 import IPython.utils.colorable as colorable
114 import IPython.utils.colorable as colorable
115
115
116 # Globals
116 # Globals
117 # amount of space to put line numbers before verbose tracebacks
117 # amount of space to put line numbers before verbose tracebacks
118 INDENT_SIZE = 8
118 INDENT_SIZE = 8
119
119
120 # Default color scheme. This is used, for example, by the traceback
120 # Default color scheme. This is used, for example, by the traceback
121 # formatter. When running in an actual IPython instance, the user's rc.colors
121 # formatter. When running in an actual IPython instance, the user's rc.colors
122 # value is used, but having a module global makes this functionality available
122 # value is used, but having a module global makes this functionality available
123 # to users of ultratb who are NOT running inside ipython.
123 # to users of ultratb who are NOT running inside ipython.
124 DEFAULT_SCHEME = 'NoColor'
124 DEFAULT_SCHEME = 'NoColor'
125
125
126 # ---------------------------------------------------------------------------
126 # ---------------------------------------------------------------------------
127 # Code begins
127 # Code begins
128
128
129 # Helper function -- largely belongs to VerboseTB, but we need the same
129 # Helper function -- largely belongs to VerboseTB, but we need the same
130 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
130 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
131 # can be recognized properly by ipython.el's py-traceback-line-re
131 # can be recognized properly by ipython.el's py-traceback-line-re
132 # (SyntaxErrors have to be treated specially because they have no traceback)
132 # (SyntaxErrors have to be treated specially because they have no traceback)
133
133
134
134
135 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
135 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
136 """
136 """
137 Format tracebacks lines with pointing arrow, leading numbers...
137 Format tracebacks lines with pointing arrow, leading numbers...
138
138
139 Parameters
139 Parameters
140 ----------
140 ----------
141 lines : list[Line]
141 lines : list[Line]
142 Colors
142 Colors
143 ColorScheme used.
143 ColorScheme used.
144 lvals : str
144 lvals : str
145 Values of local variables, already colored, to inject just after the error line.
145 Values of local variables, already colored, to inject just after the error line.
146 """
146 """
147 numbers_width = INDENT_SIZE - 1
147 numbers_width = INDENT_SIZE - 1
148 res = []
148 res = []
149
149
150 for stack_line in lines:
150 for stack_line in lines:
151 if stack_line is stack_data.LINE_GAP:
151 if stack_line is stack_data.LINE_GAP:
152 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
152 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
153 continue
153 continue
154
154
155 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
155 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
156 lineno = stack_line.lineno
156 lineno = stack_line.lineno
157 if stack_line.is_current:
157 if stack_line.is_current:
158 # This is the line with the error
158 # This is the line with the error
159 pad = numbers_width - len(str(lineno))
159 pad = numbers_width - len(str(lineno))
160 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
160 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
161 start_color = Colors.linenoEm
161 start_color = Colors.linenoEm
162 else:
162 else:
163 num = '%*s' % (numbers_width, lineno)
163 num = '%*s' % (numbers_width, lineno)
164 start_color = Colors.lineno
164 start_color = Colors.lineno
165
165
166 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
166 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
167
167
168 res.append(line)
168 res.append(line)
169 if lvals and stack_line.is_current:
169 if lvals and stack_line.is_current:
170 res.append(lvals + '\n')
170 res.append(lvals + '\n')
171 return res
171 return res
172
172
173
173
174 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
174 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
175 """
175 """
176 Format filename lines with `In [n]` if it's the nth code cell or `File *.py` if it's a module.
176 Format filename lines with `In [n]` if it's the nth code cell or `File *.py` if it's a module.
177
177
178 Parameters
178 Parameters
179 ----------
179 ----------
180 file : str
180 file : str
181 ColorFilename
181 ColorFilename
182 ColorScheme's filename coloring to be used.
182 ColorScheme's filename coloring to be used.
183 ColorNormal
183 ColorNormal
184 ColorScheme's normal coloring to be used.
184 ColorScheme's normal coloring to be used.
185 """
185 """
186 ipinst = get_ipython()
186 ipinst = get_ipython()
187
187
188 if ipinst is not None and file in ipinst.compile._filename_map:
188 if ipinst is not None and file in ipinst.compile._filename_map:
189 file = "[%s]" % ipinst.compile._filename_map[file]
189 file = "[%s]" % ipinst.compile._filename_map[file]
190 tpl_link = f"Input {ColorFilename}In {{file}}{ColorNormal}"
190 if lineno is None:
191 tpl_link = f"Cell {ColorFilename}In {{file}}{ColorNormal}"
192 else:
193 tpl_link = f"Cell {ColorFilename}In {{file}}, line {{lineno}}{ColorNormal}"
191 else:
194 else:
192 file = util_path.compress_user(
195 file = util_path.compress_user(
193 py3compat.cast_unicode(file, util_path.fs_encoding)
196 py3compat.cast_unicode(file, util_path.fs_encoding)
194 )
197 )
195 if lineno is None:
198 if lineno is None:
196 tpl_link = f"File {ColorFilename}{{file}}{ColorNormal}"
199 tpl_link = f"File {ColorFilename}{{file}}{ColorNormal}"
197 else:
200 else:
198 tpl_link = f"File {ColorFilename}{{file}}:{{lineno}}{ColorNormal}"
201 tpl_link = f"File {ColorFilename}{{file}}:{{lineno}}{ColorNormal}"
199
202
200 return tpl_link.format(file=file, lineno=lineno)
203 return tpl_link.format(file=file, lineno=lineno)
201
204
202 #---------------------------------------------------------------------------
205 #---------------------------------------------------------------------------
203 # Module classes
206 # Module classes
204 class TBTools(colorable.Colorable):
207 class TBTools(colorable.Colorable):
205 """Basic tools used by all traceback printer classes."""
208 """Basic tools used by all traceback printer classes."""
206
209
207 # Number of frames to skip when reporting tracebacks
210 # Number of frames to skip when reporting tracebacks
208 tb_offset = 0
211 tb_offset = 0
209
212
210 def __init__(
213 def __init__(
211 self,
214 self,
212 color_scheme="NoColor",
215 color_scheme="NoColor",
213 call_pdb=False,
216 call_pdb=False,
214 ostream=None,
217 ostream=None,
215 parent=None,
218 parent=None,
216 config=None,
219 config=None,
217 *,
220 *,
218 debugger_cls=None,
221 debugger_cls=None,
219 ):
222 ):
220 # Whether to call the interactive pdb debugger after printing
223 # Whether to call the interactive pdb debugger after printing
221 # tracebacks or not
224 # tracebacks or not
222 super(TBTools, self).__init__(parent=parent, config=config)
225 super(TBTools, self).__init__(parent=parent, config=config)
223 self.call_pdb = call_pdb
226 self.call_pdb = call_pdb
224
227
225 # Output stream to write to. Note that we store the original value in
228 # Output stream to write to. Note that we store the original value in
226 # a private attribute and then make the public ostream a property, so
229 # a private attribute and then make the public ostream a property, so
227 # that we can delay accessing sys.stdout until runtime. The way
230 # that we can delay accessing sys.stdout until runtime. The way
228 # things are written now, the sys.stdout object is dynamically managed
231 # things are written now, the sys.stdout object is dynamically managed
229 # so a reference to it should NEVER be stored statically. This
232 # so a reference to it should NEVER be stored statically. This
230 # property approach confines this detail to a single location, and all
233 # property approach confines this detail to a single location, and all
231 # subclasses can simply access self.ostream for writing.
234 # subclasses can simply access self.ostream for writing.
232 self._ostream = ostream
235 self._ostream = ostream
233
236
234 # Create color table
237 # Create color table
235 self.color_scheme_table = exception_colors()
238 self.color_scheme_table = exception_colors()
236
239
237 self.set_colors(color_scheme)
240 self.set_colors(color_scheme)
238 self.old_scheme = color_scheme # save initial value for toggles
241 self.old_scheme = color_scheme # save initial value for toggles
239 self.debugger_cls = debugger_cls or debugger.Pdb
242 self.debugger_cls = debugger_cls or debugger.Pdb
240
243
241 if call_pdb:
244 if call_pdb:
242 self.pdb = self.debugger_cls()
245 self.pdb = self.debugger_cls()
243 else:
246 else:
244 self.pdb = None
247 self.pdb = None
245
248
246 def _get_ostream(self):
249 def _get_ostream(self):
247 """Output stream that exceptions are written to.
250 """Output stream that exceptions are written to.
248
251
249 Valid values are:
252 Valid values are:
250
253
251 - None: the default, which means that IPython will dynamically resolve
254 - None: the default, which means that IPython will dynamically resolve
252 to sys.stdout. This ensures compatibility with most tools, including
255 to sys.stdout. This ensures compatibility with most tools, including
253 Windows (where plain stdout doesn't recognize ANSI escapes).
256 Windows (where plain stdout doesn't recognize ANSI escapes).
254
257
255 - Any object with 'write' and 'flush' attributes.
258 - Any object with 'write' and 'flush' attributes.
256 """
259 """
257 return sys.stdout if self._ostream is None else self._ostream
260 return sys.stdout if self._ostream is None else self._ostream
258
261
259 def _set_ostream(self, val):
262 def _set_ostream(self, val):
260 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
263 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
261 self._ostream = val
264 self._ostream = val
262
265
263 ostream = property(_get_ostream, _set_ostream)
266 ostream = property(_get_ostream, _set_ostream)
264
267
265 @staticmethod
268 @staticmethod
266 def _get_chained_exception(exception_value):
269 def _get_chained_exception(exception_value):
267 cause = getattr(exception_value, "__cause__", None)
270 cause = getattr(exception_value, "__cause__", None)
268 if cause:
271 if cause:
269 return cause
272 return cause
270 if getattr(exception_value, "__suppress_context__", False):
273 if getattr(exception_value, "__suppress_context__", False):
271 return None
274 return None
272 return getattr(exception_value, "__context__", None)
275 return getattr(exception_value, "__context__", None)
273
276
274 def get_parts_of_chained_exception(
277 def get_parts_of_chained_exception(
275 self, evalue
278 self, evalue
276 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
279 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
277
280
278 chained_evalue = self._get_chained_exception(evalue)
281 chained_evalue = self._get_chained_exception(evalue)
279
282
280 if chained_evalue:
283 if chained_evalue:
281 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
284 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
282 return None
285 return None
283
286
284 def prepare_chained_exception_message(self, cause) -> List[Any]:
287 def prepare_chained_exception_message(self, cause) -> List[Any]:
285 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
288 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
286 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
289 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
287
290
288 if cause:
291 if cause:
289 message = [[direct_cause]]
292 message = [[direct_cause]]
290 else:
293 else:
291 message = [[exception_during_handling]]
294 message = [[exception_during_handling]]
292 return message
295 return message
293
296
294 @property
297 @property
295 def has_colors(self) -> bool:
298 def has_colors(self) -> bool:
296 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
299 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
297
300
298 def set_colors(self, *args, **kw):
301 def set_colors(self, *args, **kw):
299 """Shorthand access to the color table scheme selector method."""
302 """Shorthand access to the color table scheme selector method."""
300
303
301 # Set own color table
304 # Set own color table
302 self.color_scheme_table.set_active_scheme(*args, **kw)
305 self.color_scheme_table.set_active_scheme(*args, **kw)
303 # for convenience, set Colors to the active scheme
306 # for convenience, set Colors to the active scheme
304 self.Colors = self.color_scheme_table.active_colors
307 self.Colors = self.color_scheme_table.active_colors
305 # Also set colors of debugger
308 # Also set colors of debugger
306 if hasattr(self, 'pdb') and self.pdb is not None:
309 if hasattr(self, 'pdb') and self.pdb is not None:
307 self.pdb.set_colors(*args, **kw)
310 self.pdb.set_colors(*args, **kw)
308
311
309 def color_toggle(self):
312 def color_toggle(self):
310 """Toggle between the currently active color scheme and NoColor."""
313 """Toggle between the currently active color scheme and NoColor."""
311
314
312 if self.color_scheme_table.active_scheme_name == 'NoColor':
315 if self.color_scheme_table.active_scheme_name == 'NoColor':
313 self.color_scheme_table.set_active_scheme(self.old_scheme)
316 self.color_scheme_table.set_active_scheme(self.old_scheme)
314 self.Colors = self.color_scheme_table.active_colors
317 self.Colors = self.color_scheme_table.active_colors
315 else:
318 else:
316 self.old_scheme = self.color_scheme_table.active_scheme_name
319 self.old_scheme = self.color_scheme_table.active_scheme_name
317 self.color_scheme_table.set_active_scheme('NoColor')
320 self.color_scheme_table.set_active_scheme('NoColor')
318 self.Colors = self.color_scheme_table.active_colors
321 self.Colors = self.color_scheme_table.active_colors
319
322
320 def stb2text(self, stb):
323 def stb2text(self, stb):
321 """Convert a structured traceback (a list) to a string."""
324 """Convert a structured traceback (a list) to a string."""
322 return '\n'.join(stb)
325 return '\n'.join(stb)
323
326
324 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5):
327 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5):
325 """Return formatted traceback.
328 """Return formatted traceback.
326
329
327 Subclasses may override this if they add extra arguments.
330 Subclasses may override this if they add extra arguments.
328 """
331 """
329 tb_list = self.structured_traceback(etype, value, tb,
332 tb_list = self.structured_traceback(etype, value, tb,
330 tb_offset, context)
333 tb_offset, context)
331 return self.stb2text(tb_list)
334 return self.stb2text(tb_list)
332
335
333 def structured_traceback(
336 def structured_traceback(
334 self, etype, evalue, tb, tb_offset: Optional[int] = None, context=5, mode=None
337 self, etype, evalue, tb, tb_offset: Optional[int] = None, context=5, mode=None
335 ):
338 ):
336 """Return a list of traceback frames.
339 """Return a list of traceback frames.
337
340
338 Must be implemented by each class.
341 Must be implemented by each class.
339 """
342 """
340 raise NotImplementedError()
343 raise NotImplementedError()
341
344
342
345
343 #---------------------------------------------------------------------------
346 #---------------------------------------------------------------------------
344 class ListTB(TBTools):
347 class ListTB(TBTools):
345 """Print traceback information from a traceback list, with optional color.
348 """Print traceback information from a traceback list, with optional color.
346
349
347 Calling requires 3 arguments: (etype, evalue, elist)
350 Calling requires 3 arguments: (etype, evalue, elist)
348 as would be obtained by::
351 as would be obtained by::
349
352
350 etype, evalue, tb = sys.exc_info()
353 etype, evalue, tb = sys.exc_info()
351 if tb:
354 if tb:
352 elist = traceback.extract_tb(tb)
355 elist = traceback.extract_tb(tb)
353 else:
356 else:
354 elist = None
357 elist = None
355
358
356 It can thus be used by programs which need to process the traceback before
359 It can thus be used by programs which need to process the traceback before
357 printing (such as console replacements based on the code module from the
360 printing (such as console replacements based on the code module from the
358 standard library).
361 standard library).
359
362
360 Because they are meant to be called without a full traceback (only a
363 Because they are meant to be called without a full traceback (only a
361 list), instances of this class can't call the interactive pdb debugger."""
364 list), instances of this class can't call the interactive pdb debugger."""
362
365
363
366
364 def __call__(self, etype, value, elist):
367 def __call__(self, etype, value, elist):
365 self.ostream.flush()
368 self.ostream.flush()
366 self.ostream.write(self.text(etype, value, elist))
369 self.ostream.write(self.text(etype, value, elist))
367 self.ostream.write('\n')
370 self.ostream.write('\n')
368
371
369 def _extract_tb(self, tb):
372 def _extract_tb(self, tb):
370 if tb:
373 if tb:
371 return traceback.extract_tb(tb)
374 return traceback.extract_tb(tb)
372 else:
375 else:
373 return None
376 return None
374
377
375 def structured_traceback(
378 def structured_traceback(
376 self,
379 self,
377 etype: type,
380 etype: type,
378 evalue: BaseException,
381 evalue: BaseException,
379 etb: Optional[TracebackType] = None,
382 etb: Optional[TracebackType] = None,
380 tb_offset: Optional[int] = None,
383 tb_offset: Optional[int] = None,
381 context=5,
384 context=5,
382 ):
385 ):
383 """Return a color formatted string with the traceback info.
386 """Return a color formatted string with the traceback info.
384
387
385 Parameters
388 Parameters
386 ----------
389 ----------
387 etype : exception type
390 etype : exception type
388 Type of the exception raised.
391 Type of the exception raised.
389 evalue : object
392 evalue : object
390 Data stored in the exception
393 Data stored in the exception
391 etb : list | TracebackType | None
394 etb : list | TracebackType | None
392 If list: List of frames, see class docstring for details.
395 If list: List of frames, see class docstring for details.
393 If Traceback: Traceback of the exception.
396 If Traceback: Traceback of the exception.
394 tb_offset : int, optional
397 tb_offset : int, optional
395 Number of frames in the traceback to skip. If not given, the
398 Number of frames in the traceback to skip. If not given, the
396 instance evalue is used (set in constructor).
399 instance evalue is used (set in constructor).
397 context : int, optional
400 context : int, optional
398 Number of lines of context information to print.
401 Number of lines of context information to print.
399
402
400 Returns
403 Returns
401 -------
404 -------
402 String with formatted exception.
405 String with formatted exception.
403 """
406 """
404 # This is a workaround to get chained_exc_ids in recursive calls
407 # This is a workaround to get chained_exc_ids in recursive calls
405 # etb should not be a tuple if structured_traceback is not recursive
408 # etb should not be a tuple if structured_traceback is not recursive
406 if isinstance(etb, tuple):
409 if isinstance(etb, tuple):
407 etb, chained_exc_ids = etb
410 etb, chained_exc_ids = etb
408 else:
411 else:
409 chained_exc_ids = set()
412 chained_exc_ids = set()
410
413
411 if isinstance(etb, list):
414 if isinstance(etb, list):
412 elist = etb
415 elist = etb
413 elif etb is not None:
416 elif etb is not None:
414 elist = self._extract_tb(etb)
417 elist = self._extract_tb(etb)
415 else:
418 else:
416 elist = []
419 elist = []
417 tb_offset = self.tb_offset if tb_offset is None else tb_offset
420 tb_offset = self.tb_offset if tb_offset is None else tb_offset
418 assert isinstance(tb_offset, int)
421 assert isinstance(tb_offset, int)
419 Colors = self.Colors
422 Colors = self.Colors
420 out_list = []
423 out_list = []
421 if elist:
424 if elist:
422
425
423 if tb_offset and len(elist) > tb_offset:
426 if tb_offset and len(elist) > tb_offset:
424 elist = elist[tb_offset:]
427 elist = elist[tb_offset:]
425
428
426 out_list.append('Traceback %s(most recent call last)%s:' %
429 out_list.append('Traceback %s(most recent call last)%s:' %
427 (Colors.normalEm, Colors.Normal) + '\n')
430 (Colors.normalEm, Colors.Normal) + '\n')
428 out_list.extend(self._format_list(elist))
431 out_list.extend(self._format_list(elist))
429 # The exception info should be a single entry in the list.
432 # The exception info should be a single entry in the list.
430 lines = ''.join(self._format_exception_only(etype, evalue))
433 lines = ''.join(self._format_exception_only(etype, evalue))
431 out_list.append(lines)
434 out_list.append(lines)
432
435
433 exception = self.get_parts_of_chained_exception(evalue)
436 exception = self.get_parts_of_chained_exception(evalue)
434
437
435 if exception and not id(exception[1]) in chained_exc_ids:
438 if exception and not id(exception[1]) in chained_exc_ids:
436 chained_exception_message = self.prepare_chained_exception_message(
439 chained_exception_message = self.prepare_chained_exception_message(
437 evalue.__cause__)[0]
440 evalue.__cause__)[0]
438 etype, evalue, etb = exception
441 etype, evalue, etb = exception
439 # Trace exception to avoid infinite 'cause' loop
442 # Trace exception to avoid infinite 'cause' loop
440 chained_exc_ids.add(id(exception[1]))
443 chained_exc_ids.add(id(exception[1]))
441 chained_exceptions_tb_offset = 0
444 chained_exceptions_tb_offset = 0
442 out_list = (
445 out_list = (
443 self.structured_traceback(
446 self.structured_traceback(
444 etype, evalue, (etb, chained_exc_ids),
447 etype, evalue, (etb, chained_exc_ids),
445 chained_exceptions_tb_offset, context)
448 chained_exceptions_tb_offset, context)
446 + chained_exception_message
449 + chained_exception_message
447 + out_list)
450 + out_list)
448
451
449 return out_list
452 return out_list
450
453
451 def _format_list(self, extracted_list):
454 def _format_list(self, extracted_list):
452 """Format a list of traceback entry tuples for printing.
455 """Format a list of traceback entry tuples for printing.
453
456
454 Given a list of tuples as returned by extract_tb() or
457 Given a list of tuples as returned by extract_tb() or
455 extract_stack(), return a list of strings ready for printing.
458 extract_stack(), return a list of strings ready for printing.
456 Each string in the resulting list corresponds to the item with the
459 Each string in the resulting list corresponds to the item with the
457 same index in the argument list. Each string ends in a newline;
460 same index in the argument list. Each string ends in a newline;
458 the strings may contain internal newlines as well, for those items
461 the strings may contain internal newlines as well, for those items
459 whose source text line is not None.
462 whose source text line is not None.
460
463
461 Lifted almost verbatim from traceback.py
464 Lifted almost verbatim from traceback.py
462 """
465 """
463
466
464 Colors = self.Colors
467 Colors = self.Colors
465 list = []
468 list = []
466 for filename, lineno, name, line in extracted_list[:-1]:
469 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
467 item = " %s in %s%s%s\n" % (
470 normalCol, nameCol, fileCol, lineCol = (
468 _format_filename(
471 # Emphasize the last entry
469 filename, Colors.filename, Colors.Normal, lineno=lineno
472 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
470 ),
473 if ind == len(extracted_list) - 1
471 Colors.name,
474 else (Colors.Normal, Colors.name, Colors.filename, "")
472 name,
473 Colors.Normal,
474 )
475 )
476
477 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
478 item = f"{normalCol} {fns}"
479
480 if name != "<module>":
481 item += f" in {nameCol}{name}{normalCol}\n"
482 else:
483 item += "\n"
475 if line:
484 if line:
476 item += ' %s\n' % line.strip()
485 item += f"{lineCol} {line.strip()}{normalCol}\n"
477 list.append(item)
486 list.append(item)
478 # Emphasize the last entry
487
479 filename, lineno, name, line = extracted_list[-1]
480 item = "%s %s in %s%s%s%s\n" % (
481 Colors.normalEm,
482 _format_filename(
483 filename, Colors.filenameEm, Colors.normalEm, lineno=lineno
484 ),
485 Colors.nameEm,
486 name,
487 Colors.normalEm,
488 Colors.Normal,
489 )
490 if line:
491 item += '%s %s%s\n' % (Colors.line, line.strip(),
492 Colors.Normal)
493 list.append(item)
494 return list
488 return list
495
489
496 def _format_exception_only(self, etype, value):
490 def _format_exception_only(self, etype, value):
497 """Format the exception part of a traceback.
491 """Format the exception part of a traceback.
498
492
499 The arguments are the exception type and value such as given by
493 The arguments are the exception type and value such as given by
500 sys.exc_info()[:2]. The return value is a list of strings, each ending
494 sys.exc_info()[:2]. The return value is a list of strings, each ending
501 in a newline. Normally, the list contains a single string; however,
495 in a newline. Normally, the list contains a single string; however,
502 for SyntaxError exceptions, it contains several lines that (when
496 for SyntaxError exceptions, it contains several lines that (when
503 printed) display detailed information about where the syntax error
497 printed) display detailed information about where the syntax error
504 occurred. The message indicating which exception occurred is the
498 occurred. The message indicating which exception occurred is the
505 always last string in the list.
499 always last string in the list.
506
500
507 Also lifted nearly verbatim from traceback.py
501 Also lifted nearly verbatim from traceback.py
508 """
502 """
509 have_filedata = False
503 have_filedata = False
510 Colors = self.Colors
504 Colors = self.Colors
511 list = []
505 list = []
512 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
506 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
513 if value is None:
507 if value is None:
514 # Not sure if this can still happen in Python 2.6 and above
508 # Not sure if this can still happen in Python 2.6 and above
515 list.append(stype + '\n')
509 list.append(stype + '\n')
516 else:
510 else:
517 if issubclass(etype, SyntaxError):
511 if issubclass(etype, SyntaxError):
518 have_filedata = True
512 have_filedata = True
519 if not value.filename: value.filename = "<string>"
513 if not value.filename: value.filename = "<string>"
520 if value.lineno:
514 if value.lineno:
521 lineno = value.lineno
515 lineno = value.lineno
522 textline = linecache.getline(value.filename, value.lineno)
516 textline = linecache.getline(value.filename, value.lineno)
523 else:
517 else:
524 lineno = "unknown"
518 lineno = "unknown"
525 textline = ""
519 textline = ""
526 list.append(
520 list.append(
527 "%s %s%s\n"
521 "%s %s%s\n"
528 % (
522 % (
529 Colors.normalEm,
523 Colors.normalEm,
530 _format_filename(
524 _format_filename(
531 value.filename,
525 value.filename,
532 Colors.filenameEm,
526 Colors.filenameEm,
533 Colors.normalEm,
527 Colors.normalEm,
534 lineno=(None if lineno == "unknown" else lineno),
528 lineno=(None if lineno == "unknown" else lineno),
535 ),
529 ),
536 Colors.Normal,
530 Colors.Normal,
537 )
531 )
538 )
532 )
539 if textline == "":
533 if textline == "":
540 textline = py3compat.cast_unicode(value.text, "utf-8")
534 textline = py3compat.cast_unicode(value.text, "utf-8")
541
535
542 if textline is not None:
536 if textline is not None:
543 i = 0
537 i = 0
544 while i < len(textline) and textline[i].isspace():
538 while i < len(textline) and textline[i].isspace():
545 i += 1
539 i += 1
546 list.append('%s %s%s\n' % (Colors.line,
540 list.append('%s %s%s\n' % (Colors.line,
547 textline.strip(),
541 textline.strip(),
548 Colors.Normal))
542 Colors.Normal))
549 if value.offset is not None:
543 if value.offset is not None:
550 s = ' '
544 s = ' '
551 for c in textline[i:value.offset - 1]:
545 for c in textline[i:value.offset - 1]:
552 if c.isspace():
546 if c.isspace():
553 s += c
547 s += c
554 else:
548 else:
555 s += ' '
549 s += ' '
556 list.append('%s%s^%s\n' % (Colors.caret, s,
550 list.append('%s%s^%s\n' % (Colors.caret, s,
557 Colors.Normal))
551 Colors.Normal))
558
552
559 try:
553 try:
560 s = value.msg
554 s = value.msg
561 except Exception:
555 except Exception:
562 s = self._some_str(value)
556 s = self._some_str(value)
563 if s:
557 if s:
564 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
558 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
565 Colors.Normal, s))
559 Colors.Normal, s))
566 else:
560 else:
567 list.append('%s\n' % stype)
561 list.append('%s\n' % stype)
568
562
569 # sync with user hooks
563 # sync with user hooks
570 if have_filedata:
564 if have_filedata:
571 ipinst = get_ipython()
565 ipinst = get_ipython()
572 if ipinst is not None:
566 if ipinst is not None:
573 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
567 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
574
568
575 return list
569 return list
576
570
577 def get_exception_only(self, etype, value):
571 def get_exception_only(self, etype, value):
578 """Only print the exception type and message, without a traceback.
572 """Only print the exception type and message, without a traceback.
579
573
580 Parameters
574 Parameters
581 ----------
575 ----------
582 etype : exception type
576 etype : exception type
583 value : exception value
577 value : exception value
584 """
578 """
585 return ListTB.structured_traceback(self, etype, value)
579 return ListTB.structured_traceback(self, etype, value)
586
580
587 def show_exception_only(self, etype, evalue):
581 def show_exception_only(self, etype, evalue):
588 """Only print the exception type and message, without a traceback.
582 """Only print the exception type and message, without a traceback.
589
583
590 Parameters
584 Parameters
591 ----------
585 ----------
592 etype : exception type
586 etype : exception type
593 evalue : exception value
587 evalue : exception value
594 """
588 """
595 # This method needs to use __call__ from *this* class, not the one from
589 # This method needs to use __call__ from *this* class, not the one from
596 # a subclass whose signature or behavior may be different
590 # a subclass whose signature or behavior may be different
597 ostream = self.ostream
591 ostream = self.ostream
598 ostream.flush()
592 ostream.flush()
599 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
593 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
600 ostream.flush()
594 ostream.flush()
601
595
602 def _some_str(self, value):
596 def _some_str(self, value):
603 # Lifted from traceback.py
597 # Lifted from traceback.py
604 try:
598 try:
605 return py3compat.cast_unicode(str(value))
599 return py3compat.cast_unicode(str(value))
606 except:
600 except:
607 return u'<unprintable %s object>' % type(value).__name__
601 return u'<unprintable %s object>' % type(value).__name__
608
602
609
603
610 #----------------------------------------------------------------------------
604 #----------------------------------------------------------------------------
611 class VerboseTB(TBTools):
605 class VerboseTB(TBTools):
612 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
606 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
613 of HTML. Requires inspect and pydoc. Crazy, man.
607 of HTML. Requires inspect and pydoc. Crazy, man.
614
608
615 Modified version which optionally strips the topmost entries from the
609 Modified version which optionally strips the topmost entries from the
616 traceback, to be used with alternate interpreters (because their own code
610 traceback, to be used with alternate interpreters (because their own code
617 would appear in the traceback)."""
611 would appear in the traceback)."""
618
612
619 def __init__(
613 def __init__(
620 self,
614 self,
621 color_scheme: str = "Linux",
615 color_scheme: str = "Linux",
622 call_pdb: bool = False,
616 call_pdb: bool = False,
623 ostream=None,
617 ostream=None,
624 tb_offset: int = 0,
618 tb_offset: int = 0,
625 long_header: bool = False,
619 long_header: bool = False,
626 include_vars: bool = True,
620 include_vars: bool = True,
627 check_cache=None,
621 check_cache=None,
628 debugger_cls=None,
622 debugger_cls=None,
629 parent=None,
623 parent=None,
630 config=None,
624 config=None,
631 ):
625 ):
632 """Specify traceback offset, headers and color scheme.
626 """Specify traceback offset, headers and color scheme.
633
627
634 Define how many frames to drop from the tracebacks. Calling it with
628 Define how many frames to drop from the tracebacks. Calling it with
635 tb_offset=1 allows use of this handler in interpreters which will have
629 tb_offset=1 allows use of this handler in interpreters which will have
636 their own code at the top of the traceback (VerboseTB will first
630 their own code at the top of the traceback (VerboseTB will first
637 remove that frame before printing the traceback info)."""
631 remove that frame before printing the traceback info)."""
638 TBTools.__init__(
632 TBTools.__init__(
639 self,
633 self,
640 color_scheme=color_scheme,
634 color_scheme=color_scheme,
641 call_pdb=call_pdb,
635 call_pdb=call_pdb,
642 ostream=ostream,
636 ostream=ostream,
643 parent=parent,
637 parent=parent,
644 config=config,
638 config=config,
645 debugger_cls=debugger_cls,
639 debugger_cls=debugger_cls,
646 )
640 )
647 self.tb_offset = tb_offset
641 self.tb_offset = tb_offset
648 self.long_header = long_header
642 self.long_header = long_header
649 self.include_vars = include_vars
643 self.include_vars = include_vars
650 # By default we use linecache.checkcache, but the user can provide a
644 # By default we use linecache.checkcache, but the user can provide a
651 # different check_cache implementation. This is used by the IPython
645 # different check_cache implementation. This is used by the IPython
652 # kernel to provide tracebacks for interactive code that is cached,
646 # kernel to provide tracebacks for interactive code that is cached,
653 # by a compiler instance that flushes the linecache but preserves its
647 # by a compiler instance that flushes the linecache but preserves its
654 # own code cache.
648 # own code cache.
655 if check_cache is None:
649 if check_cache is None:
656 check_cache = linecache.checkcache
650 check_cache = linecache.checkcache
657 self.check_cache = check_cache
651 self.check_cache = check_cache
658
652
659 self.skip_hidden = True
653 self.skip_hidden = True
660
654
661 def format_record(self, frame_info):
655 def format_record(self, frame_info):
662 """Format a single stack frame"""
656 """Format a single stack frame"""
663 Colors = self.Colors # just a shorthand + quicker name lookup
657 Colors = self.Colors # just a shorthand + quicker name lookup
664 ColorsNormal = Colors.Normal # used a lot
658 ColorsNormal = Colors.Normal # used a lot
665
659
666 if isinstance(frame_info, stack_data.RepeatedFrames):
660 if isinstance(frame_info, stack_data.RepeatedFrames):
667 return ' %s[... skipping similar frames: %s]%s\n' % (
661 return ' %s[... skipping similar frames: %s]%s\n' % (
668 Colors.excName, frame_info.description, ColorsNormal)
662 Colors.excName, frame_info.description, ColorsNormal)
669
663
670 indent = " " * INDENT_SIZE
664 indent = " " * INDENT_SIZE
671 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
665 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
672 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
666 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
673 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
667 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
674 Colors.vName,
668 Colors.vName,
675 Colors.valEm,
669 Colors.valEm,
676 ColorsNormal,
670 ColorsNormal,
677 )
671 )
678 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
672 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
679
673
680 link = _format_filename(
674 link = _format_filename(
681 frame_info.filename,
675 frame_info.filename,
682 Colors.filenameEm,
676 Colors.filenameEm,
683 ColorsNormal,
677 ColorsNormal,
684 lineno=frame_info.lineno,
678 lineno=frame_info.lineno,
685 )
679 )
686 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
680 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
687
681
688 func = frame_info.executing.code_qualname()
682 func = frame_info.executing.code_qualname()
689 if func == "<module>":
683 if func == "<module>":
690 call = tpl_call.format(file=func, scope="")
684 call = ""
691 else:
685 else:
692 # Decide whether to include variable details or not
686 # Decide whether to include variable details or not
693 var_repr = eqrepr if self.include_vars else nullrepr
687 var_repr = eqrepr if self.include_vars else nullrepr
694 try:
688 try:
695 scope = inspect.formatargvalues(
689 scope = inspect.formatargvalues(
696 args, varargs, varkw, locals_, formatvalue=var_repr
690 args, varargs, varkw, locals_, formatvalue=var_repr
697 )
691 )
698 call = tpl_call.format(file=func, scope=scope)
692 call = tpl_call.format(file=func, scope=scope)
699 except KeyError:
693 except KeyError:
700 # This happens in situations like errors inside generator
694 # This happens in situations like errors inside generator
701 # expressions, where local variables are listed in the
695 # expressions, where local variables are listed in the
702 # line, but can't be extracted from the frame. I'm not
696 # line, but can't be extracted from the frame. I'm not
703 # 100% sure this isn't actually a bug in inspect itself,
697 # 100% sure this isn't actually a bug in inspect itself,
704 # but since there's no info for us to compute with, the
698 # but since there's no info for us to compute with, the
705 # best we can do is report the failure and move on. Here
699 # best we can do is report the failure and move on. Here
706 # we must *not* call any traceback construction again,
700 # we must *not* call any traceback construction again,
707 # because that would mess up use of %debug later on. So we
701 # because that would mess up use of %debug later on. So we
708 # simply report the failure and move on. The only
702 # simply report the failure and move on. The only
709 # limitation will be that this frame won't have locals
703 # limitation will be that this frame won't have locals
710 # listed in the call signature. Quite subtle problem...
704 # listed in the call signature. Quite subtle problem...
711 # I can't think of a good way to validate this in a unit
705 # I can't think of a good way to validate this in a unit
712 # test, but running a script consisting of:
706 # test, but running a script consisting of:
713 # dict( (k,v.strip()) for (k,v) in range(10) )
707 # dict( (k,v.strip()) for (k,v) in range(10) )
714 # will illustrate the error, if this exception catch is
708 # will illustrate the error, if this exception catch is
715 # disabled.
709 # disabled.
716 call = tpl_call_fail % func
710 call = tpl_call_fail % func
717
711
718 lvals = ''
712 lvals = ''
719 lvals_list = []
713 lvals_list = []
720 if self.include_vars:
714 if self.include_vars:
721 try:
715 try:
722 # we likely want to fix stackdata at some point, but
716 # we likely want to fix stackdata at some point, but
723 # still need a workaround.
717 # still need a workaround.
724 fibp = frame_info.variables_in_executing_piece
718 fibp = frame_info.variables_in_executing_piece
725 for var in fibp:
719 for var in fibp:
726 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
720 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
727 except Exception:
721 except Exception:
728 lvals_list.append(
722 lvals_list.append(
729 "Exception trying to inspect frame. No more locals available."
723 "Exception trying to inspect frame. No more locals available."
730 )
724 )
731 if lvals_list:
725 if lvals_list:
732 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
726 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
733
727
734 result = "%s, %s\n" % (link, call)
728 result = f'{link}{", " if call else ""}{call}\n'
735
729
736 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
730 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
737 return result
731 return result
738
732
739 def prepare_header(self, etype, long_version=False):
733 def prepare_header(self, etype, long_version=False):
740 colors = self.Colors # just a shorthand + quicker name lookup
734 colors = self.Colors # just a shorthand + quicker name lookup
741 colorsnormal = colors.Normal # used a lot
735 colorsnormal = colors.Normal # used a lot
742 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
736 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
743 width = min(75, get_terminal_size()[0])
737 width = min(75, get_terminal_size()[0])
744 if long_version:
738 if long_version:
745 # Header with the exception type, python version, and date
739 # Header with the exception type, python version, and date
746 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
740 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
747 date = time.ctime(time.time())
741 date = time.ctime(time.time())
748
742
749 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
743 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
750 exc, ' ' * (width - len(str(etype)) - len(pyver)),
744 exc, ' ' * (width - len(str(etype)) - len(pyver)),
751 pyver, date.rjust(width) )
745 pyver, date.rjust(width) )
752 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
746 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
753 "\ncalls leading up to the error, with the most recent (innermost) call last."
747 "\ncalls leading up to the error, with the most recent (innermost) call last."
754 else:
748 else:
755 # Simplified header
749 # Simplified header
756 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
750 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
757 rjust(width - len(str(etype))) )
751 rjust(width - len(str(etype))) )
758
752
759 return head
753 return head
760
754
761 def format_exception(self, etype, evalue):
755 def format_exception(self, etype, evalue):
762 colors = self.Colors # just a shorthand + quicker name lookup
756 colors = self.Colors # just a shorthand + quicker name lookup
763 colorsnormal = colors.Normal # used a lot
757 colorsnormal = colors.Normal # used a lot
764 # Get (safely) a string form of the exception info
758 # Get (safely) a string form of the exception info
765 try:
759 try:
766 etype_str, evalue_str = map(str, (etype, evalue))
760 etype_str, evalue_str = map(str, (etype, evalue))
767 except:
761 except:
768 # User exception is improperly defined.
762 # User exception is improperly defined.
769 etype, evalue = str, sys.exc_info()[:2]
763 etype, evalue = str, sys.exc_info()[:2]
770 etype_str, evalue_str = map(str, (etype, evalue))
764 etype_str, evalue_str = map(str, (etype, evalue))
771 # ... and format it
765 # ... and format it
772 return ['%s%s%s: %s' % (colors.excName, etype_str,
766 return ['%s%s%s: %s' % (colors.excName, etype_str,
773 colorsnormal, py3compat.cast_unicode(evalue_str))]
767 colorsnormal, py3compat.cast_unicode(evalue_str))]
774
768
775 def format_exception_as_a_whole(
769 def format_exception_as_a_whole(
776 self,
770 self,
777 etype: type,
771 etype: type,
778 evalue: BaseException,
772 evalue: BaseException,
779 etb: Optional[TracebackType],
773 etb: Optional[TracebackType],
780 number_of_lines_of_context,
774 number_of_lines_of_context,
781 tb_offset: Optional[int],
775 tb_offset: Optional[int],
782 ):
776 ):
783 """Formats the header, traceback and exception message for a single exception.
777 """Formats the header, traceback and exception message for a single exception.
784
778
785 This may be called multiple times by Python 3 exception chaining
779 This may be called multiple times by Python 3 exception chaining
786 (PEP 3134).
780 (PEP 3134).
787 """
781 """
788 # some locals
782 # some locals
789 orig_etype = etype
783 orig_etype = etype
790 try:
784 try:
791 etype = etype.__name__
785 etype = etype.__name__
792 except AttributeError:
786 except AttributeError:
793 pass
787 pass
794
788
795 tb_offset = self.tb_offset if tb_offset is None else tb_offset
789 tb_offset = self.tb_offset if tb_offset is None else tb_offset
796 assert isinstance(tb_offset, int)
790 assert isinstance(tb_offset, int)
797 head = self.prepare_header(etype, self.long_header)
791 head = self.prepare_header(etype, self.long_header)
798 records = (
792 records = (
799 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
793 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
800 )
794 )
801
795
802 frames = []
796 frames = []
803 skipped = 0
797 skipped = 0
804 lastrecord = len(records) - 1
798 lastrecord = len(records) - 1
805 for i, r in enumerate(records):
799 for i, r in enumerate(records):
806 if not isinstance(r, stack_data.RepeatedFrames) and self.skip_hidden:
800 if not isinstance(r, stack_data.RepeatedFrames) and self.skip_hidden:
807 if r.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord:
801 if r.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord:
808 skipped += 1
802 skipped += 1
809 continue
803 continue
810 if skipped:
804 if skipped:
811 Colors = self.Colors # just a shorthand + quicker name lookup
805 Colors = self.Colors # just a shorthand + quicker name lookup
812 ColorsNormal = Colors.Normal # used a lot
806 ColorsNormal = Colors.Normal # used a lot
813 frames.append(
807 frames.append(
814 " %s[... skipping hidden %s frame]%s\n"
808 " %s[... skipping hidden %s frame]%s\n"
815 % (Colors.excName, skipped, ColorsNormal)
809 % (Colors.excName, skipped, ColorsNormal)
816 )
810 )
817 skipped = 0
811 skipped = 0
818 frames.append(self.format_record(r))
812 frames.append(self.format_record(r))
819 if skipped:
813 if skipped:
820 Colors = self.Colors # just a shorthand + quicker name lookup
814 Colors = self.Colors # just a shorthand + quicker name lookup
821 ColorsNormal = Colors.Normal # used a lot
815 ColorsNormal = Colors.Normal # used a lot
822 frames.append(
816 frames.append(
823 " %s[... skipping hidden %s frame]%s\n"
817 " %s[... skipping hidden %s frame]%s\n"
824 % (Colors.excName, skipped, ColorsNormal)
818 % (Colors.excName, skipped, ColorsNormal)
825 )
819 )
826
820
827 formatted_exception = self.format_exception(etype, evalue)
821 formatted_exception = self.format_exception(etype, evalue)
828 if records:
822 if records:
829 frame_info = records[-1]
823 frame_info = records[-1]
830 ipinst = get_ipython()
824 ipinst = get_ipython()
831 if ipinst is not None:
825 if ipinst is not None:
832 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
826 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
833
827
834 return [[head] + frames + [''.join(formatted_exception[0])]]
828 return [[head] + frames + [''.join(formatted_exception[0])]]
835
829
836 def get_records(
830 def get_records(
837 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
831 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
838 ):
832 ):
839 assert etb is not None
833 assert etb is not None
840 context = number_of_lines_of_context - 1
834 context = number_of_lines_of_context - 1
841 after = context // 2
835 after = context // 2
842 before = context - after
836 before = context - after
843 if self.has_colors:
837 if self.has_colors:
844 style = get_style_by_name("default")
838 style = get_style_by_name("default")
845 style = stack_data.style_with_executing_node(style, "bg:ansiyellow")
839 style = stack_data.style_with_executing_node(style, "bg:ansiyellow")
846 formatter = Terminal256Formatter(style=style)
840 formatter = Terminal256Formatter(style=style)
847 else:
841 else:
848 formatter = None
842 formatter = None
849 options = stack_data.Options(
843 options = stack_data.Options(
850 before=before,
844 before=before,
851 after=after,
845 after=after,
852 pygments_formatter=formatter,
846 pygments_formatter=formatter,
853 )
847 )
854 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
848 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
855
849
856 def structured_traceback(
850 def structured_traceback(
857 self,
851 self,
858 etype: type,
852 etype: type,
859 evalue: Optional[BaseException],
853 evalue: Optional[BaseException],
860 etb: Optional[TracebackType],
854 etb: Optional[TracebackType],
861 tb_offset: Optional[int] = None,
855 tb_offset: Optional[int] = None,
862 number_of_lines_of_context: int = 5,
856 number_of_lines_of_context: int = 5,
863 ):
857 ):
864 """Return a nice text document describing the traceback."""
858 """Return a nice text document describing the traceback."""
865 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
859 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
866 tb_offset)
860 tb_offset)
867
861
868 colors = self.Colors # just a shorthand + quicker name lookup
862 colors = self.Colors # just a shorthand + quicker name lookup
869 colorsnormal = colors.Normal # used a lot
863 colorsnormal = colors.Normal # used a lot
870 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
864 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
871 structured_traceback_parts = [head]
865 structured_traceback_parts = [head]
872 chained_exceptions_tb_offset = 0
866 chained_exceptions_tb_offset = 0
873 lines_of_context = 3
867 lines_of_context = 3
874 formatted_exceptions = formatted_exception
868 formatted_exceptions = formatted_exception
875 exception = self.get_parts_of_chained_exception(evalue)
869 exception = self.get_parts_of_chained_exception(evalue)
876 if exception:
870 if exception:
877 assert evalue is not None
871 assert evalue is not None
878 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
872 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
879 etype, evalue, etb = exception
873 etype, evalue, etb = exception
880 else:
874 else:
881 evalue = None
875 evalue = None
882 chained_exc_ids = set()
876 chained_exc_ids = set()
883 while evalue:
877 while evalue:
884 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
878 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
885 chained_exceptions_tb_offset)
879 chained_exceptions_tb_offset)
886 exception = self.get_parts_of_chained_exception(evalue)
880 exception = self.get_parts_of_chained_exception(evalue)
887
881
888 if exception and not id(exception[1]) in chained_exc_ids:
882 if exception and not id(exception[1]) in chained_exc_ids:
889 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
883 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
890 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
884 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
891 etype, evalue, etb = exception
885 etype, evalue, etb = exception
892 else:
886 else:
893 evalue = None
887 evalue = None
894
888
895 # we want to see exceptions in a reversed order:
889 # we want to see exceptions in a reversed order:
896 # the first exception should be on top
890 # the first exception should be on top
897 for formatted_exception in reversed(formatted_exceptions):
891 for formatted_exception in reversed(formatted_exceptions):
898 structured_traceback_parts += formatted_exception
892 structured_traceback_parts += formatted_exception
899
893
900 return structured_traceback_parts
894 return structured_traceback_parts
901
895
902 def debugger(self, force: bool = False):
896 def debugger(self, force: bool = False):
903 """Call up the pdb debugger if desired, always clean up the tb
897 """Call up the pdb debugger if desired, always clean up the tb
904 reference.
898 reference.
905
899
906 Keywords:
900 Keywords:
907
901
908 - force(False): by default, this routine checks the instance call_pdb
902 - force(False): by default, this routine checks the instance call_pdb
909 flag and does not actually invoke the debugger if the flag is false.
903 flag and does not actually invoke the debugger if the flag is false.
910 The 'force' option forces the debugger to activate even if the flag
904 The 'force' option forces the debugger to activate even if the flag
911 is false.
905 is false.
912
906
913 If the call_pdb flag is set, the pdb interactive debugger is
907 If the call_pdb flag is set, the pdb interactive debugger is
914 invoked. In all cases, the self.tb reference to the current traceback
908 invoked. In all cases, the self.tb reference to the current traceback
915 is deleted to prevent lingering references which hamper memory
909 is deleted to prevent lingering references which hamper memory
916 management.
910 management.
917
911
918 Note that each call to pdb() does an 'import readline', so if your app
912 Note that each call to pdb() does an 'import readline', so if your app
919 requires a special setup for the readline completers, you'll have to
913 requires a special setup for the readline completers, you'll have to
920 fix that by hand after invoking the exception handler."""
914 fix that by hand after invoking the exception handler."""
921
915
922 if force or self.call_pdb:
916 if force or self.call_pdb:
923 if self.pdb is None:
917 if self.pdb is None:
924 self.pdb = self.debugger_cls()
918 self.pdb = self.debugger_cls()
925 # the system displayhook may have changed, restore the original
919 # the system displayhook may have changed, restore the original
926 # for pdb
920 # for pdb
927 display_trap = DisplayTrap(hook=sys.__displayhook__)
921 display_trap = DisplayTrap(hook=sys.__displayhook__)
928 with display_trap:
922 with display_trap:
929 self.pdb.reset()
923 self.pdb.reset()
930 # Find the right frame so we don't pop up inside ipython itself
924 # Find the right frame so we don't pop up inside ipython itself
931 if hasattr(self, 'tb') and self.tb is not None:
925 if hasattr(self, 'tb') and self.tb is not None:
932 etb = self.tb
926 etb = self.tb
933 else:
927 else:
934 etb = self.tb = sys.last_traceback
928 etb = self.tb = sys.last_traceback
935 while self.tb is not None and self.tb.tb_next is not None:
929 while self.tb is not None and self.tb.tb_next is not None:
936 assert self.tb.tb_next is not None
930 assert self.tb.tb_next is not None
937 self.tb = self.tb.tb_next
931 self.tb = self.tb.tb_next
938 if etb and etb.tb_next:
932 if etb and etb.tb_next:
939 etb = etb.tb_next
933 etb = etb.tb_next
940 self.pdb.botframe = etb.tb_frame
934 self.pdb.botframe = etb.tb_frame
941 self.pdb.interaction(None, etb)
935 self.pdb.interaction(None, etb)
942
936
943 if hasattr(self, 'tb'):
937 if hasattr(self, 'tb'):
944 del self.tb
938 del self.tb
945
939
946 def handler(self, info=None):
940 def handler(self, info=None):
947 (etype, evalue, etb) = info or sys.exc_info()
941 (etype, evalue, etb) = info or sys.exc_info()
948 self.tb = etb
942 self.tb = etb
949 ostream = self.ostream
943 ostream = self.ostream
950 ostream.flush()
944 ostream.flush()
951 ostream.write(self.text(etype, evalue, etb))
945 ostream.write(self.text(etype, evalue, etb))
952 ostream.write('\n')
946 ostream.write('\n')
953 ostream.flush()
947 ostream.flush()
954
948
955 # Changed so an instance can just be called as VerboseTB_inst() and print
949 # Changed so an instance can just be called as VerboseTB_inst() and print
956 # out the right info on its own.
950 # out the right info on its own.
957 def __call__(self, etype=None, evalue=None, etb=None):
951 def __call__(self, etype=None, evalue=None, etb=None):
958 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
952 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
959 if etb is None:
953 if etb is None:
960 self.handler()
954 self.handler()
961 else:
955 else:
962 self.handler((etype, evalue, etb))
956 self.handler((etype, evalue, etb))
963 try:
957 try:
964 self.debugger()
958 self.debugger()
965 except KeyboardInterrupt:
959 except KeyboardInterrupt:
966 print("\nKeyboardInterrupt")
960 print("\nKeyboardInterrupt")
967
961
968
962
969 #----------------------------------------------------------------------------
963 #----------------------------------------------------------------------------
970 class FormattedTB(VerboseTB, ListTB):
964 class FormattedTB(VerboseTB, ListTB):
971 """Subclass ListTB but allow calling with a traceback.
965 """Subclass ListTB but allow calling with a traceback.
972
966
973 It can thus be used as a sys.excepthook for Python > 2.1.
967 It can thus be used as a sys.excepthook for Python > 2.1.
974
968
975 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
969 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
976
970
977 Allows a tb_offset to be specified. This is useful for situations where
971 Allows a tb_offset to be specified. This is useful for situations where
978 one needs to remove a number of topmost frames from the traceback (such as
972 one needs to remove a number of topmost frames from the traceback (such as
979 occurs with python programs that themselves execute other python code,
973 occurs with python programs that themselves execute other python code,
980 like Python shells). """
974 like Python shells). """
981
975
982 mode: str
976 mode: str
983
977
984 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
978 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
985 ostream=None,
979 ostream=None,
986 tb_offset=0, long_header=False, include_vars=False,
980 tb_offset=0, long_header=False, include_vars=False,
987 check_cache=None, debugger_cls=None,
981 check_cache=None, debugger_cls=None,
988 parent=None, config=None):
982 parent=None, config=None):
989
983
990 # NEVER change the order of this list. Put new modes at the end:
984 # NEVER change the order of this list. Put new modes at the end:
991 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
985 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
992 self.verbose_modes = self.valid_modes[1:3]
986 self.verbose_modes = self.valid_modes[1:3]
993
987
994 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
988 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
995 ostream=ostream, tb_offset=tb_offset,
989 ostream=ostream, tb_offset=tb_offset,
996 long_header=long_header, include_vars=include_vars,
990 long_header=long_header, include_vars=include_vars,
997 check_cache=check_cache, debugger_cls=debugger_cls,
991 check_cache=check_cache, debugger_cls=debugger_cls,
998 parent=parent, config=config)
992 parent=parent, config=config)
999
993
1000 # Different types of tracebacks are joined with different separators to
994 # Different types of tracebacks are joined with different separators to
1001 # form a single string. They are taken from this dict
995 # form a single string. They are taken from this dict
1002 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
996 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1003 Minimal='')
997 Minimal='')
1004 # set_mode also sets the tb_join_char attribute
998 # set_mode also sets the tb_join_char attribute
1005 self.set_mode(mode)
999 self.set_mode(mode)
1006
1000
1007 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1001 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1008 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1002 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1009 mode = self.mode
1003 mode = self.mode
1010 if mode in self.verbose_modes:
1004 if mode in self.verbose_modes:
1011 # Verbose modes need a full traceback
1005 # Verbose modes need a full traceback
1012 return VerboseTB.structured_traceback(
1006 return VerboseTB.structured_traceback(
1013 self, etype, value, tb, tb_offset, number_of_lines_of_context
1007 self, etype, value, tb, tb_offset, number_of_lines_of_context
1014 )
1008 )
1015 elif mode == 'Minimal':
1009 elif mode == 'Minimal':
1016 return ListTB.get_exception_only(self, etype, value)
1010 return ListTB.get_exception_only(self, etype, value)
1017 else:
1011 else:
1018 # We must check the source cache because otherwise we can print
1012 # We must check the source cache because otherwise we can print
1019 # out-of-date source code.
1013 # out-of-date source code.
1020 self.check_cache()
1014 self.check_cache()
1021 # Now we can extract and format the exception
1015 # Now we can extract and format the exception
1022 return ListTB.structured_traceback(
1016 return ListTB.structured_traceback(
1023 self, etype, value, tb, tb_offset, number_of_lines_of_context
1017 self, etype, value, tb, tb_offset, number_of_lines_of_context
1024 )
1018 )
1025
1019
1026 def stb2text(self, stb):
1020 def stb2text(self, stb):
1027 """Convert a structured traceback (a list) to a string."""
1021 """Convert a structured traceback (a list) to a string."""
1028 return self.tb_join_char.join(stb)
1022 return self.tb_join_char.join(stb)
1029
1023
1030 def set_mode(self, mode: Optional[str] = None):
1024 def set_mode(self, mode: Optional[str] = None):
1031 """Switch to the desired mode.
1025 """Switch to the desired mode.
1032
1026
1033 If mode is not specified, cycles through the available modes."""
1027 If mode is not specified, cycles through the available modes."""
1034
1028
1035 if not mode:
1029 if not mode:
1036 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1030 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1037 len(self.valid_modes)
1031 len(self.valid_modes)
1038 self.mode = self.valid_modes[new_idx]
1032 self.mode = self.valid_modes[new_idx]
1039 elif mode not in self.valid_modes:
1033 elif mode not in self.valid_modes:
1040 raise ValueError(
1034 raise ValueError(
1041 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1035 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1042 "Valid modes: " + str(self.valid_modes)
1036 "Valid modes: " + str(self.valid_modes)
1043 )
1037 )
1044 else:
1038 else:
1045 assert isinstance(mode, str)
1039 assert isinstance(mode, str)
1046 self.mode = mode
1040 self.mode = mode
1047 # include variable details only in 'Verbose' mode
1041 # include variable details only in 'Verbose' mode
1048 self.include_vars = (self.mode == self.valid_modes[2])
1042 self.include_vars = (self.mode == self.valid_modes[2])
1049 # Set the join character for generating text tracebacks
1043 # Set the join character for generating text tracebacks
1050 self.tb_join_char = self._join_chars[self.mode]
1044 self.tb_join_char = self._join_chars[self.mode]
1051
1045
1052 # some convenient shortcuts
1046 # some convenient shortcuts
1053 def plain(self):
1047 def plain(self):
1054 self.set_mode(self.valid_modes[0])
1048 self.set_mode(self.valid_modes[0])
1055
1049
1056 def context(self):
1050 def context(self):
1057 self.set_mode(self.valid_modes[1])
1051 self.set_mode(self.valid_modes[1])
1058
1052
1059 def verbose(self):
1053 def verbose(self):
1060 self.set_mode(self.valid_modes[2])
1054 self.set_mode(self.valid_modes[2])
1061
1055
1062 def minimal(self):
1056 def minimal(self):
1063 self.set_mode(self.valid_modes[3])
1057 self.set_mode(self.valid_modes[3])
1064
1058
1065
1059
1066 #----------------------------------------------------------------------------
1060 #----------------------------------------------------------------------------
1067 class AutoFormattedTB(FormattedTB):
1061 class AutoFormattedTB(FormattedTB):
1068 """A traceback printer which can be called on the fly.
1062 """A traceback printer which can be called on the fly.
1069
1063
1070 It will find out about exceptions by itself.
1064 It will find out about exceptions by itself.
1071
1065
1072 A brief example::
1066 A brief example::
1073
1067
1074 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1068 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1075 try:
1069 try:
1076 ...
1070 ...
1077 except:
1071 except:
1078 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1072 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1079 """
1073 """
1080
1074
1081 def __call__(self, etype=None, evalue=None, etb=None,
1075 def __call__(self, etype=None, evalue=None, etb=None,
1082 out=None, tb_offset=None):
1076 out=None, tb_offset=None):
1083 """Print out a formatted exception traceback.
1077 """Print out a formatted exception traceback.
1084
1078
1085 Optional arguments:
1079 Optional arguments:
1086 - out: an open file-like object to direct output to.
1080 - out: an open file-like object to direct output to.
1087
1081
1088 - tb_offset: the number of frames to skip over in the stack, on a
1082 - tb_offset: the number of frames to skip over in the stack, on a
1089 per-call basis (this overrides temporarily the instance's tb_offset
1083 per-call basis (this overrides temporarily the instance's tb_offset
1090 given at initialization time."""
1084 given at initialization time."""
1091
1085
1092 if out is None:
1086 if out is None:
1093 out = self.ostream
1087 out = self.ostream
1094 out.flush()
1088 out.flush()
1095 out.write(self.text(etype, evalue, etb, tb_offset))
1089 out.write(self.text(etype, evalue, etb, tb_offset))
1096 out.write('\n')
1090 out.write('\n')
1097 out.flush()
1091 out.flush()
1098 # FIXME: we should remove the auto pdb behavior from here and leave
1092 # FIXME: we should remove the auto pdb behavior from here and leave
1099 # that to the clients.
1093 # that to the clients.
1100 try:
1094 try:
1101 self.debugger()
1095 self.debugger()
1102 except KeyboardInterrupt:
1096 except KeyboardInterrupt:
1103 print("\nKeyboardInterrupt")
1097 print("\nKeyboardInterrupt")
1104
1098
1105 def structured_traceback(self, etype=None, value=None, tb=None,
1099 def structured_traceback(self, etype=None, value=None, tb=None,
1106 tb_offset=None, number_of_lines_of_context=5):
1100 tb_offset=None, number_of_lines_of_context=5):
1107
1101
1108 etype: type
1102 etype: type
1109 value: BaseException
1103 value: BaseException
1110 # tb: TracebackType or tupleof tb types ?
1104 # tb: TracebackType or tupleof tb types ?
1111 if etype is None:
1105 if etype is None:
1112 etype, value, tb = sys.exc_info()
1106 etype, value, tb = sys.exc_info()
1113 if isinstance(tb, tuple):
1107 if isinstance(tb, tuple):
1114 # tb is a tuple if this is a chained exception.
1108 # tb is a tuple if this is a chained exception.
1115 self.tb = tb[0]
1109 self.tb = tb[0]
1116 else:
1110 else:
1117 self.tb = tb
1111 self.tb = tb
1118 return FormattedTB.structured_traceback(
1112 return FormattedTB.structured_traceback(
1119 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1113 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1120
1114
1121
1115
1122 #---------------------------------------------------------------------------
1116 #---------------------------------------------------------------------------
1123
1117
1124 # A simple class to preserve Nathan's original functionality.
1118 # A simple class to preserve Nathan's original functionality.
1125 class ColorTB(FormattedTB):
1119 class ColorTB(FormattedTB):
1126 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1120 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1127
1121
1128 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1122 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1129 FormattedTB.__init__(self, color_scheme=color_scheme,
1123 FormattedTB.__init__(self, color_scheme=color_scheme,
1130 call_pdb=call_pdb, **kwargs)
1124 call_pdb=call_pdb, **kwargs)
1131
1125
1132
1126
1133 class SyntaxTB(ListTB):
1127 class SyntaxTB(ListTB):
1134 """Extension which holds some state: the last exception value"""
1128 """Extension which holds some state: the last exception value"""
1135
1129
1136 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1130 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1137 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1131 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1138 self.last_syntax_error = None
1132 self.last_syntax_error = None
1139
1133
1140 def __call__(self, etype, value, elist):
1134 def __call__(self, etype, value, elist):
1141 self.last_syntax_error = value
1135 self.last_syntax_error = value
1142
1136
1143 ListTB.__call__(self, etype, value, elist)
1137 ListTB.__call__(self, etype, value, elist)
1144
1138
1145 def structured_traceback(self, etype, value, elist, tb_offset=None,
1139 def structured_traceback(self, etype, value, elist, tb_offset=None,
1146 context=5):
1140 context=5):
1147 # If the source file has been edited, the line in the syntax error can
1141 # If the source file has been edited, the line in the syntax error can
1148 # be wrong (retrieved from an outdated cache). This replaces it with
1142 # be wrong (retrieved from an outdated cache). This replaces it with
1149 # the current value.
1143 # the current value.
1150 if isinstance(value, SyntaxError) \
1144 if isinstance(value, SyntaxError) \
1151 and isinstance(value.filename, str) \
1145 and isinstance(value.filename, str) \
1152 and isinstance(value.lineno, int):
1146 and isinstance(value.lineno, int):
1153 linecache.checkcache(value.filename)
1147 linecache.checkcache(value.filename)
1154 newtext = linecache.getline(value.filename, value.lineno)
1148 newtext = linecache.getline(value.filename, value.lineno)
1155 if newtext:
1149 if newtext:
1156 value.text = newtext
1150 value.text = newtext
1157 self.last_syntax_error = value
1151 self.last_syntax_error = value
1158 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1152 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1159 tb_offset=tb_offset, context=context)
1153 tb_offset=tb_offset, context=context)
1160
1154
1161 def clear_err_state(self):
1155 def clear_err_state(self):
1162 """Return the current error state and clear it"""
1156 """Return the current error state and clear it"""
1163 e = self.last_syntax_error
1157 e = self.last_syntax_error
1164 self.last_syntax_error = None
1158 self.last_syntax_error = None
1165 return e
1159 return e
1166
1160
1167 def stb2text(self, stb):
1161 def stb2text(self, stb):
1168 """Convert a structured traceback (a list) to a string."""
1162 """Convert a structured traceback (a list) to a string."""
1169 return ''.join(stb)
1163 return ''.join(stb)
1170
1164
1171
1165
1172 # some internal-use functions
1166 # some internal-use functions
1173 def text_repr(value):
1167 def text_repr(value):
1174 """Hopefully pretty robust repr equivalent."""
1168 """Hopefully pretty robust repr equivalent."""
1175 # this is pretty horrible but should always return *something*
1169 # this is pretty horrible but should always return *something*
1176 try:
1170 try:
1177 return pydoc.text.repr(value)
1171 return pydoc.text.repr(value)
1178 except KeyboardInterrupt:
1172 except KeyboardInterrupt:
1179 raise
1173 raise
1180 except:
1174 except:
1181 try:
1175 try:
1182 return repr(value)
1176 return repr(value)
1183 except KeyboardInterrupt:
1177 except KeyboardInterrupt:
1184 raise
1178 raise
1185 except:
1179 except:
1186 try:
1180 try:
1187 # all still in an except block so we catch
1181 # all still in an except block so we catch
1188 # getattr raising
1182 # getattr raising
1189 name = getattr(value, '__name__', None)
1183 name = getattr(value, '__name__', None)
1190 if name:
1184 if name:
1191 # ick, recursion
1185 # ick, recursion
1192 return text_repr(name)
1186 return text_repr(name)
1193 klass = getattr(value, '__class__', None)
1187 klass = getattr(value, '__class__', None)
1194 if klass:
1188 if klass:
1195 return '%s instance' % text_repr(klass)
1189 return '%s instance' % text_repr(klass)
1196 except KeyboardInterrupt:
1190 except KeyboardInterrupt:
1197 raise
1191 raise
1198 except:
1192 except:
1199 return 'UNRECOVERABLE REPR FAILURE'
1193 return 'UNRECOVERABLE REPR FAILURE'
1200
1194
1201
1195
1202 def eqrepr(value, repr=text_repr):
1196 def eqrepr(value, repr=text_repr):
1203 return '=%s' % repr(value)
1197 return '=%s' % repr(value)
1204
1198
1205
1199
1206 def nullrepr(value, repr=text_repr):
1200 def nullrepr(value, repr=text_repr):
1207 return ''
1201 return ''
General Comments 0
You need to be logged in to leave comments. Login now