##// END OF EJS Templates
Backport PR #14010 on branch 8.12.x (try to fix tbcode) (#14036)...
Matthias Bussonnier -
r28239:d9c47500 merge
parent child Browse files
Show More
@@ -1,1399 +1,1486 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 functools
92 import inspect
93 import inspect
93 import linecache
94 import linecache
94 import pydoc
95 import pydoc
95 import sys
96 import sys
96 import time
97 import time
97 import traceback
98 import traceback
99 import types
98 from types import TracebackType
100 from types import TracebackType
99 from typing import Tuple, List, Any, Optional
101 from typing import Any, List, Optional, Tuple
100
102
101 import stack_data
103 import stack_data
102 from stack_data import FrameInfo as SDFrameInfo
103 from pygments.formatters.terminal256 import Terminal256Formatter
104 from pygments.formatters.terminal256 import Terminal256Formatter
104 from pygments.styles import get_style_by_name
105 from pygments.styles import get_style_by_name
105
106
107 import IPython.utils.colorable as colorable
106 # IPython's own modules
108 # IPython's own modules
107 from IPython import get_ipython
109 from IPython import get_ipython
108 from IPython.core import debugger
110 from IPython.core import debugger
109 from IPython.core.display_trap import DisplayTrap
111 from IPython.core.display_trap import DisplayTrap
110 from IPython.core.excolors import exception_colors
112 from IPython.core.excolors import exception_colors
111 from IPython.utils import PyColorize
113 from IPython.utils import PyColorize
112 from IPython.utils import path as util_path
114 from IPython.utils import path as util_path
113 from IPython.utils import py3compat
115 from IPython.utils import py3compat
114 from IPython.utils.terminal import get_terminal_size
116 from IPython.utils.terminal import get_terminal_size
115
117
116 import IPython.utils.colorable as colorable
117
118 # Globals
118 # Globals
119 # amount of space to put line numbers before verbose tracebacks
119 # amount of space to put line numbers before verbose tracebacks
120 INDENT_SIZE = 8
120 INDENT_SIZE = 8
121
121
122 # Default color scheme. This is used, for example, by the traceback
122 # Default color scheme. This is used, for example, by the traceback
123 # formatter. When running in an actual IPython instance, the user's rc.colors
123 # formatter. When running in an actual IPython instance, the user's rc.colors
124 # value is used, but having a module global makes this functionality available
124 # value is used, but having a module global makes this functionality available
125 # to users of ultratb who are NOT running inside ipython.
125 # to users of ultratb who are NOT running inside ipython.
126 DEFAULT_SCHEME = 'NoColor'
126 DEFAULT_SCHEME = 'NoColor'
127 FAST_THRESHOLD = 10_000
127 FAST_THRESHOLD = 10_000
128
128
129 # ---------------------------------------------------------------------------
129 # ---------------------------------------------------------------------------
130 # Code begins
130 # Code begins
131
131
132 # Helper function -- largely belongs to VerboseTB, but we need the same
132 # Helper function -- largely belongs to VerboseTB, but we need the same
133 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
133 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
134 # can be recognized properly by ipython.el's py-traceback-line-re
134 # can be recognized properly by ipython.el's py-traceback-line-re
135 # (SyntaxErrors have to be treated specially because they have no traceback)
135 # (SyntaxErrors have to be treated specially because they have no traceback)
136
136
137
137
138 @functools.lru_cache()
139 def count_lines_in_py_file(filename: str) -> int:
140 """
141 Given a filename, returns the number of lines in the file
142 if it ends with the extension ".py". Otherwise, returns 0.
143 """
144 if not filename.endswith(".py"):
145 return 0
146 else:
147 try:
148 with open(filename, "r") as file:
149 s = sum(1 for line in file)
150 except UnicodeError:
151 return 0
152 return s
153
154 """
155 Given a frame object, returns the total number of lines in the file
156 if the filename ends with the extension ".py". Otherwise, returns 0.
157 """
158
159
160 def get_line_number_of_frame(frame: types.FrameType) -> int:
161 """
162 Given a frame object, returns the total number of lines in the file
163 containing the frame's code object, or the number of lines in the
164 frame's source code if the file is not available.
165
166 Parameters
167 ----------
168 frame : FrameType
169 The frame object whose line number is to be determined.
170
171 Returns
172 -------
173 int
174 The total number of lines in the file containing the frame's
175 code object, or the number of lines in the frame's source code
176 if the file is not available.
177 """
178 filename = frame.f_code.co_filename
179 if filename is None:
180 print("No file....")
181 lines, first = inspect.getsourcelines(frame)
182 return first + len(lines)
183 return count_lines_in_py_file(filename)
184
185
138 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
186 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
139 """
187 """
140 Format tracebacks lines with pointing arrow, leading numbers...
188 Format tracebacks lines with pointing arrow, leading numbers...
141
189
142 Parameters
190 Parameters
143 ----------
191 ----------
144 lines : list[Line]
192 lines : list[Line]
145 Colors
193 Colors
146 ColorScheme used.
194 ColorScheme used.
147 lvals : str
195 lvals : str
148 Values of local variables, already colored, to inject just after the error line.
196 Values of local variables, already colored, to inject just after the error line.
149 """
197 """
150 numbers_width = INDENT_SIZE - 1
198 numbers_width = INDENT_SIZE - 1
151 res = []
199 res = []
152
200
153 for stack_line in lines:
201 for stack_line in lines:
154 if stack_line is stack_data.LINE_GAP:
202 if stack_line is stack_data.LINE_GAP:
155 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
203 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
156 continue
204 continue
157
205
158 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
206 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
159 lineno = stack_line.lineno
207 lineno = stack_line.lineno
160 if stack_line.is_current:
208 if stack_line.is_current:
161 # This is the line with the error
209 # This is the line with the error
162 pad = numbers_width - len(str(lineno))
210 pad = numbers_width - len(str(lineno))
163 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
211 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
164 start_color = Colors.linenoEm
212 start_color = Colors.linenoEm
165 else:
213 else:
166 num = '%*s' % (numbers_width, lineno)
214 num = '%*s' % (numbers_width, lineno)
167 start_color = Colors.lineno
215 start_color = Colors.lineno
168
216
169 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
217 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
170
218
171 res.append(line)
219 res.append(line)
172 if lvals and stack_line.is_current:
220 if lvals and stack_line.is_current:
173 res.append(lvals + '\n')
221 res.append(lvals + '\n')
174 return res
222 return res
175
223
176 def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
224 def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
177 """
225 """
178 Format tracebacks lines with pointing arrow, leading numbers...
226 Format tracebacks lines with pointing arrow, leading numbers...
179
227
180 Parameters
228 Parameters
181 ==========
229 ==========
182
230
183 lnum: int
231 lnum: int
184 number of the target line of code.
232 number of the target line of code.
185 index: int
233 index: int
186 which line in the list should be highlighted.
234 which line in the list should be highlighted.
187 lines: list[string]
235 lines: list[string]
188 Colors:
236 Colors:
189 ColorScheme used.
237 ColorScheme used.
190 lvals: bytes
238 lvals: bytes
191 Values of local variables, already colored, to inject just after the error line.
239 Values of local variables, already colored, to inject just after the error line.
192 _line_format: f (str) -> (str, bool)
240 _line_format: f (str) -> (str, bool)
193 return (colorized version of str, failure to do so)
241 return (colorized version of str, failure to do so)
194 """
242 """
195 numbers_width = INDENT_SIZE - 1
243 numbers_width = INDENT_SIZE - 1
196 res = []
244 res = []
197
198 for i, line in enumerate(lines, lnum - index):
245 for i, line in enumerate(lines, lnum - index):
246 # assert isinstance(line, str)
199 line = py3compat.cast_unicode(line)
247 line = py3compat.cast_unicode(line)
200
248
201 new_line, err = _line_format(line, "str")
249 new_line, err = _line_format(line, "str")
202 if not err:
250 if not err:
203 line = new_line
251 line = new_line
204
252
205 if i == lnum:
253 if i == lnum:
206 # This is the line with the error
254 # This is the line with the error
207 pad = numbers_width - len(str(i))
255 pad = numbers_width - len(str(i))
208 num = "%s%s" % (debugger.make_arrow(pad), str(lnum))
256 num = "%s%s" % (debugger.make_arrow(pad), str(lnum))
209 line = "%s%s%s %s%s" % (
257 line = "%s%s%s %s%s" % (
210 Colors.linenoEm,
258 Colors.linenoEm,
211 num,
259 num,
212 Colors.line,
260 Colors.line,
213 line,
261 line,
214 Colors.Normal,
262 Colors.Normal,
215 )
263 )
216 else:
264 else:
217 num = "%*s" % (numbers_width, i)
265 num = "%*s" % (numbers_width, i)
218 line = "%s%s%s %s" % (Colors.lineno, num, Colors.Normal, line)
266 line = "%s%s%s %s" % (Colors.lineno, num, Colors.Normal, line)
219
267
220 res.append(line)
268 res.append(line)
221 if lvals and i == lnum:
269 if lvals and i == lnum:
222 res.append(lvals + "\n")
270 res.append(lvals + "\n")
223 return res
271 return res
224
272
225
273
226 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
274 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
227 """
275 """
228 Format filename lines with custom formatting from caching compiler or `File *.py` by default
276 Format filename lines with custom formatting from caching compiler or `File *.py` by default
229
277
230 Parameters
278 Parameters
231 ----------
279 ----------
232 file : str
280 file : str
233 ColorFilename
281 ColorFilename
234 ColorScheme's filename coloring to be used.
282 ColorScheme's filename coloring to be used.
235 ColorNormal
283 ColorNormal
236 ColorScheme's normal coloring to be used.
284 ColorScheme's normal coloring to be used.
237 """
285 """
238 ipinst = get_ipython()
286 ipinst = get_ipython()
239 if (
287 if (
240 ipinst is not None
288 ipinst is not None
241 and (data := ipinst.compile.format_code_name(file)) is not None
289 and (data := ipinst.compile.format_code_name(file)) is not None
242 ):
290 ):
243 label, name = data
291 label, name = data
244 if lineno is None:
292 if lineno is None:
245 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
293 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
246 else:
294 else:
247 tpl_link = (
295 tpl_link = (
248 f"{{label}} {ColorFilename}{{name}}, line {{lineno}}{ColorNormal}"
296 f"{{label}} {ColorFilename}{{name}}, line {{lineno}}{ColorNormal}"
249 )
297 )
250 else:
298 else:
251 label = "File"
299 label = "File"
252 name = util_path.compress_user(
300 name = util_path.compress_user(
253 py3compat.cast_unicode(file, util_path.fs_encoding)
301 py3compat.cast_unicode(file, util_path.fs_encoding)
254 )
302 )
255 if lineno is None:
303 if lineno is None:
256 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
304 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
257 else:
305 else:
258 # can we make this the more friendly ", line {{lineno}}", or do we need to preserve the formatting with the colon?
306 # can we make this the more friendly ", line {{lineno}}", or do we need to preserve the formatting with the colon?
259 tpl_link = f"{{label}} {ColorFilename}{{name}}:{{lineno}}{ColorNormal}"
307 tpl_link = f"{{label}} {ColorFilename}{{name}}:{{lineno}}{ColorNormal}"
260
308
261 return tpl_link.format(label=label, name=name, lineno=lineno)
309 return tpl_link.format(label=label, name=name, lineno=lineno)
262
310
263 #---------------------------------------------------------------------------
311 #---------------------------------------------------------------------------
264 # Module classes
312 # Module classes
265 class TBTools(colorable.Colorable):
313 class TBTools(colorable.Colorable):
266 """Basic tools used by all traceback printer classes."""
314 """Basic tools used by all traceback printer classes."""
267
315
268 # Number of frames to skip when reporting tracebacks
316 # Number of frames to skip when reporting tracebacks
269 tb_offset = 0
317 tb_offset = 0
270
318
271 def __init__(
319 def __init__(
272 self,
320 self,
273 color_scheme="NoColor",
321 color_scheme="NoColor",
274 call_pdb=False,
322 call_pdb=False,
275 ostream=None,
323 ostream=None,
276 parent=None,
324 parent=None,
277 config=None,
325 config=None,
278 *,
326 *,
279 debugger_cls=None,
327 debugger_cls=None,
280 ):
328 ):
281 # Whether to call the interactive pdb debugger after printing
329 # Whether to call the interactive pdb debugger after printing
282 # tracebacks or not
330 # tracebacks or not
283 super(TBTools, self).__init__(parent=parent, config=config)
331 super(TBTools, self).__init__(parent=parent, config=config)
284 self.call_pdb = call_pdb
332 self.call_pdb = call_pdb
285
333
286 # Output stream to write to. Note that we store the original value in
334 # Output stream to write to. Note that we store the original value in
287 # a private attribute and then make the public ostream a property, so
335 # a private attribute and then make the public ostream a property, so
288 # that we can delay accessing sys.stdout until runtime. The way
336 # that we can delay accessing sys.stdout until runtime. The way
289 # things are written now, the sys.stdout object is dynamically managed
337 # things are written now, the sys.stdout object is dynamically managed
290 # so a reference to it should NEVER be stored statically. This
338 # so a reference to it should NEVER be stored statically. This
291 # property approach confines this detail to a single location, and all
339 # property approach confines this detail to a single location, and all
292 # subclasses can simply access self.ostream for writing.
340 # subclasses can simply access self.ostream for writing.
293 self._ostream = ostream
341 self._ostream = ostream
294
342
295 # Create color table
343 # Create color table
296 self.color_scheme_table = exception_colors()
344 self.color_scheme_table = exception_colors()
297
345
298 self.set_colors(color_scheme)
346 self.set_colors(color_scheme)
299 self.old_scheme = color_scheme # save initial value for toggles
347 self.old_scheme = color_scheme # save initial value for toggles
300 self.debugger_cls = debugger_cls or debugger.Pdb
348 self.debugger_cls = debugger_cls or debugger.Pdb
301
349
302 if call_pdb:
350 if call_pdb:
303 self.pdb = self.debugger_cls()
351 self.pdb = self.debugger_cls()
304 else:
352 else:
305 self.pdb = None
353 self.pdb = None
306
354
307 def _get_ostream(self):
355 def _get_ostream(self):
308 """Output stream that exceptions are written to.
356 """Output stream that exceptions are written to.
309
357
310 Valid values are:
358 Valid values are:
311
359
312 - None: the default, which means that IPython will dynamically resolve
360 - None: the default, which means that IPython will dynamically resolve
313 to sys.stdout. This ensures compatibility with most tools, including
361 to sys.stdout. This ensures compatibility with most tools, including
314 Windows (where plain stdout doesn't recognize ANSI escapes).
362 Windows (where plain stdout doesn't recognize ANSI escapes).
315
363
316 - Any object with 'write' and 'flush' attributes.
364 - Any object with 'write' and 'flush' attributes.
317 """
365 """
318 return sys.stdout if self._ostream is None else self._ostream
366 return sys.stdout if self._ostream is None else self._ostream
319
367
320 def _set_ostream(self, val):
368 def _set_ostream(self, val):
321 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
369 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
322 self._ostream = val
370 self._ostream = val
323
371
324 ostream = property(_get_ostream, _set_ostream)
372 ostream = property(_get_ostream, _set_ostream)
325
373
326 @staticmethod
374 @staticmethod
327 def _get_chained_exception(exception_value):
375 def _get_chained_exception(exception_value):
328 cause = getattr(exception_value, "__cause__", None)
376 cause = getattr(exception_value, "__cause__", None)
329 if cause:
377 if cause:
330 return cause
378 return cause
331 if getattr(exception_value, "__suppress_context__", False):
379 if getattr(exception_value, "__suppress_context__", False):
332 return None
380 return None
333 return getattr(exception_value, "__context__", None)
381 return getattr(exception_value, "__context__", None)
334
382
335 def get_parts_of_chained_exception(
383 def get_parts_of_chained_exception(
336 self, evalue
384 self, evalue
337 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
385 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
338 chained_evalue = self._get_chained_exception(evalue)
386 chained_evalue = self._get_chained_exception(evalue)
339
387
340 if chained_evalue:
388 if chained_evalue:
341 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
389 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
342 return None
390 return None
343
391
344 def prepare_chained_exception_message(self, cause) -> List[Any]:
392 def prepare_chained_exception_message(self, cause) -> List[Any]:
345 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
393 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
346 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
394 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
347
395
348 if cause:
396 if cause:
349 message = [[direct_cause]]
397 message = [[direct_cause]]
350 else:
398 else:
351 message = [[exception_during_handling]]
399 message = [[exception_during_handling]]
352 return message
400 return message
353
401
354 @property
402 @property
355 def has_colors(self) -> bool:
403 def has_colors(self) -> bool:
356 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
404 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
357
405
358 def set_colors(self, *args, **kw):
406 def set_colors(self, *args, **kw):
359 """Shorthand access to the color table scheme selector method."""
407 """Shorthand access to the color table scheme selector method."""
360
408
361 # Set own color table
409 # Set own color table
362 self.color_scheme_table.set_active_scheme(*args, **kw)
410 self.color_scheme_table.set_active_scheme(*args, **kw)
363 # for convenience, set Colors to the active scheme
411 # for convenience, set Colors to the active scheme
364 self.Colors = self.color_scheme_table.active_colors
412 self.Colors = self.color_scheme_table.active_colors
365 # Also set colors of debugger
413 # Also set colors of debugger
366 if hasattr(self, 'pdb') and self.pdb is not None:
414 if hasattr(self, 'pdb') and self.pdb is not None:
367 self.pdb.set_colors(*args, **kw)
415 self.pdb.set_colors(*args, **kw)
368
416
369 def color_toggle(self):
417 def color_toggle(self):
370 """Toggle between the currently active color scheme and NoColor."""
418 """Toggle between the currently active color scheme and NoColor."""
371
419
372 if self.color_scheme_table.active_scheme_name == 'NoColor':
420 if self.color_scheme_table.active_scheme_name == 'NoColor':
373 self.color_scheme_table.set_active_scheme(self.old_scheme)
421 self.color_scheme_table.set_active_scheme(self.old_scheme)
374 self.Colors = self.color_scheme_table.active_colors
422 self.Colors = self.color_scheme_table.active_colors
375 else:
423 else:
376 self.old_scheme = self.color_scheme_table.active_scheme_name
424 self.old_scheme = self.color_scheme_table.active_scheme_name
377 self.color_scheme_table.set_active_scheme('NoColor')
425 self.color_scheme_table.set_active_scheme('NoColor')
378 self.Colors = self.color_scheme_table.active_colors
426 self.Colors = self.color_scheme_table.active_colors
379
427
380 def stb2text(self, stb):
428 def stb2text(self, stb):
381 """Convert a structured traceback (a list) to a string."""
429 """Convert a structured traceback (a list) to a string."""
382 return '\n'.join(stb)
430 return '\n'.join(stb)
383
431
384 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5):
432 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5):
385 """Return formatted traceback.
433 """Return formatted traceback.
386
434
387 Subclasses may override this if they add extra arguments.
435 Subclasses may override this if they add extra arguments.
388 """
436 """
389 tb_list = self.structured_traceback(etype, value, tb,
437 tb_list = self.structured_traceback(etype, value, tb,
390 tb_offset, context)
438 tb_offset, context)
391 return self.stb2text(tb_list)
439 return self.stb2text(tb_list)
392
440
393 def structured_traceback(
441 def structured_traceback(
394 self,
442 self,
395 etype: type,
443 etype: type,
396 evalue: Optional[BaseException],
444 evalue: Optional[BaseException],
397 etb: Optional[TracebackType] = None,
445 etb: Optional[TracebackType] = None,
398 tb_offset: Optional[int] = None,
446 tb_offset: Optional[int] = None,
399 context=5,
447 number_of_lines_of_context: int = 5,
400 ):
448 ):
401 """Return a list of traceback frames.
449 """Return a list of traceback frames.
402
450
403 Must be implemented by each class.
451 Must be implemented by each class.
404 """
452 """
405 raise NotImplementedError()
453 raise NotImplementedError()
406
454
407
455
408 #---------------------------------------------------------------------------
456 #---------------------------------------------------------------------------
409 class ListTB(TBTools):
457 class ListTB(TBTools):
410 """Print traceback information from a traceback list, with optional color.
458 """Print traceback information from a traceback list, with optional color.
411
459
412 Calling requires 3 arguments: (etype, evalue, elist)
460 Calling requires 3 arguments: (etype, evalue, elist)
413 as would be obtained by::
461 as would be obtained by::
414
462
415 etype, evalue, tb = sys.exc_info()
463 etype, evalue, tb = sys.exc_info()
416 if tb:
464 if tb:
417 elist = traceback.extract_tb(tb)
465 elist = traceback.extract_tb(tb)
418 else:
466 else:
419 elist = None
467 elist = None
420
468
421 It can thus be used by programs which need to process the traceback before
469 It can thus be used by programs which need to process the traceback before
422 printing (such as console replacements based on the code module from the
470 printing (such as console replacements based on the code module from the
423 standard library).
471 standard library).
424
472
425 Because they are meant to be called without a full traceback (only a
473 Because they are meant to be called without a full traceback (only a
426 list), instances of this class can't call the interactive pdb debugger."""
474 list), instances of this class can't call the interactive pdb debugger."""
427
475
428
476
429 def __call__(self, etype, value, elist):
477 def __call__(self, etype, value, elist):
430 self.ostream.flush()
478 self.ostream.flush()
431 self.ostream.write(self.text(etype, value, elist))
479 self.ostream.write(self.text(etype, value, elist))
432 self.ostream.write('\n')
480 self.ostream.write('\n')
433
481
434 def _extract_tb(self, tb):
482 def _extract_tb(self, tb):
435 if tb:
483 if tb:
436 return traceback.extract_tb(tb)
484 return traceback.extract_tb(tb)
437 else:
485 else:
438 return None
486 return None
439
487
440 def structured_traceback(
488 def structured_traceback(
441 self,
489 self,
442 etype: type,
490 etype: type,
443 evalue: Optional[BaseException],
491 evalue: Optional[BaseException],
444 etb: Optional[TracebackType] = None,
492 etb: Optional[TracebackType] = None,
445 tb_offset: Optional[int] = None,
493 tb_offset: Optional[int] = None,
446 context=5,
494 context=5,
447 ):
495 ):
448 """Return a color formatted string with the traceback info.
496 """Return a color formatted string with the traceback info.
449
497
450 Parameters
498 Parameters
451 ----------
499 ----------
452 etype : exception type
500 etype : exception type
453 Type of the exception raised.
501 Type of the exception raised.
454 evalue : object
502 evalue : object
455 Data stored in the exception
503 Data stored in the exception
456 etb : list | TracebackType | None
504 etb : list | TracebackType | None
457 If list: List of frames, see class docstring for details.
505 If list: List of frames, see class docstring for details.
458 If Traceback: Traceback of the exception.
506 If Traceback: Traceback of the exception.
459 tb_offset : int, optional
507 tb_offset : int, optional
460 Number of frames in the traceback to skip. If not given, the
508 Number of frames in the traceback to skip. If not given, the
461 instance evalue is used (set in constructor).
509 instance evalue is used (set in constructor).
462 context : int, optional
510 context : int, optional
463 Number of lines of context information to print.
511 Number of lines of context information to print.
464
512
465 Returns
513 Returns
466 -------
514 -------
467 String with formatted exception.
515 String with formatted exception.
468 """
516 """
469 # This is a workaround to get chained_exc_ids in recursive calls
517 # This is a workaround to get chained_exc_ids in recursive calls
470 # etb should not be a tuple if structured_traceback is not recursive
518 # etb should not be a tuple if structured_traceback is not recursive
471 if isinstance(etb, tuple):
519 if isinstance(etb, tuple):
472 etb, chained_exc_ids = etb
520 etb, chained_exc_ids = etb
473 else:
521 else:
474 chained_exc_ids = set()
522 chained_exc_ids = set()
475
523
476 if isinstance(etb, list):
524 if isinstance(etb, list):
477 elist = etb
525 elist = etb
478 elif etb is not None:
526 elif etb is not None:
479 elist = self._extract_tb(etb)
527 elist = self._extract_tb(etb)
480 else:
528 else:
481 elist = []
529 elist = []
482 tb_offset = self.tb_offset if tb_offset is None else tb_offset
530 tb_offset = self.tb_offset if tb_offset is None else tb_offset
483 assert isinstance(tb_offset, int)
531 assert isinstance(tb_offset, int)
484 Colors = self.Colors
532 Colors = self.Colors
485 out_list = []
533 out_list = []
486 if elist:
534 if elist:
487
535
488 if tb_offset and len(elist) > tb_offset:
536 if tb_offset and len(elist) > tb_offset:
489 elist = elist[tb_offset:]
537 elist = elist[tb_offset:]
490
538
491 out_list.append('Traceback %s(most recent call last)%s:' %
539 out_list.append('Traceback %s(most recent call last)%s:' %
492 (Colors.normalEm, Colors.Normal) + '\n')
540 (Colors.normalEm, Colors.Normal) + '\n')
493 out_list.extend(self._format_list(elist))
541 out_list.extend(self._format_list(elist))
494 # The exception info should be a single entry in the list.
542 # The exception info should be a single entry in the list.
495 lines = ''.join(self._format_exception_only(etype, evalue))
543 lines = ''.join(self._format_exception_only(etype, evalue))
496 out_list.append(lines)
544 out_list.append(lines)
497
545
498 exception = self.get_parts_of_chained_exception(evalue)
546 exception = self.get_parts_of_chained_exception(evalue)
499
547
500 if exception and not id(exception[1]) in chained_exc_ids:
548 if exception and (id(exception[1]) not in chained_exc_ids):
501 chained_exception_message = (
549 chained_exception_message = (
502 self.prepare_chained_exception_message(evalue.__cause__)[0]
550 self.prepare_chained_exception_message(evalue.__cause__)[0]
503 if evalue is not None
551 if evalue is not None
504 else ""
552 else ""
505 )
553 )
506 etype, evalue, etb = exception
554 etype, evalue, etb = exception
507 # Trace exception to avoid infinite 'cause' loop
555 # Trace exception to avoid infinite 'cause' loop
508 chained_exc_ids.add(id(exception[1]))
556 chained_exc_ids.add(id(exception[1]))
509 chained_exceptions_tb_offset = 0
557 chained_exceptions_tb_offset = 0
510 out_list = (
558 out_list = (
511 self.structured_traceback(
559 self.structured_traceback(
512 etype, evalue, (etb, chained_exc_ids),
560 etype,
513 chained_exceptions_tb_offset, context)
561 evalue,
562 (etb, chained_exc_ids), # type: ignore
563 chained_exceptions_tb_offset,
564 context,
565 )
514 + chained_exception_message
566 + chained_exception_message
515 + out_list)
567 + out_list)
516
568
517 return out_list
569 return out_list
518
570
519 def _format_list(self, extracted_list):
571 def _format_list(self, extracted_list):
520 """Format a list of traceback entry tuples for printing.
572 """Format a list of traceback entry tuples for printing.
521
573
522 Given a list of tuples as returned by extract_tb() or
574 Given a list of tuples as returned by extract_tb() or
523 extract_stack(), return a list of strings ready for printing.
575 extract_stack(), return a list of strings ready for printing.
524 Each string in the resulting list corresponds to the item with the
576 Each string in the resulting list corresponds to the item with the
525 same index in the argument list. Each string ends in a newline;
577 same index in the argument list. Each string ends in a newline;
526 the strings may contain internal newlines as well, for those items
578 the strings may contain internal newlines as well, for those items
527 whose source text line is not None.
579 whose source text line is not None.
528
580
529 Lifted almost verbatim from traceback.py
581 Lifted almost verbatim from traceback.py
530 """
582 """
531
583
532 Colors = self.Colors
584 Colors = self.Colors
533 list = []
585 list = []
534 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
586 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
535 normalCol, nameCol, fileCol, lineCol = (
587 normalCol, nameCol, fileCol, lineCol = (
536 # Emphasize the last entry
588 # Emphasize the last entry
537 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
589 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
538 if ind == len(extracted_list) - 1
590 if ind == len(extracted_list) - 1
539 else (Colors.Normal, Colors.name, Colors.filename, "")
591 else (Colors.Normal, Colors.name, Colors.filename, "")
540 )
592 )
541
593
542 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
594 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
543 item = f"{normalCol} {fns}"
595 item = f"{normalCol} {fns}"
544
596
545 if name != "<module>":
597 if name != "<module>":
546 item += f" in {nameCol}{name}{normalCol}\n"
598 item += f" in {nameCol}{name}{normalCol}\n"
547 else:
599 else:
548 item += "\n"
600 item += "\n"
549 if line:
601 if line:
550 item += f"{lineCol} {line.strip()}{normalCol}\n"
602 item += f"{lineCol} {line.strip()}{normalCol}\n"
551 list.append(item)
603 list.append(item)
552
604
553 return list
605 return list
554
606
555 def _format_exception_only(self, etype, value):
607 def _format_exception_only(self, etype, value):
556 """Format the exception part of a traceback.
608 """Format the exception part of a traceback.
557
609
558 The arguments are the exception type and value such as given by
610 The arguments are the exception type and value such as given by
559 sys.exc_info()[:2]. The return value is a list of strings, each ending
611 sys.exc_info()[:2]. The return value is a list of strings, each ending
560 in a newline. Normally, the list contains a single string; however,
612 in a newline. Normally, the list contains a single string; however,
561 for SyntaxError exceptions, it contains several lines that (when
613 for SyntaxError exceptions, it contains several lines that (when
562 printed) display detailed information about where the syntax error
614 printed) display detailed information about where the syntax error
563 occurred. The message indicating which exception occurred is the
615 occurred. The message indicating which exception occurred is the
564 always last string in the list.
616 always last string in the list.
565
617
566 Also lifted nearly verbatim from traceback.py
618 Also lifted nearly verbatim from traceback.py
567 """
619 """
568 have_filedata = False
620 have_filedata = False
569 Colors = self.Colors
621 Colors = self.Colors
570 list = []
622 list = []
571 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
623 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
572 if value is None:
624 if value is None:
573 # Not sure if this can still happen in Python 2.6 and above
625 # Not sure if this can still happen in Python 2.6 and above
574 list.append(stype + '\n')
626 list.append(stype + '\n')
575 else:
627 else:
576 if issubclass(etype, SyntaxError):
628 if issubclass(etype, SyntaxError):
577 have_filedata = True
629 have_filedata = True
578 if not value.filename: value.filename = "<string>"
630 if not value.filename: value.filename = "<string>"
579 if value.lineno:
631 if value.lineno:
580 lineno = value.lineno
632 lineno = value.lineno
581 textline = linecache.getline(value.filename, value.lineno)
633 textline = linecache.getline(value.filename, value.lineno)
582 else:
634 else:
583 lineno = "unknown"
635 lineno = "unknown"
584 textline = ""
636 textline = ""
585 list.append(
637 list.append(
586 "%s %s%s\n"
638 "%s %s%s\n"
587 % (
639 % (
588 Colors.normalEm,
640 Colors.normalEm,
589 _format_filename(
641 _format_filename(
590 value.filename,
642 value.filename,
591 Colors.filenameEm,
643 Colors.filenameEm,
592 Colors.normalEm,
644 Colors.normalEm,
593 lineno=(None if lineno == "unknown" else lineno),
645 lineno=(None if lineno == "unknown" else lineno),
594 ),
646 ),
595 Colors.Normal,
647 Colors.Normal,
596 )
648 )
597 )
649 )
598 if textline == "":
650 if textline == "":
599 textline = py3compat.cast_unicode(value.text, "utf-8")
651 textline = py3compat.cast_unicode(value.text, "utf-8")
600
652
601 if textline is not None:
653 if textline is not None:
602 i = 0
654 i = 0
603 while i < len(textline) and textline[i].isspace():
655 while i < len(textline) and textline[i].isspace():
604 i += 1
656 i += 1
605 list.append('%s %s%s\n' % (Colors.line,
657 list.append('%s %s%s\n' % (Colors.line,
606 textline.strip(),
658 textline.strip(),
607 Colors.Normal))
659 Colors.Normal))
608 if value.offset is not None:
660 if value.offset is not None:
609 s = ' '
661 s = ' '
610 for c in textline[i:value.offset - 1]:
662 for c in textline[i:value.offset - 1]:
611 if c.isspace():
663 if c.isspace():
612 s += c
664 s += c
613 else:
665 else:
614 s += ' '
666 s += ' '
615 list.append('%s%s^%s\n' % (Colors.caret, s,
667 list.append('%s%s^%s\n' % (Colors.caret, s,
616 Colors.Normal))
668 Colors.Normal))
617
669
618 try:
670 try:
619 s = value.msg
671 s = value.msg
620 except Exception:
672 except Exception:
621 s = self._some_str(value)
673 s = self._some_str(value)
622 if s:
674 if s:
623 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
675 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
624 Colors.Normal, s))
676 Colors.Normal, s))
625 else:
677 else:
626 list.append('%s\n' % stype)
678 list.append('%s\n' % stype)
627
679
628 # sync with user hooks
680 # sync with user hooks
629 if have_filedata:
681 if have_filedata:
630 ipinst = get_ipython()
682 ipinst = get_ipython()
631 if ipinst is not None:
683 if ipinst is not None:
632 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
684 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
633
685
634 return list
686 return list
635
687
636 def get_exception_only(self, etype, value):
688 def get_exception_only(self, etype, value):
637 """Only print the exception type and message, without a traceback.
689 """Only print the exception type and message, without a traceback.
638
690
639 Parameters
691 Parameters
640 ----------
692 ----------
641 etype : exception type
693 etype : exception type
642 value : exception value
694 value : exception value
643 """
695 """
644 return ListTB.structured_traceback(self, etype, value)
696 return ListTB.structured_traceback(self, etype, value)
645
697
646 def show_exception_only(self, etype, evalue):
698 def show_exception_only(self, etype, evalue):
647 """Only print the exception type and message, without a traceback.
699 """Only print the exception type and message, without a traceback.
648
700
649 Parameters
701 Parameters
650 ----------
702 ----------
651 etype : exception type
703 etype : exception type
652 evalue : exception value
704 evalue : exception value
653 """
705 """
654 # This method needs to use __call__ from *this* class, not the one from
706 # This method needs to use __call__ from *this* class, not the one from
655 # a subclass whose signature or behavior may be different
707 # a subclass whose signature or behavior may be different
656 ostream = self.ostream
708 ostream = self.ostream
657 ostream.flush()
709 ostream.flush()
658 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
710 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
659 ostream.flush()
711 ostream.flush()
660
712
661 def _some_str(self, value):
713 def _some_str(self, value):
662 # Lifted from traceback.py
714 # Lifted from traceback.py
663 try:
715 try:
664 return py3compat.cast_unicode(str(value))
716 return py3compat.cast_unicode(str(value))
665 except:
717 except:
666 return u'<unprintable %s object>' % type(value).__name__
718 return u'<unprintable %s object>' % type(value).__name__
667
719
668
720
669 class FrameInfo:
721 class FrameInfo:
670 """
722 """
671 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
723 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
672 really long frames.
724 really long frames.
673 """
725 """
674
726
675 description: Optional[str]
727 description: Optional[str]
676 filename: str
728 filename: Optional[str]
677 lineno: int
729 lineno: Tuple[int]
730 # number of context lines to use
731 context: Optional[int]
678
732
679 @classmethod
733 @classmethod
680 def _from_stack_data_FrameInfo(cls, frame_info):
734 def _from_stack_data_FrameInfo(cls, frame_info):
681 return cls(
735 return cls(
682 getattr(frame_info, "description", None),
736 getattr(frame_info, "description", None),
683 getattr(frame_info, "filename", None),
737 getattr(frame_info, "filename", None), # type: ignore[arg-type]
684 getattr(frame_info, "lineno", None),
738 getattr(frame_info, "lineno", None), # type: ignore[arg-type]
685 getattr(frame_info, "frame", None),
739 getattr(frame_info, "frame", None),
686 getattr(frame_info, "code", None),
740 getattr(frame_info, "code", None),
687 sd=frame_info,
741 sd=frame_info,
742 context=None,
688 )
743 )
689
744
690 def __init__(self, description, filename, lineno, frame, code, sd=None):
745 def __init__(
746 self,
747 description: Optional[str],
748 filename: str,
749 lineno: Tuple[int],
750 frame,
751 code,
752 *,
753 sd=None,
754 context=None,
755 ):
691 self.description = description
756 self.description = description
692 self.filename = filename
757 self.filename = filename
693 self.lineno = lineno
758 self.lineno = lineno
694 self.frame = frame
759 self.frame = frame
695 self.code = code
760 self.code = code
696 self._sd = sd
761 self._sd = sd
762 self.context = context
697
763
698 # self.lines = []
764 # self.lines = []
699 if sd is None:
765 if sd is None:
700 ix = inspect.getsourcelines(frame)
766 ix = inspect.getsourcelines(frame)
701 self.raw_lines = ix[0]
767 self.raw_lines = ix[0]
702
768
703 @property
769 @property
704 def variables_in_executing_piece(self):
770 def variables_in_executing_piece(self):
705 if self._sd:
771 if self._sd:
706 return self._sd.variables_in_executing_piece
772 return self._sd.variables_in_executing_piece
707 else:
773 else:
708 return []
774 return []
709
775
710 @property
776 @property
711 def lines(self):
777 def lines(self):
712 return self._sd.lines
778 return self._sd.lines
713
779
714 @property
780 @property
715 def executing(self):
781 def executing(self):
716 if self._sd:
782 if self._sd:
717 return self._sd.executing
783 return self._sd.executing
718 else:
784 else:
719 return None
785 return None
720
786
721
787
722 # ----------------------------------------------------------------------------
788 # ----------------------------------------------------------------------------
723 class VerboseTB(TBTools):
789 class VerboseTB(TBTools):
724 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
790 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
725 of HTML. Requires inspect and pydoc. Crazy, man.
791 of HTML. Requires inspect and pydoc. Crazy, man.
726
792
727 Modified version which optionally strips the topmost entries from the
793 Modified version which optionally strips the topmost entries from the
728 traceback, to be used with alternate interpreters (because their own code
794 traceback, to be used with alternate interpreters (because their own code
729 would appear in the traceback)."""
795 would appear in the traceback)."""
730
796
731 _tb_highlight = "bg:ansiyellow"
797 _tb_highlight = "bg:ansiyellow"
732
798
733 def __init__(
799 def __init__(
734 self,
800 self,
735 color_scheme: str = "Linux",
801 color_scheme: str = "Linux",
736 call_pdb: bool = False,
802 call_pdb: bool = False,
737 ostream=None,
803 ostream=None,
738 tb_offset: int = 0,
804 tb_offset: int = 0,
739 long_header: bool = False,
805 long_header: bool = False,
740 include_vars: bool = True,
806 include_vars: bool = True,
741 check_cache=None,
807 check_cache=None,
742 debugger_cls=None,
808 debugger_cls=None,
743 parent=None,
809 parent=None,
744 config=None,
810 config=None,
745 ):
811 ):
746 """Specify traceback offset, headers and color scheme.
812 """Specify traceback offset, headers and color scheme.
747
813
748 Define how many frames to drop from the tracebacks. Calling it with
814 Define how many frames to drop from the tracebacks. Calling it with
749 tb_offset=1 allows use of this handler in interpreters which will have
815 tb_offset=1 allows use of this handler in interpreters which will have
750 their own code at the top of the traceback (VerboseTB will first
816 their own code at the top of the traceback (VerboseTB will first
751 remove that frame before printing the traceback info)."""
817 remove that frame before printing the traceback info)."""
752 TBTools.__init__(
818 TBTools.__init__(
753 self,
819 self,
754 color_scheme=color_scheme,
820 color_scheme=color_scheme,
755 call_pdb=call_pdb,
821 call_pdb=call_pdb,
756 ostream=ostream,
822 ostream=ostream,
757 parent=parent,
823 parent=parent,
758 config=config,
824 config=config,
759 debugger_cls=debugger_cls,
825 debugger_cls=debugger_cls,
760 )
826 )
761 self.tb_offset = tb_offset
827 self.tb_offset = tb_offset
762 self.long_header = long_header
828 self.long_header = long_header
763 self.include_vars = include_vars
829 self.include_vars = include_vars
764 # By default we use linecache.checkcache, but the user can provide a
830 # By default we use linecache.checkcache, but the user can provide a
765 # different check_cache implementation. This was formerly used by the
831 # different check_cache implementation. This was formerly used by the
766 # IPython kernel for interactive code, but is no longer necessary.
832 # IPython kernel for interactive code, but is no longer necessary.
767 if check_cache is None:
833 if check_cache is None:
768 check_cache = linecache.checkcache
834 check_cache = linecache.checkcache
769 self.check_cache = check_cache
835 self.check_cache = check_cache
770
836
771 self.skip_hidden = True
837 self.skip_hidden = True
772
838
773 def format_record(self, frame_info: FrameInfo):
839 def format_record(self, frame_info: FrameInfo):
774 """Format a single stack frame"""
840 """Format a single stack frame"""
775 assert isinstance(frame_info, FrameInfo)
841 assert isinstance(frame_info, FrameInfo)
776 Colors = self.Colors # just a shorthand + quicker name lookup
842 Colors = self.Colors # just a shorthand + quicker name lookup
777 ColorsNormal = Colors.Normal # used a lot
843 ColorsNormal = Colors.Normal # used a lot
778
844
779 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
845 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
780 return ' %s[... skipping similar frames: %s]%s\n' % (
846 return ' %s[... skipping similar frames: %s]%s\n' % (
781 Colors.excName, frame_info.description, ColorsNormal)
847 Colors.excName, frame_info.description, ColorsNormal)
782
848
783 indent = " " * INDENT_SIZE
849 indent = " " * INDENT_SIZE
784 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
850 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
785 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
851 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
786 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
852 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
787 Colors.vName,
853 Colors.vName,
788 Colors.valEm,
854 Colors.valEm,
789 ColorsNormal,
855 ColorsNormal,
790 )
856 )
791 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
857 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
792
858
793 link = _format_filename(
859 link = _format_filename(
794 frame_info.filename,
860 frame_info.filename,
795 Colors.filenameEm,
861 Colors.filenameEm,
796 ColorsNormal,
862 ColorsNormal,
797 lineno=frame_info.lineno,
863 lineno=frame_info.lineno,
798 )
864 )
799 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
865 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
800 if frame_info.executing is not None:
866 if frame_info.executing is not None:
801 func = frame_info.executing.code_qualname()
867 func = frame_info.executing.code_qualname()
802 else:
868 else:
803 func = "?"
869 func = "?"
804 if func == "<module>":
870 if func == "<module>":
805 call = ""
871 call = ""
806 else:
872 else:
807 # Decide whether to include variable details or not
873 # Decide whether to include variable details or not
808 var_repr = eqrepr if self.include_vars else nullrepr
874 var_repr = eqrepr if self.include_vars else nullrepr
809 try:
875 try:
810 scope = inspect.formatargvalues(
876 scope = inspect.formatargvalues(
811 args, varargs, varkw, locals_, formatvalue=var_repr
877 args, varargs, varkw, locals_, formatvalue=var_repr
812 )
878 )
813 call = tpl_call.format(file=func, scope=scope)
879 call = tpl_call.format(file=func, scope=scope)
814 except KeyError:
880 except KeyError:
815 # This happens in situations like errors inside generator
881 # This happens in situations like errors inside generator
816 # expressions, where local variables are listed in the
882 # expressions, where local variables are listed in the
817 # line, but can't be extracted from the frame. I'm not
883 # line, but can't be extracted from the frame. I'm not
818 # 100% sure this isn't actually a bug in inspect itself,
884 # 100% sure this isn't actually a bug in inspect itself,
819 # but since there's no info for us to compute with, the
885 # but since there's no info for us to compute with, the
820 # best we can do is report the failure and move on. Here
886 # best we can do is report the failure and move on. Here
821 # we must *not* call any traceback construction again,
887 # we must *not* call any traceback construction again,
822 # because that would mess up use of %debug later on. So we
888 # because that would mess up use of %debug later on. So we
823 # simply report the failure and move on. The only
889 # simply report the failure and move on. The only
824 # limitation will be that this frame won't have locals
890 # limitation will be that this frame won't have locals
825 # listed in the call signature. Quite subtle problem...
891 # listed in the call signature. Quite subtle problem...
826 # I can't think of a good way to validate this in a unit
892 # I can't think of a good way to validate this in a unit
827 # test, but running a script consisting of:
893 # test, but running a script consisting of:
828 # dict( (k,v.strip()) for (k,v) in range(10) )
894 # dict( (k,v.strip()) for (k,v) in range(10) )
829 # will illustrate the error, if this exception catch is
895 # will illustrate the error, if this exception catch is
830 # disabled.
896 # disabled.
831 call = tpl_call_fail % func
897 call = tpl_call_fail % func
832
898
833 lvals = ''
899 lvals = ''
834 lvals_list = []
900 lvals_list = []
835 if self.include_vars:
901 if self.include_vars:
836 try:
902 try:
837 # we likely want to fix stackdata at some point, but
903 # we likely want to fix stackdata at some point, but
838 # still need a workaround.
904 # still need a workaround.
839 fibp = frame_info.variables_in_executing_piece
905 fibp = frame_info.variables_in_executing_piece
840 for var in fibp:
906 for var in fibp:
841 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
907 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
842 except Exception:
908 except Exception:
843 lvals_list.append(
909 lvals_list.append(
844 "Exception trying to inspect frame. No more locals available."
910 "Exception trying to inspect frame. No more locals available."
845 )
911 )
846 if lvals_list:
912 if lvals_list:
847 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
913 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
848
914
849 result = f'{link}{", " if call else ""}{call}\n'
915 result = f'{link}{", " if call else ""}{call}\n'
850 if frame_info._sd is None:
916 if frame_info._sd is None:
851 assert False
852 # fast fallback if file is too long
917 # fast fallback if file is too long
853 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
918 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
854 link = tpl_link % util_path.compress_user(frame_info.filename)
919 link = tpl_link % util_path.compress_user(frame_info.filename)
855 level = "%s %s\n" % (link, call)
920 level = "%s %s\n" % (link, call)
856 _line_format = PyColorize.Parser(
921 _line_format = PyColorize.Parser(
857 style=self.color_scheme_table.active_scheme_name, parent=self
922 style=self.color_scheme_table.active_scheme_name, parent=self
858 ).format2
923 ).format2
859 first_line = frame_info.code.co_firstlineno
924 first_line = frame_info.code.co_firstlineno
860 current_line = frame_info.lineno[0]
925 current_line = frame_info.lineno[0]
926 raw_lines = frame_info.raw_lines
927 index = current_line - first_line
928
929 if index >= frame_info.context:
930 start = max(index - frame_info.context, 0)
931 stop = index + frame_info.context
932 index = frame_info.context
933 else:
934 start = 0
935 stop = index + frame_info.context
936 raw_lines = raw_lines[start:stop]
937
861 return "%s%s" % (
938 return "%s%s" % (
862 level,
939 level,
863 "".join(
940 "".join(
864 _simple_format_traceback_lines(
941 _simple_format_traceback_lines(
865 current_line,
942 current_line,
866 current_line - first_line,
943 index,
867 frame_info.raw_lines,
944 raw_lines,
868 Colors,
945 Colors,
869 lvals,
946 lvals,
870 _line_format,
947 _line_format,
871 )
948 )
872 ),
949 ),
873 )
950 )
874 # result += "\n".join(frame_info.raw_lines)
951 # result += "\n".join(frame_info.raw_lines)
875 else:
952 else:
876 result += "".join(
953 result += "".join(
877 _format_traceback_lines(
954 _format_traceback_lines(
878 frame_info.lines, Colors, self.has_colors, lvals
955 frame_info.lines, Colors, self.has_colors, lvals
879 )
956 )
880 )
957 )
881 return result
958 return result
882
959
883 def prepare_header(self, etype: str, long_version: bool = False):
960 def prepare_header(self, etype: str, long_version: bool = False):
884 colors = self.Colors # just a shorthand + quicker name lookup
961 colors = self.Colors # just a shorthand + quicker name lookup
885 colorsnormal = colors.Normal # used a lot
962 colorsnormal = colors.Normal # used a lot
886 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
963 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
887 width = min(75, get_terminal_size()[0])
964 width = min(75, get_terminal_size()[0])
888 if long_version:
965 if long_version:
889 # Header with the exception type, python version, and date
966 # Header with the exception type, python version, and date
890 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
967 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
891 date = time.ctime(time.time())
968 date = time.ctime(time.time())
892
969
893 head = "%s%s%s\n%s%s%s\n%s" % (
970 head = "%s%s%s\n%s%s%s\n%s" % (
894 colors.topline,
971 colors.topline,
895 "-" * width,
972 "-" * width,
896 colorsnormal,
973 colorsnormal,
897 exc,
974 exc,
898 " " * (width - len(etype) - len(pyver)),
975 " " * (width - len(etype) - len(pyver)),
899 pyver,
976 pyver,
900 date.rjust(width),
977 date.rjust(width),
901 )
978 )
902 head += (
979 head += (
903 "\nA problem occurred executing Python code. Here is the sequence of function"
980 "\nA problem occurred executing Python code. Here is the sequence of function"
904 "\ncalls leading up to the error, with the most recent (innermost) call last."
981 "\ncalls leading up to the error, with the most recent (innermost) call last."
905 )
982 )
906 else:
983 else:
907 # Simplified header
984 # Simplified header
908 head = "%s%s" % (
985 head = "%s%s" % (
909 exc,
986 exc,
910 "Traceback (most recent call last)".rjust(width - len(etype)),
987 "Traceback (most recent call last)".rjust(width - len(etype)),
911 )
988 )
912
989
913 return head
990 return head
914
991
915 def format_exception(self, etype, evalue):
992 def format_exception(self, etype, evalue):
916 colors = self.Colors # just a shorthand + quicker name lookup
993 colors = self.Colors # just a shorthand + quicker name lookup
917 colorsnormal = colors.Normal # used a lot
994 colorsnormal = colors.Normal # used a lot
918 # Get (safely) a string form of the exception info
995 # Get (safely) a string form of the exception info
919 try:
996 try:
920 etype_str, evalue_str = map(str, (etype, evalue))
997 etype_str, evalue_str = map(str, (etype, evalue))
921 except:
998 except:
922 # User exception is improperly defined.
999 # User exception is improperly defined.
923 etype, evalue = str, sys.exc_info()[:2]
1000 etype, evalue = str, sys.exc_info()[:2]
924 etype_str, evalue_str = map(str, (etype, evalue))
1001 etype_str, evalue_str = map(str, (etype, evalue))
925 # ... and format it
1002 # ... and format it
926 return ['%s%s%s: %s' % (colors.excName, etype_str,
1003 return ['%s%s%s: %s' % (colors.excName, etype_str,
927 colorsnormal, py3compat.cast_unicode(evalue_str))]
1004 colorsnormal, py3compat.cast_unicode(evalue_str))]
928
1005
929 def format_exception_as_a_whole(
1006 def format_exception_as_a_whole(
930 self,
1007 self,
931 etype: type,
1008 etype: type,
932 evalue: Optional[BaseException],
1009 evalue: Optional[BaseException],
933 etb: Optional[TracebackType],
1010 etb: Optional[TracebackType],
934 number_of_lines_of_context,
1011 number_of_lines_of_context,
935 tb_offset: Optional[int],
1012 tb_offset: Optional[int],
936 ):
1013 ):
937 """Formats the header, traceback and exception message for a single exception.
1014 """Formats the header, traceback and exception message for a single exception.
938
1015
939 This may be called multiple times by Python 3 exception chaining
1016 This may be called multiple times by Python 3 exception chaining
940 (PEP 3134).
1017 (PEP 3134).
941 """
1018 """
942 # some locals
1019 # some locals
943 orig_etype = etype
1020 orig_etype = etype
944 try:
1021 try:
945 etype = etype.__name__
1022 etype = etype.__name__ # type: ignore
946 except AttributeError:
1023 except AttributeError:
947 pass
1024 pass
948
1025
949 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1026 tb_offset = self.tb_offset if tb_offset is None else tb_offset
950 assert isinstance(tb_offset, int)
1027 assert isinstance(tb_offset, int)
951 head = self.prepare_header(etype, self.long_header)
1028 head = self.prepare_header(str(etype), self.long_header)
952 records = (
1029 records = (
953 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
1030 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
954 )
1031 )
955
1032
956 frames = []
1033 frames = []
957 skipped = 0
1034 skipped = 0
958 lastrecord = len(records) - 1
1035 lastrecord = len(records) - 1
959 for i, record in enumerate(records):
1036 for i, record in enumerate(records):
960 if (
1037 if (
961 not isinstance(record._sd, stack_data.RepeatedFrames)
1038 not isinstance(record._sd, stack_data.RepeatedFrames)
962 and self.skip_hidden
1039 and self.skip_hidden
963 ):
1040 ):
964 if (
1041 if (
965 record.frame.f_locals.get("__tracebackhide__", 0)
1042 record.frame.f_locals.get("__tracebackhide__", 0)
966 and i != lastrecord
1043 and i != lastrecord
967 ):
1044 ):
968 skipped += 1
1045 skipped += 1
969 continue
1046 continue
970 if skipped:
1047 if skipped:
971 Colors = self.Colors # just a shorthand + quicker name lookup
1048 Colors = self.Colors # just a shorthand + quicker name lookup
972 ColorsNormal = Colors.Normal # used a lot
1049 ColorsNormal = Colors.Normal # used a lot
973 frames.append(
1050 frames.append(
974 " %s[... skipping hidden %s frame]%s\n"
1051 " %s[... skipping hidden %s frame]%s\n"
975 % (Colors.excName, skipped, ColorsNormal)
1052 % (Colors.excName, skipped, ColorsNormal)
976 )
1053 )
977 skipped = 0
1054 skipped = 0
978 frames.append(self.format_record(record))
1055 frames.append(self.format_record(record))
979 if skipped:
1056 if skipped:
980 Colors = self.Colors # just a shorthand + quicker name lookup
1057 Colors = self.Colors # just a shorthand + quicker name lookup
981 ColorsNormal = Colors.Normal # used a lot
1058 ColorsNormal = Colors.Normal # used a lot
982 frames.append(
1059 frames.append(
983 " %s[... skipping hidden %s frame]%s\n"
1060 " %s[... skipping hidden %s frame]%s\n"
984 % (Colors.excName, skipped, ColorsNormal)
1061 % (Colors.excName, skipped, ColorsNormal)
985 )
1062 )
986
1063
987 formatted_exception = self.format_exception(etype, evalue)
1064 formatted_exception = self.format_exception(etype, evalue)
988 if records:
1065 if records:
989 frame_info = records[-1]
1066 frame_info = records[-1]
990 ipinst = get_ipython()
1067 ipinst = get_ipython()
991 if ipinst is not None:
1068 if ipinst is not None:
992 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
1069 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
993
1070
994 return [[head] + frames + [''.join(formatted_exception[0])]]
1071 return [[head] + frames + [''.join(formatted_exception[0])]]
995
1072
996 def get_records(
1073 def get_records(
997 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
1074 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
998 ):
1075 ):
999 assert etb is not None
1076 assert etb is not None
1000 context = number_of_lines_of_context - 1
1077 context = number_of_lines_of_context - 1
1001 after = context // 2
1078 after = context // 2
1002 before = context - after
1079 before = context - after
1003 if self.has_colors:
1080 if self.has_colors:
1004 style = get_style_by_name("default")
1081 style = get_style_by_name("default")
1005 style = stack_data.style_with_executing_node(style, self._tb_highlight)
1082 style = stack_data.style_with_executing_node(style, self._tb_highlight)
1006 formatter = Terminal256Formatter(style=style)
1083 formatter = Terminal256Formatter(style=style)
1007 else:
1084 else:
1008 formatter = None
1085 formatter = None
1009 options = stack_data.Options(
1086 options = stack_data.Options(
1010 before=before,
1087 before=before,
1011 after=after,
1088 after=after,
1012 pygments_formatter=formatter,
1089 pygments_formatter=formatter,
1013 )
1090 )
1014
1091
1015 # Let's estimate the amount of code we will have to parse/highlight.
1092 # Let's estimate the amount of code we will have to parse/highlight.
1016 cf: Optional[TracebackType] = etb
1093 cf: Optional[TracebackType] = etb
1017 max_len = 0
1094 max_len = 0
1018 tbs = []
1095 tbs = []
1019 while cf is not None:
1096 while cf is not None:
1020 try:
1097 try:
1021 source_file = inspect.getsourcefile(etb.tb_frame)
1098 mod = inspect.getmodule(cf.tb_frame)
1022 lines, first = inspect.getsourcelines(etb.tb_frame)
1099 if mod is not None:
1100 mod_name = mod.__name__
1101 root_name, *_ = mod_name.split(".")
1102 if root_name == "IPython":
1103 cf = cf.tb_next
1104 continue
1105 max_len = get_line_number_of_frame(cf.tb_frame)
1106
1023 except OSError:
1107 except OSError:
1024 max_len = float("-inf")
1108 max_len = 0
1025 break
1109 max_len = max(max_len, max_len)
1026 max_len = max(max_len, first + len(lines))
1027 tbs.append(cf)
1110 tbs.append(cf)
1028 cf = cf.tb_next
1111 cf = getattr(cf, "tb_next", None)
1029
1112
1030 if max_len > FAST_THRESHOLD:
1113 if max_len > FAST_THRESHOLD:
1031 FIs = []
1114 FIs = []
1032 for tb in tbs:
1115 for tb in tbs:
1033 frame = tb.tb_frame
1116 frame = tb.tb_frame # type: ignore
1034 lineno = (frame.f_lineno,)
1117 lineno = (frame.f_lineno,)
1035 code = frame.f_code
1118 code = frame.f_code
1036 filename = code.co_filename
1119 filename = code.co_filename
1037 FIs.append(FrameInfo("Raw frame", filename, lineno, frame, code))
1120 # TODO: Here we need to use before/after/
1121 FIs.append(
1122 FrameInfo(
1123 "Raw frame", filename, lineno, frame, code, context=context
1124 )
1125 )
1038 return FIs
1126 return FIs
1039 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1127 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1040 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1128 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1041 return res
1129 return res
1042
1130
1043 def structured_traceback(
1131 def structured_traceback(
1044 self,
1132 self,
1045 etype: type,
1133 etype: type,
1046 evalue: Optional[BaseException],
1134 evalue: Optional[BaseException],
1047 etb: Optional[TracebackType],
1135 etb: Optional[TracebackType] = None,
1048 tb_offset: Optional[int] = None,
1136 tb_offset: Optional[int] = None,
1049 number_of_lines_of_context: int = 5,
1137 number_of_lines_of_context: int = 5,
1050 ):
1138 ):
1051 """Return a nice text document describing the traceback."""
1139 """Return a nice text document describing the traceback."""
1052 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1140 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1053 tb_offset)
1141 tb_offset)
1054
1142
1055 colors = self.Colors # just a shorthand + quicker name lookup
1143 colors = self.Colors # just a shorthand + quicker name lookup
1056 colorsnormal = colors.Normal # used a lot
1144 colorsnormal = colors.Normal # used a lot
1057 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1145 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1058 structured_traceback_parts = [head]
1146 structured_traceback_parts = [head]
1059 chained_exceptions_tb_offset = 0
1147 chained_exceptions_tb_offset = 0
1060 lines_of_context = 3
1148 lines_of_context = 3
1061 formatted_exceptions = formatted_exception
1149 formatted_exceptions = formatted_exception
1062 exception = self.get_parts_of_chained_exception(evalue)
1150 exception = self.get_parts_of_chained_exception(evalue)
1063 if exception:
1151 if exception:
1064 assert evalue is not None
1152 assert evalue is not None
1065 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1153 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1066 etype, evalue, etb = exception
1154 etype, evalue, etb = exception
1067 else:
1155 else:
1068 evalue = None
1156 evalue = None
1069 chained_exc_ids = set()
1157 chained_exc_ids = set()
1070 while evalue:
1158 while evalue:
1071 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1159 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1072 chained_exceptions_tb_offset)
1160 chained_exceptions_tb_offset)
1073 exception = self.get_parts_of_chained_exception(evalue)
1161 exception = self.get_parts_of_chained_exception(evalue)
1074
1162
1075 if exception and not id(exception[1]) in chained_exc_ids:
1163 if exception and not id(exception[1]) in chained_exc_ids:
1076 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1164 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1077 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1165 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1078 etype, evalue, etb = exception
1166 etype, evalue, etb = exception
1079 else:
1167 else:
1080 evalue = None
1168 evalue = None
1081
1169
1082 # we want to see exceptions in a reversed order:
1170 # we want to see exceptions in a reversed order:
1083 # the first exception should be on top
1171 # the first exception should be on top
1084 for formatted_exception in reversed(formatted_exceptions):
1172 for formatted_exception in reversed(formatted_exceptions):
1085 structured_traceback_parts += formatted_exception
1173 structured_traceback_parts += formatted_exception
1086
1174
1087 return structured_traceback_parts
1175 return structured_traceback_parts
1088
1176
1089 def debugger(self, force: bool = False):
1177 def debugger(self, force: bool = False):
1090 """Call up the pdb debugger if desired, always clean up the tb
1178 """Call up the pdb debugger if desired, always clean up the tb
1091 reference.
1179 reference.
1092
1180
1093 Keywords:
1181 Keywords:
1094
1182
1095 - force(False): by default, this routine checks the instance call_pdb
1183 - force(False): by default, this routine checks the instance call_pdb
1096 flag and does not actually invoke the debugger if the flag is false.
1184 flag and does not actually invoke the debugger if the flag is false.
1097 The 'force' option forces the debugger to activate even if the flag
1185 The 'force' option forces the debugger to activate even if the flag
1098 is false.
1186 is false.
1099
1187
1100 If the call_pdb flag is set, the pdb interactive debugger is
1188 If the call_pdb flag is set, the pdb interactive debugger is
1101 invoked. In all cases, the self.tb reference to the current traceback
1189 invoked. In all cases, the self.tb reference to the current traceback
1102 is deleted to prevent lingering references which hamper memory
1190 is deleted to prevent lingering references which hamper memory
1103 management.
1191 management.
1104
1192
1105 Note that each call to pdb() does an 'import readline', so if your app
1193 Note that each call to pdb() does an 'import readline', so if your app
1106 requires a special setup for the readline completers, you'll have to
1194 requires a special setup for the readline completers, you'll have to
1107 fix that by hand after invoking the exception handler."""
1195 fix that by hand after invoking the exception handler."""
1108
1196
1109 if force or self.call_pdb:
1197 if force or self.call_pdb:
1110 if self.pdb is None:
1198 if self.pdb is None:
1111 self.pdb = self.debugger_cls()
1199 self.pdb = self.debugger_cls()
1112 # the system displayhook may have changed, restore the original
1200 # the system displayhook may have changed, restore the original
1113 # for pdb
1201 # for pdb
1114 display_trap = DisplayTrap(hook=sys.__displayhook__)
1202 display_trap = DisplayTrap(hook=sys.__displayhook__)
1115 with display_trap:
1203 with display_trap:
1116 self.pdb.reset()
1204 self.pdb.reset()
1117 # Find the right frame so we don't pop up inside ipython itself
1205 # Find the right frame so we don't pop up inside ipython itself
1118 if hasattr(self, 'tb') and self.tb is not None:
1206 if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type]
1119 etb = self.tb
1207 etb = self.tb # type: ignore[has-type]
1120 else:
1208 else:
1121 etb = self.tb = sys.last_traceback
1209 etb = self.tb = sys.last_traceback
1122 while self.tb is not None and self.tb.tb_next is not None:
1210 while self.tb is not None and self.tb.tb_next is not None:
1123 assert self.tb.tb_next is not None
1211 assert self.tb.tb_next is not None
1124 self.tb = self.tb.tb_next
1212 self.tb = self.tb.tb_next
1125 if etb and etb.tb_next:
1213 if etb and etb.tb_next:
1126 etb = etb.tb_next
1214 etb = etb.tb_next
1127 self.pdb.botframe = etb.tb_frame
1215 self.pdb.botframe = etb.tb_frame
1128 self.pdb.interaction(None, etb)
1216 self.pdb.interaction(None, etb)
1129
1217
1130 if hasattr(self, 'tb'):
1218 if hasattr(self, 'tb'):
1131 del self.tb
1219 del self.tb
1132
1220
1133 def handler(self, info=None):
1221 def handler(self, info=None):
1134 (etype, evalue, etb) = info or sys.exc_info()
1222 (etype, evalue, etb) = info or sys.exc_info()
1135 self.tb = etb
1223 self.tb = etb
1136 ostream = self.ostream
1224 ostream = self.ostream
1137 ostream.flush()
1225 ostream.flush()
1138 ostream.write(self.text(etype, evalue, etb))
1226 ostream.write(self.text(etype, evalue, etb))
1139 ostream.write('\n')
1227 ostream.write('\n')
1140 ostream.flush()
1228 ostream.flush()
1141
1229
1142 # Changed so an instance can just be called as VerboseTB_inst() and print
1230 # Changed so an instance can just be called as VerboseTB_inst() and print
1143 # out the right info on its own.
1231 # out the right info on its own.
1144 def __call__(self, etype=None, evalue=None, etb=None):
1232 def __call__(self, etype=None, evalue=None, etb=None):
1145 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1233 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1146 if etb is None:
1234 if etb is None:
1147 self.handler()
1235 self.handler()
1148 else:
1236 else:
1149 self.handler((etype, evalue, etb))
1237 self.handler((etype, evalue, etb))
1150 try:
1238 try:
1151 self.debugger()
1239 self.debugger()
1152 except KeyboardInterrupt:
1240 except KeyboardInterrupt:
1153 print("\nKeyboardInterrupt")
1241 print("\nKeyboardInterrupt")
1154
1242
1155
1243
1156 #----------------------------------------------------------------------------
1244 #----------------------------------------------------------------------------
1157 class FormattedTB(VerboseTB, ListTB):
1245 class FormattedTB(VerboseTB, ListTB):
1158 """Subclass ListTB but allow calling with a traceback.
1246 """Subclass ListTB but allow calling with a traceback.
1159
1247
1160 It can thus be used as a sys.excepthook for Python > 2.1.
1248 It can thus be used as a sys.excepthook for Python > 2.1.
1161
1249
1162 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1250 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1163
1251
1164 Allows a tb_offset to be specified. This is useful for situations where
1252 Allows a tb_offset to be specified. This is useful for situations where
1165 one needs to remove a number of topmost frames from the traceback (such as
1253 one needs to remove a number of topmost frames from the traceback (such as
1166 occurs with python programs that themselves execute other python code,
1254 occurs with python programs that themselves execute other python code,
1167 like Python shells). """
1255 like Python shells). """
1168
1256
1169 mode: str
1257 mode: str
1170
1258
1171 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1259 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1172 ostream=None,
1260 ostream=None,
1173 tb_offset=0, long_header=False, include_vars=False,
1261 tb_offset=0, long_header=False, include_vars=False,
1174 check_cache=None, debugger_cls=None,
1262 check_cache=None, debugger_cls=None,
1175 parent=None, config=None):
1263 parent=None, config=None):
1176
1264
1177 # NEVER change the order of this list. Put new modes at the end:
1265 # NEVER change the order of this list. Put new modes at the end:
1178 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1266 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1179 self.verbose_modes = self.valid_modes[1:3]
1267 self.verbose_modes = self.valid_modes[1:3]
1180
1268
1181 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1269 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1182 ostream=ostream, tb_offset=tb_offset,
1270 ostream=ostream, tb_offset=tb_offset,
1183 long_header=long_header, include_vars=include_vars,
1271 long_header=long_header, include_vars=include_vars,
1184 check_cache=check_cache, debugger_cls=debugger_cls,
1272 check_cache=check_cache, debugger_cls=debugger_cls,
1185 parent=parent, config=config)
1273 parent=parent, config=config)
1186
1274
1187 # Different types of tracebacks are joined with different separators to
1275 # Different types of tracebacks are joined with different separators to
1188 # form a single string. They are taken from this dict
1276 # form a single string. They are taken from this dict
1189 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1277 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1190 Minimal='')
1278 Minimal='')
1191 # set_mode also sets the tb_join_char attribute
1279 # set_mode also sets the tb_join_char attribute
1192 self.set_mode(mode)
1280 self.set_mode(mode)
1193
1281
1194 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1282 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1195 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1283 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1196 mode = self.mode
1284 mode = self.mode
1197 if mode in self.verbose_modes:
1285 if mode in self.verbose_modes:
1198 # Verbose modes need a full traceback
1286 # Verbose modes need a full traceback
1199 return VerboseTB.structured_traceback(
1287 return VerboseTB.structured_traceback(
1200 self, etype, value, tb, tb_offset, number_of_lines_of_context
1288 self, etype, value, tb, tb_offset, number_of_lines_of_context
1201 )
1289 )
1202 elif mode == 'Minimal':
1290 elif mode == 'Minimal':
1203 return ListTB.get_exception_only(self, etype, value)
1291 return ListTB.get_exception_only(self, etype, value)
1204 else:
1292 else:
1205 # We must check the source cache because otherwise we can print
1293 # We must check the source cache because otherwise we can print
1206 # out-of-date source code.
1294 # out-of-date source code.
1207 self.check_cache()
1295 self.check_cache()
1208 # Now we can extract and format the exception
1296 # Now we can extract and format the exception
1209 return ListTB.structured_traceback(
1297 return ListTB.structured_traceback(
1210 self, etype, value, tb, tb_offset, number_of_lines_of_context
1298 self, etype, value, tb, tb_offset, number_of_lines_of_context
1211 )
1299 )
1212
1300
1213 def stb2text(self, stb):
1301 def stb2text(self, stb):
1214 """Convert a structured traceback (a list) to a string."""
1302 """Convert a structured traceback (a list) to a string."""
1215 return self.tb_join_char.join(stb)
1303 return self.tb_join_char.join(stb)
1216
1304
1217 def set_mode(self, mode: Optional[str] = None):
1305 def set_mode(self, mode: Optional[str] = None):
1218 """Switch to the desired mode.
1306 """Switch to the desired mode.
1219
1307
1220 If mode is not specified, cycles through the available modes."""
1308 If mode is not specified, cycles through the available modes."""
1221
1309
1222 if not mode:
1310 if not mode:
1223 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1311 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1224 len(self.valid_modes)
1312 len(self.valid_modes)
1225 self.mode = self.valid_modes[new_idx]
1313 self.mode = self.valid_modes[new_idx]
1226 elif mode not in self.valid_modes:
1314 elif mode not in self.valid_modes:
1227 raise ValueError(
1315 raise ValueError(
1228 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1316 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1229 "Valid modes: " + str(self.valid_modes)
1317 "Valid modes: " + str(self.valid_modes)
1230 )
1318 )
1231 else:
1319 else:
1232 assert isinstance(mode, str)
1320 assert isinstance(mode, str)
1233 self.mode = mode
1321 self.mode = mode
1234 # include variable details only in 'Verbose' mode
1322 # include variable details only in 'Verbose' mode
1235 self.include_vars = (self.mode == self.valid_modes[2])
1323 self.include_vars = (self.mode == self.valid_modes[2])
1236 # Set the join character for generating text tracebacks
1324 # Set the join character for generating text tracebacks
1237 self.tb_join_char = self._join_chars[self.mode]
1325 self.tb_join_char = self._join_chars[self.mode]
1238
1326
1239 # some convenient shortcuts
1327 # some convenient shortcuts
1240 def plain(self):
1328 def plain(self):
1241 self.set_mode(self.valid_modes[0])
1329 self.set_mode(self.valid_modes[0])
1242
1330
1243 def context(self):
1331 def context(self):
1244 self.set_mode(self.valid_modes[1])
1332 self.set_mode(self.valid_modes[1])
1245
1333
1246 def verbose(self):
1334 def verbose(self):
1247 self.set_mode(self.valid_modes[2])
1335 self.set_mode(self.valid_modes[2])
1248
1336
1249 def minimal(self):
1337 def minimal(self):
1250 self.set_mode(self.valid_modes[3])
1338 self.set_mode(self.valid_modes[3])
1251
1339
1252
1340
1253 #----------------------------------------------------------------------------
1341 #----------------------------------------------------------------------------
1254 class AutoFormattedTB(FormattedTB):
1342 class AutoFormattedTB(FormattedTB):
1255 """A traceback printer which can be called on the fly.
1343 """A traceback printer which can be called on the fly.
1256
1344
1257 It will find out about exceptions by itself.
1345 It will find out about exceptions by itself.
1258
1346
1259 A brief example::
1347 A brief example::
1260
1348
1261 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1349 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1262 try:
1350 try:
1263 ...
1351 ...
1264 except:
1352 except:
1265 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1353 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1266 """
1354 """
1267
1355
1268 def __call__(self, etype=None, evalue=None, etb=None,
1356 def __call__(self, etype=None, evalue=None, etb=None,
1269 out=None, tb_offset=None):
1357 out=None, tb_offset=None):
1270 """Print out a formatted exception traceback.
1358 """Print out a formatted exception traceback.
1271
1359
1272 Optional arguments:
1360 Optional arguments:
1273 - out: an open file-like object to direct output to.
1361 - out: an open file-like object to direct output to.
1274
1362
1275 - tb_offset: the number of frames to skip over in the stack, on a
1363 - tb_offset: the number of frames to skip over in the stack, on a
1276 per-call basis (this overrides temporarily the instance's tb_offset
1364 per-call basis (this overrides temporarily the instance's tb_offset
1277 given at initialization time."""
1365 given at initialization time."""
1278
1366
1279 if out is None:
1367 if out is None:
1280 out = self.ostream
1368 out = self.ostream
1281 out.flush()
1369 out.flush()
1282 out.write(self.text(etype, evalue, etb, tb_offset))
1370 out.write(self.text(etype, evalue, etb, tb_offset))
1283 out.write('\n')
1371 out.write('\n')
1284 out.flush()
1372 out.flush()
1285 # FIXME: we should remove the auto pdb behavior from here and leave
1373 # FIXME: we should remove the auto pdb behavior from here and leave
1286 # that to the clients.
1374 # that to the clients.
1287 try:
1375 try:
1288 self.debugger()
1376 self.debugger()
1289 except KeyboardInterrupt:
1377 except KeyboardInterrupt:
1290 print("\nKeyboardInterrupt")
1378 print("\nKeyboardInterrupt")
1291
1379
1292 def structured_traceback(
1380 def structured_traceback(
1293 self,
1381 self,
1294 etype=None,
1382 etype: type,
1295 value=None,
1383 evalue: Optional[BaseException],
1296 tb=None,
1384 etb: Optional[TracebackType] = None,
1297 tb_offset=None,
1385 tb_offset: Optional[int] = None,
1298 number_of_lines_of_context=5,
1386 number_of_lines_of_context: int = 5,
1299 ):
1387 ):
1300 etype: type
1301 value: BaseException
1302 # tb: TracebackType or tupleof tb types ?
1388 # tb: TracebackType or tupleof tb types ?
1303 if etype is None:
1389 if etype is None:
1304 etype, value, tb = sys.exc_info()
1390 etype, evalue, etb = sys.exc_info()
1305 if isinstance(tb, tuple):
1391 if isinstance(etb, tuple):
1306 # tb is a tuple if this is a chained exception.
1392 # tb is a tuple if this is a chained exception.
1307 self.tb = tb[0]
1393 self.tb = etb[0]
1308 else:
1394 else:
1309 self.tb = tb
1395 self.tb = etb
1310 return FormattedTB.structured_traceback(
1396 return FormattedTB.structured_traceback(
1311 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1397 self, etype, evalue, etb, tb_offset, number_of_lines_of_context
1398 )
1312
1399
1313
1400
1314 #---------------------------------------------------------------------------
1401 #---------------------------------------------------------------------------
1315
1402
1316 # A simple class to preserve Nathan's original functionality.
1403 # A simple class to preserve Nathan's original functionality.
1317 class ColorTB(FormattedTB):
1404 class ColorTB(FormattedTB):
1318 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1405 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1319
1406
1320 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1407 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1321 FormattedTB.__init__(self, color_scheme=color_scheme,
1408 FormattedTB.__init__(self, color_scheme=color_scheme,
1322 call_pdb=call_pdb, **kwargs)
1409 call_pdb=call_pdb, **kwargs)
1323
1410
1324
1411
1325 class SyntaxTB(ListTB):
1412 class SyntaxTB(ListTB):
1326 """Extension which holds some state: the last exception value"""
1413 """Extension which holds some state: the last exception value"""
1327
1414
1328 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1415 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1329 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1416 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1330 self.last_syntax_error = None
1417 self.last_syntax_error = None
1331
1418
1332 def __call__(self, etype, value, elist):
1419 def __call__(self, etype, value, elist):
1333 self.last_syntax_error = value
1420 self.last_syntax_error = value
1334
1421
1335 ListTB.__call__(self, etype, value, elist)
1422 ListTB.__call__(self, etype, value, elist)
1336
1423
1337 def structured_traceback(self, etype, value, elist, tb_offset=None,
1424 def structured_traceback(self, etype, value, elist, tb_offset=None,
1338 context=5):
1425 context=5):
1339 # If the source file has been edited, the line in the syntax error can
1426 # If the source file has been edited, the line in the syntax error can
1340 # be wrong (retrieved from an outdated cache). This replaces it with
1427 # be wrong (retrieved from an outdated cache). This replaces it with
1341 # the current value.
1428 # the current value.
1342 if isinstance(value, SyntaxError) \
1429 if isinstance(value, SyntaxError) \
1343 and isinstance(value.filename, str) \
1430 and isinstance(value.filename, str) \
1344 and isinstance(value.lineno, int):
1431 and isinstance(value.lineno, int):
1345 linecache.checkcache(value.filename)
1432 linecache.checkcache(value.filename)
1346 newtext = linecache.getline(value.filename, value.lineno)
1433 newtext = linecache.getline(value.filename, value.lineno)
1347 if newtext:
1434 if newtext:
1348 value.text = newtext
1435 value.text = newtext
1349 self.last_syntax_error = value
1436 self.last_syntax_error = value
1350 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1437 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1351 tb_offset=tb_offset, context=context)
1438 tb_offset=tb_offset, context=context)
1352
1439
1353 def clear_err_state(self):
1440 def clear_err_state(self):
1354 """Return the current error state and clear it"""
1441 """Return the current error state and clear it"""
1355 e = self.last_syntax_error
1442 e = self.last_syntax_error
1356 self.last_syntax_error = None
1443 self.last_syntax_error = None
1357 return e
1444 return e
1358
1445
1359 def stb2text(self, stb):
1446 def stb2text(self, stb):
1360 """Convert a structured traceback (a list) to a string."""
1447 """Convert a structured traceback (a list) to a string."""
1361 return ''.join(stb)
1448 return ''.join(stb)
1362
1449
1363
1450
1364 # some internal-use functions
1451 # some internal-use functions
1365 def text_repr(value):
1452 def text_repr(value):
1366 """Hopefully pretty robust repr equivalent."""
1453 """Hopefully pretty robust repr equivalent."""
1367 # this is pretty horrible but should always return *something*
1454 # this is pretty horrible but should always return *something*
1368 try:
1455 try:
1369 return pydoc.text.repr(value)
1456 return pydoc.text.repr(value) # type: ignore[call-arg]
1370 except KeyboardInterrupt:
1457 except KeyboardInterrupt:
1371 raise
1458 raise
1372 except:
1459 except:
1373 try:
1460 try:
1374 return repr(value)
1461 return repr(value)
1375 except KeyboardInterrupt:
1462 except KeyboardInterrupt:
1376 raise
1463 raise
1377 except:
1464 except:
1378 try:
1465 try:
1379 # all still in an except block so we catch
1466 # all still in an except block so we catch
1380 # getattr raising
1467 # getattr raising
1381 name = getattr(value, '__name__', None)
1468 name = getattr(value, '__name__', None)
1382 if name:
1469 if name:
1383 # ick, recursion
1470 # ick, recursion
1384 return text_repr(name)
1471 return text_repr(name)
1385 klass = getattr(value, '__class__', None)
1472 klass = getattr(value, '__class__', None)
1386 if klass:
1473 if klass:
1387 return '%s instance' % text_repr(klass)
1474 return '%s instance' % text_repr(klass)
1388 except KeyboardInterrupt:
1475 except KeyboardInterrupt:
1389 raise
1476 raise
1390 except:
1477 except:
1391 return 'UNRECOVERABLE REPR FAILURE'
1478 return 'UNRECOVERABLE REPR FAILURE'
1392
1479
1393
1480
1394 def eqrepr(value, repr=text_repr):
1481 def eqrepr(value, repr=text_repr):
1395 return '=%s' % repr(value)
1482 return '=%s' % repr(value)
1396
1483
1397
1484
1398 def nullrepr(value, repr=text_repr):
1485 def nullrepr(value, repr=text_repr):
1399 return ''
1486 return ''
@@ -1,32 +1,32 b''
1 [build-system]
1 [build-system]
2 requires = ["setuptools >= 51.0.0"]
2 requires = ["setuptools >= 51.0.0"]
3 build-backend = "setuptools.build_meta"
3 build-backend = "setuptools.build_meta"
4 [tool.mypy]
4 [tool.mypy]
5 python_version = 3.8
5 python_version = 3.8
6 ignore_missing_imports = true
6 ignore_missing_imports = true
7 follow_imports = 'silent'
7 follow_imports = 'silent'
8 exclude = [
8 exclude = [
9 'test_\.+\.py',
9 'test_\.+\.py',
10 'IPython.utils.tests.test_wildcard',
10 'IPython.utils.tests.test_wildcard',
11 'testing',
11 'testing',
12 'tests',
12 'tests',
13 'PyColorize.py',
13 'PyColorize.py',
14 '_process_win32_controller.py',
14 '_process_win32_controller.py',
15 'IPython/core/application.py',
15 'IPython/core/application.py',
16 'IPython/core/completerlib.py',
16 'IPython/core/completerlib.py',
17 'IPython/core/displaypub.py',
17 'IPython/core/displaypub.py',
18 'IPython/core/historyapp.py',
18 'IPython/core/historyapp.py',
19 #'IPython/core/interactiveshell.py',
19 #'IPython/core/interactiveshell.py',
20 'IPython/core/magic.py',
20 'IPython/core/magic.py',
21 'IPython/core/profileapp.py',
21 'IPython/core/profileapp.py',
22 'IPython/core/ultratb.py',
22 # 'IPython/core/ultratb.py',
23 'IPython/lib/deepreload.py',
23 'IPython/lib/deepreload.py',
24 'IPython/lib/pretty.py',
24 'IPython/lib/pretty.py',
25 'IPython/sphinxext/ipython_directive.py',
25 'IPython/sphinxext/ipython_directive.py',
26 'IPython/terminal/ipapp.py',
26 'IPython/terminal/ipapp.py',
27 'IPython/utils/_process_win32.py',
27 'IPython/utils/_process_win32.py',
28 'IPython/utils/path.py',
28 'IPython/utils/path.py',
29 'IPython/utils/timing.py',
29 'IPython/utils/timing.py',
30 'IPython/utils/text.py'
30 'IPython/utils/text.py'
31 ]
31 ]
32
32
General Comments 0
You need to be logged in to leave comments. Login now