##// END OF EJS Templates
Merge pull request #11886 from alexmojaki/master...
Matthias Bussonnier -
r25477:7db73df5 merge
parent child Browse files
Show More
@@ -65,8 +65,8 b' In [4]: run simpleerr.py'
65 ZeroDivisionError Traceback (most recent call last)
65 ZeroDivisionError Traceback (most recent call last)
66 <BLANKLINE>
66 <BLANKLINE>
67 ... in <module>
67 ... in <module>
68 29 except IndexError:
68 30 mode = 'div'
69 30 mode = 'div'
69 31
70 ---> 32 bar(mode)
70 ---> 32 bar(mode)
71 <BLANKLINE>
71 <BLANKLINE>
72 ... in bar(mode)
72 ... in bar(mode)
@@ -80,8 +80,6 b' ZeroDivisionError Traceback (most recent call last)'
80 6 x = 1
80 6 x = 1
81 7 y = 0
81 7 y = 0
82 ----> 8 x/y
82 ----> 8 x/y
83 9
84 10 def sysexit(stat, mode):
85 <BLANKLINE>
83 <BLANKLINE>
86 ZeroDivisionError: ...
84 ZeroDivisionError: ...
87 """
85 """
@@ -97,17 +95,15 b' In [6]: run simpleerr.py'
97 ZeroDivisionError Traceback (most recent call last)
95 ZeroDivisionError Traceback (most recent call last)
98 <BLANKLINE>
96 <BLANKLINE>
99 ... in <module>
97 ... in <module>
98 29 except IndexError:
100 30 mode = 'div'
99 30 mode = 'div'
101 31
102 ---> 32 bar(mode)
100 ---> 32 bar(mode)
103 global bar = <function bar at ...>
101 mode = 'div'
104 global mode = 'div'
105 <BLANKLINE>
102 <BLANKLINE>
106 ... in bar(mode='div')
103 ... in bar(mode='div')
107 14 "bar"
104 14 "bar"
108 15 if mode=='div':
105 15 if mode=='div':
109 ---> 16 div0()
106 ---> 16 div0()
110 global div0 = <function div0 at ...>
111 17 elif mode=='exit':
107 17 elif mode=='exit':
112 18 try:
108 18 try:
113 <BLANKLINE>
109 <BLANKLINE>
@@ -117,8 +113,6 b' ZeroDivisionError Traceback (most recent call last)'
117 ----> 8 x/y
113 ----> 8 x/y
118 x = 1
114 x = 1
119 y = 0
115 y = 0
120 9
121 10 def sysexit(stat, mode):
122 <BLANKLINE>
116 <BLANKLINE>
123 ZeroDivisionError: ...
117 ZeroDivisionError: ...
124 """
118 """
@@ -154,8 +148,8 b' In [22]: %tb'
154 SystemExit Traceback (most recent call last)
148 SystemExit Traceback (most recent call last)
155 <BLANKLINE>
149 <BLANKLINE>
156 ...<module>
150 ...<module>
151 29 except IndexError:
157 30 mode = 'div'
152 30 mode = 'div'
158 31
159 ---> 32 bar(mode)
153 ---> 32 bar(mode)
160 <BLANKLINE>
154 <BLANKLINE>
161 ...bar(mode)
155 ...bar(mode)
@@ -166,11 +160,8 b' SystemExit Traceback (most recent call last)'
166 24 raise ValueError('Unknown mode')
160 24 raise ValueError('Unknown mode')
167 <BLANKLINE>
161 <BLANKLINE>
168 ...sysexit(stat, mode)
162 ...sysexit(stat, mode)
169 9
170 10 def sysexit(stat, mode):
163 10 def sysexit(stat, mode):
171 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
164 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
172 12
173 13 def bar(mode):
174 <BLANKLINE>
165 <BLANKLINE>
175 SystemExit: (2, 'Mode = exit')
166 SystemExit: (2, 'Mode = exit')
176
167
@@ -182,31 +173,25 b' In [24]: %tb'
182 SystemExit Traceback (most recent call last)
173 SystemExit Traceback (most recent call last)
183 <BLANKLINE>
174 <BLANKLINE>
184 ... in <module>
175 ... in <module>
176 29 except IndexError:
185 30 mode = 'div'
177 30 mode = 'div'
186 31
187 ---> 32 bar(mode)
178 ---> 32 bar(mode)
188 global bar = <function bar at ...>
179 mode = 'exit'
189 global mode = 'exit'
190 <BLANKLINE>
180 <BLANKLINE>
191 ... in bar(mode='exit')
181 ... in bar(mode='exit')
192 20 except:
182 20 except:
193 21 stat = 1
183 21 stat = 1
194 ---> 22 sysexit(stat, mode)
184 ---> 22 sysexit(stat, mode)
195 global sysexit = <function sysexit at ...>
196 stat = 2
197 mode = 'exit'
185 mode = 'exit'
186 stat = 2
198 23 else:
187 23 else:
199 24 raise ValueError('Unknown mode')
188 24 raise ValueError('Unknown mode')
200 <BLANKLINE>
189 <BLANKLINE>
201 ... in sysexit(stat=2, mode='exit')
190 ... in sysexit(stat=2, mode='exit')
202 9
203 10 def sysexit(stat, mode):
191 10 def sysexit(stat, mode):
204 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
192 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
205 global SystemExit = undefined
206 stat = 2
193 stat = 2
207 mode = 'exit'
194 mode = 'exit'
208 12
209 13 def bar(mode):
210 <BLANKLINE>
195 <BLANKLINE>
211 SystemExit: (2, 'Mode = exit')
196 SystemExit: (2, 'Mode = exit')
212 """
197 """
@@ -3,15 +3,14 b''
3 """
3 """
4 import io
4 import io
5 import logging
5 import logging
6 import re
6 import sys
7 import sys
7 import os.path
8 import os.path
8 from textwrap import dedent
9 from textwrap import dedent
9 import traceback
10 import traceback
10 import unittest
11 import unittest
11 from unittest import mock
12
12
13 import IPython.core.ultratb as ultratb
13 from IPython.core.ultratb import ColorTB, VerboseTB
14 from IPython.core.ultratb import ColorTB, VerboseTB, find_recursion
15
14
16
15
17 from IPython.testing import tools as tt
16 from IPython.testing import tools as tt
@@ -38,16 +37,12 b' def recursionlimit(frames):'
38
37
39 def inner(test_function):
38 def inner(test_function):
40 def wrapper(*args, **kwargs):
39 def wrapper(*args, **kwargs):
41 _orig_rec_limit = ultratb._FRAME_RECURSION_LIMIT
42 ultratb._FRAME_RECURSION_LIMIT = 50
43
44 rl = sys.getrecursionlimit()
40 rl = sys.getrecursionlimit()
45 sys.setrecursionlimit(frames)
41 sys.setrecursionlimit(frames)
46 try:
42 try:
47 return test_function(*args, **kwargs)
43 return test_function(*args, **kwargs)
48 finally:
44 finally:
49 sys.setrecursionlimit(rl)
45 sys.setrecursionlimit(rl)
50 ultratb._FRAME_RECURSION_LIMIT = _orig_rec_limit
51
46
52 return wrapper
47 return wrapper
53
48
@@ -350,45 +345,24 b' def r3o2():'
350 ip.run_cell(self.DEFINITIONS)
345 ip.run_cell(self.DEFINITIONS)
351
346
352 def test_no_recursion(self):
347 def test_no_recursion(self):
353 with tt.AssertNotPrints("frames repeated"):
348 with tt.AssertNotPrints("skipping similar frames"):
354 ip.run_cell("non_recurs()")
349 ip.run_cell("non_recurs()")
355
350
356 @recursionlimit(150)
351 @recursionlimit(150)
357 def test_recursion_one_frame(self):
352 def test_recursion_one_frame(self):
358 with tt.AssertPrints("1 frames repeated"):
353 with tt.AssertPrints(re.compile(
354 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2} times\)\]")
355 ):
359 ip.run_cell("r1()")
356 ip.run_cell("r1()")
360
357
361 @recursionlimit(150)
358 @recursionlimit(150)
362 def test_recursion_three_frames(self):
359 def test_recursion_three_frames(self):
363 with tt.AssertPrints("3 frames repeated"):
360 with tt.AssertPrints("[... skipping similar frames: "), \
364 ip.run_cell("r3o2()")
361 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
365
362 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
366 @recursionlimit(150)
363 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
367 def test_find_recursion(self):
368 captured = []
369 def capture_exc(*args, **kwargs):
370 captured.append(sys.exc_info())
371 with mock.patch.object(ip, 'showtraceback', capture_exc):
372 ip.run_cell("r3o2()")
364 ip.run_cell("r3o2()")
373
365
374 self.assertEqual(len(captured), 1)
375 etype, evalue, tb = captured[0]
376 self.assertIn("recursion", str(evalue))
377
378 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
379 for r in records[:10]:
380 print(r[1:4])
381
382 # The outermost frames should be:
383 # 0: the 'cell' that was running when the exception came up
384 # 1: r3o2()
385 # 2: r3o1()
386 # 3: r3a()
387 # Then repeating r3b, r3c, r3a
388 last_unique, repeat_length = find_recursion(etype, evalue, records)
389 self.assertEqual(last_unique, 2)
390 self.assertEqual(repeat_length, 3)
391
392
366
393 #----------------------------------------------------------------------------
367 #----------------------------------------------------------------------------
394
368
@@ -432,35 +406,3 b' def test_handlers():'
432 except:
406 except:
433 handler(*sys.exc_info())
407 handler(*sys.exc_info())
434 buff.write('')
408 buff.write('')
435
436 from IPython.testing.decorators import skipif
437
438 class TokenizeFailureTest(unittest.TestCase):
439 """Tests related to https://github.com/ipython/ipython/issues/6864."""
440
441 # that appear to test that we are handling an exception that can be thrown
442 # by the tokenizer due to a bug that seem to have been fixed in 3.8, though
443 # I'm unsure if other sequences can make it raise this error. Let's just
444 # skip in 3.8 for now
445 @skipif(sys.version_info > (3,8))
446 def testLogging(self):
447 message = "An unexpected error occurred while tokenizing input"
448 cell = 'raise ValueError("""a\nb""")'
449
450 stream = io.StringIO()
451 handler = logging.StreamHandler(stream)
452 logger = logging.getLogger()
453 loglevel = logger.level
454 logger.addHandler(handler)
455 self.addCleanup(lambda: logger.removeHandler(handler))
456 self.addCleanup(lambda: logger.setLevel(loglevel))
457
458 logger.setLevel(logging.INFO)
459 with tt.AssertNotPrints(message):
460 ip.run_cell(cell)
461 self.assertNotIn(message, stream.getvalue())
462
463 logger.setLevel(logging.DEBUG)
464 with tt.AssertNotPrints(message):
465 ip.run_cell(cell)
466 self.assertIn(message, stream.getvalue())
This diff has been collapsed as it changes many lines, (521 lines changed) Show them Hide them
@@ -89,23 +89,14 b' Inheritance diagram:'
89 #*****************************************************************************
89 #*****************************************************************************
90
90
91
91
92 import dis
93 import inspect
92 import inspect
94 import keyword
95 import linecache
93 import linecache
96 import os
97 import pydoc
94 import pydoc
98 import re
99 import sys
95 import sys
100 import time
96 import time
101 import tokenize
102 import traceback
97 import traceback
103
98
104 from tokenize import generate_tokens
99 import stack_data
105
106 # For purposes of monkeypatching inspect to fix a bug in it.
107 from inspect import getsourcefile, getfile, getmodule, \
108 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
109
100
110 # IPython's own modules
101 # IPython's own modules
111 from IPython import get_ipython
102 from IPython import get_ipython
@@ -115,13 +106,8 b' from IPython.core.excolors import exception_colors'
115 from IPython.utils import PyColorize
106 from IPython.utils import PyColorize
116 from IPython.utils import path as util_path
107 from IPython.utils import path as util_path
117 from IPython.utils import py3compat
108 from IPython.utils import py3compat
118 from IPython.utils.data import uniq_stable
119 from IPython.utils.terminal import get_terminal_size
109 from IPython.utils.terminal import get_terminal_size
120
110
121 from logging import info, error, debug
122
123 from importlib.util import source_from_cache
124
125 import IPython.utils.colorable as colorable
111 import IPython.utils.colorable as colorable
126
112
127 # Globals
113 # Globals
@@ -134,264 +120,26 b' INDENT_SIZE = 8'
134 # to users of ultratb who are NOT running inside ipython.
120 # to users of ultratb who are NOT running inside ipython.
135 DEFAULT_SCHEME = 'NoColor'
121 DEFAULT_SCHEME = 'NoColor'
136
122
137
138 # Number of frame above which we are likely to have a recursion and will
139 # **attempt** to detect it. Made modifiable mostly to speedup test suite
140 # as detecting recursion is one of our slowest test
141 _FRAME_RECURSION_LIMIT = 500
142
143 # ---------------------------------------------------------------------------
123 # ---------------------------------------------------------------------------
144 # Code begins
124 # Code begins
145
125
146 # Utility functions
147 def inspect_error():
148 """Print a message about internal inspect errors.
149
150 These are unfortunately quite common."""
151
152 error('Internal Python error in the inspect module.\n'
153 'Below is the traceback from this internal error.\n')
154
155
156 # This function is a monkeypatch we apply to the Python inspect module. We have
157 # now found when it's needed (see discussion on issue gh-1456), and we have a
158 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
159 # the monkeypatch is not applied. TK, Aug 2012.
160 def findsource(object):
161 """Return the entire source file and starting line number for an object.
162
163 The argument may be a module, class, method, function, traceback, frame,
164 or code object. The source code is returned as a list of all the lines
165 in the file and the line number indexes a line in that list. An IOError
166 is raised if the source code cannot be retrieved.
167
168 FIXED version with which we monkeypatch the stdlib to work around a bug."""
169
170 file = getsourcefile(object) or getfile(object)
171 # If the object is a frame, then trying to get the globals dict from its
172 # module won't work. Instead, the frame object itself has the globals
173 # dictionary.
174 globals_dict = None
175 if inspect.isframe(object):
176 # XXX: can this ever be false?
177 globals_dict = object.f_globals
178 else:
179 module = getmodule(object, file)
180 if module:
181 globals_dict = module.__dict__
182 lines = linecache.getlines(file, globals_dict)
183 if not lines:
184 raise IOError('could not get source code')
185
186 if ismodule(object):
187 return lines, 0
188
189 if isclass(object):
190 name = object.__name__
191 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
192 # make some effort to find the best matching class definition:
193 # use the one with the least indentation, which is the one
194 # that's most probably not inside a function definition.
195 candidates = []
196 for i, line in enumerate(lines):
197 match = pat.match(line)
198 if match:
199 # if it's at toplevel, it's already the best one
200 if line[0] == 'c':
201 return lines, i
202 # else add whitespace to candidate list
203 candidates.append((match.group(1), i))
204 if candidates:
205 # this will sort by whitespace, and by line number,
206 # less whitespace first
207 candidates.sort()
208 return lines, candidates[0][1]
209 else:
210 raise IOError('could not find class definition')
211
212 if ismethod(object):
213 object = object.__func__
214 if isfunction(object):
215 object = object.__code__
216 if istraceback(object):
217 object = object.tb_frame
218 if isframe(object):
219 object = object.f_code
220 if iscode(object):
221 if not hasattr(object, 'co_firstlineno'):
222 raise IOError('could not find function definition')
223 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
224 pmatch = pat.match
225 # fperez - fix: sometimes, co_firstlineno can give a number larger than
226 # the length of lines, which causes an error. Safeguard against that.
227 lnum = min(object.co_firstlineno, len(lines)) - 1
228 while lnum > 0:
229 if pmatch(lines[lnum]):
230 break
231 lnum -= 1
232
233 return lines, lnum
234 raise IOError('could not find code object')
235
236
237 # This is a patched version of inspect.getargs that applies the (unmerged)
238 # patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes
239 # https://github.com/ipython/ipython/issues/8205 and
240 # https://github.com/ipython/ipython/issues/8293
241 def getargs(co):
242 """Get information about the arguments accepted by a code object.
243
244 Three things are returned: (args, varargs, varkw), where 'args' is
245 a list of argument names (possibly containing nested lists), and
246 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
247 if not iscode(co):
248 raise TypeError('{!r} is not a code object'.format(co))
249
250 nargs = co.co_argcount
251 names = co.co_varnames
252 args = list(names[:nargs])
253 step = 0
254
255 # The following acrobatics are for anonymous (tuple) arguments.
256 for i in range(nargs):
257 if args[i][:1] in ('', '.'):
258 stack, remain, count = [], [], []
259 while step < len(co.co_code):
260 op = ord(co.co_code[step])
261 step = step + 1
262 if op >= dis.HAVE_ARGUMENT:
263 opname = dis.opname[op]
264 value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256
265 step = step + 2
266 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
267 remain.append(value)
268 count.append(value)
269 elif opname in ('STORE_FAST', 'STORE_DEREF'):
270 if op in dis.haslocal:
271 stack.append(co.co_varnames[value])
272 elif op in dis.hasfree:
273 stack.append((co.co_cellvars + co.co_freevars)[value])
274 # Special case for sublists of length 1: def foo((bar))
275 # doesn't generate the UNPACK_TUPLE bytecode, so if
276 # `remain` is empty here, we have such a sublist.
277 if not remain:
278 stack[0] = [stack[0]]
279 break
280 else:
281 remain[-1] = remain[-1] - 1
282 while remain[-1] == 0:
283 remain.pop()
284 size = count.pop()
285 stack[-size:] = [stack[-size:]]
286 if not remain:
287 break
288 remain[-1] = remain[-1] - 1
289 if not remain:
290 break
291 args[i] = stack[0]
292
293 varargs = None
294 if co.co_flags & inspect.CO_VARARGS:
295 varargs = co.co_varnames[nargs]
296 nargs = nargs + 1
297 varkw = None
298 if co.co_flags & inspect.CO_VARKEYWORDS:
299 varkw = co.co_varnames[nargs]
300 return inspect.Arguments(args, varargs, varkw)
301
302
303 # Monkeypatch inspect to apply our bugfix.
304 def with_patch_inspect(f):
305 """
306 Deprecated since IPython 6.0
307 decorator for monkeypatching inspect.findsource
308 """
309
310 def wrapped(*args, **kwargs):
311 save_findsource = inspect.findsource
312 save_getargs = inspect.getargs
313 inspect.findsource = findsource
314 inspect.getargs = getargs
315 try:
316 return f(*args, **kwargs)
317 finally:
318 inspect.findsource = save_findsource
319 inspect.getargs = save_getargs
320
321 return wrapped
322
323
324 def fix_frame_records_filenames(records):
325 """Try to fix the filenames in each record from inspect.getinnerframes().
326
327 Particularly, modules loaded from within zip files have useless filenames
328 attached to their code object, and inspect.getinnerframes() just uses it.
329 """
330 fixed_records = []
331 for frame, filename, line_no, func_name, lines, index in records:
332 # Look inside the frame's globals dictionary for __file__,
333 # which should be better. However, keep Cython filenames since
334 # we prefer the source filenames over the compiled .so file.
335 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
336 better_fn = frame.f_globals.get('__file__', None)
337 if isinstance(better_fn, str):
338 # Check the type just in case someone did something weird with
339 # __file__. It might also be None if the error occurred during
340 # import.
341 filename = better_fn
342 fixed_records.append((frame, filename, line_no, func_name, lines, index))
343 return fixed_records
344
345
346 @with_patch_inspect
347 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
348 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
349
350 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
351 # If the error is at the console, don't build any context, since it would
352 # otherwise produce 5 blank lines printed out (there is no file at the
353 # console)
354 rec_check = records[tb_offset:]
355 try:
356 rname = rec_check[0][1]
357 if rname == '<ipython console>' or rname.endswith('<string>'):
358 return rec_check
359 except IndexError:
360 pass
361
362 aux = traceback.extract_tb(etb)
363 assert len(records) == len(aux)
364 for i, (file, lnum, _, _) in enumerate(aux):
365 maybeStart = lnum - 1 - context // 2
366 start = max(maybeStart, 0)
367 end = start + context
368 lines = linecache.getlines(file)[start:end]
369 buf = list(records[i])
370 buf[LNUM_POS] = lnum
371 buf[INDEX_POS] = lnum - 1 - start
372 buf[LINES_POS] = lines
373 records[i] = tuple(buf)
374 return records[tb_offset:]
375
376 # Helper function -- largely belongs to VerboseTB, but we need the same
126 # Helper function -- largely belongs to VerboseTB, but we need the same
377 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
127 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
378 # can be recognized properly by ipython.el's py-traceback-line-re
128 # can be recognized properly by ipython.el's py-traceback-line-re
379 # (SyntaxErrors have to be treated specially because they have no traceback)
129 # (SyntaxErrors have to be treated specially because they have no traceback)
380
130
381
131
382 def _format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
132 def _format_traceback_lines(lines, Colors, lvals, _line_format):
383 """
133 """
384 Format tracebacks lines with pointing arrow, leading numbers...
134 Format tracebacks lines with pointing arrow, leading numbers...
385
135
386 Parameters
136 Parameters
387 ==========
137 ==========
388
138
389 lnum: int
139 lines: list[Line]
390 index: int
391 lines: list[string]
392 Colors:
140 Colors:
393 ColorScheme used.
141 ColorScheme used.
394 lvals: bytes
142 lvals: str
395 Values of local variables, already colored, to inject just after the error line.
143 Values of local variables, already colored, to inject just after the error line.
396 _line_format: f (str) -> (str, bool)
144 _line_format: f (str) -> (str, bool)
397 return (colorized version of str, failure to do so)
145 return (colorized version of str, failure to do so)
@@ -399,80 +147,34 b' def _format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):'
399 numbers_width = INDENT_SIZE - 1
147 numbers_width = INDENT_SIZE - 1
400 res = []
148 res = []
401
149
402 for i,line in enumerate(lines, lnum-index):
150 for stack_line in lines:
403 line = py3compat.cast_unicode(line)
151 if stack_line is stack_data.LINE_GAP:
152 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
153 continue
154
155 line = stack_line.text.rstrip('\n') + '\n'
404
156
405 new_line, err = _line_format(line, 'str')
157 new_line, err = _line_format(line, 'str')
406 if not err:
158 if not err:
407 line = new_line
159 line = new_line
408
160
409 if i == lnum:
161 lineno = stack_line.lineno
162 if stack_line.is_current:
410 # This is the line with the error
163 # This is the line with the error
411 pad = numbers_width - len(str(i))
164 pad = numbers_width - len(str(lineno))
412 num = '%s%s' % (debugger.make_arrow(pad), str(lnum))
165 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
413 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
166 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
414 Colors.line, line, Colors.Normal)
167 Colors.line, line, Colors.Normal)
415 else:
168 else:
416 num = '%*s' % (numbers_width, i)
169 num = '%*s' % (numbers_width, lineno)
417 line = '%s%s%s %s' % (Colors.lineno, num,
170 line = '%s%s%s %s' % (Colors.lineno, num,
418 Colors.Normal, line)
171 Colors.Normal, line)
419
172
420 res.append(line)
173 res.append(line)
421 if lvals and i == lnum:
174 if lvals and stack_line.is_current:
422 res.append(lvals + '\n')
175 res.append(lvals + '\n')
423 return res
176 return res
424
177
425 def is_recursion_error(etype, value, records):
426 try:
427 # RecursionError is new in Python 3.5
428 recursion_error_type = RecursionError
429 except NameError:
430 recursion_error_type = RuntimeError
431
432 # The default recursion limit is 1000, but some of that will be taken up
433 # by stack frames in IPython itself. >500 frames probably indicates
434 # a recursion error.
435 return (etype is recursion_error_type) \
436 and "recursion" in str(value).lower() \
437 and len(records) > _FRAME_RECURSION_LIMIT
438
439 def find_recursion(etype, value, records):
440 """Identify the repeating stack frames from a RecursionError traceback
441
442 'records' is a list as returned by VerboseTB.get_records()
443
444 Returns (last_unique, repeat_length)
445 """
446 # This involves a bit of guesswork - we want to show enough of the traceback
447 # to indicate where the recursion is occurring. We guess that the innermost
448 # quarter of the traceback (250 frames by default) is repeats, and find the
449 # first frame (from in to out) that looks different.
450 if not is_recursion_error(etype, value, records):
451 return len(records), 0
452
453 # Select filename, lineno, func_name to track frames with
454 records = [r[1:4] for r in records]
455 inner_frames = records[-(len(records)//4):]
456 frames_repeated = set(inner_frames)
457
458 last_seen_at = {}
459 longest_repeat = 0
460 i = len(records)
461 for frame in reversed(records):
462 i -= 1
463 if frame not in frames_repeated:
464 last_unique = i
465 break
466
467 if frame in last_seen_at:
468 distance = last_seen_at[frame] - i
469 longest_repeat = max(longest_repeat, distance)
470
471 last_seen_at[frame] = i
472 else:
473 last_unique = 0 # The whole traceback was recursion
474
475 return last_unique, longest_repeat
476
178
477 #---------------------------------------------------------------------------
179 #---------------------------------------------------------------------------
478 # Module classes
180 # Module classes
@@ -880,63 +582,33 b' class VerboseTB(TBTools):'
880
582
881 self.debugger_cls = debugger_cls or debugger.Pdb
583 self.debugger_cls = debugger_cls or debugger.Pdb
882
584
883 def format_records(self, records, last_unique, recursion_repeat):
585 def format_record(self, frame_info):
884 """Format the stack frames of the traceback"""
885 frames = []
886 for r in records[:last_unique+recursion_repeat+1]:
887 #print '*** record:',file,lnum,func,lines,index # dbg
888 frames.append(self.format_record(*r))
889
890 if recursion_repeat:
891 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
892 frames.append(self.format_record(*records[last_unique+recursion_repeat+1]))
893
894 return frames
895
896 def format_record(self, frame, file, lnum, func, lines, index):
897 """Format a single stack frame"""
586 """Format a single stack frame"""
898 Colors = self.Colors # just a shorthand + quicker name lookup
587 Colors = self.Colors # just a shorthand + quicker name lookup
899 ColorsNormal = Colors.Normal # used a lot
588 ColorsNormal = Colors.Normal # used a lot
589
590 if isinstance(frame_info, stack_data.RepeatedFrames):
591 return ' %s[... skipping similar frames: %s]%s\n' % (
592 Colors.excName, frame_info.description, ColorsNormal)
593
900 col_scheme = self.color_scheme_table.active_scheme_name
594 col_scheme = self.color_scheme_table.active_scheme_name
901 indent = ' ' * INDENT_SIZE
595 indent = ' ' * INDENT_SIZE
902 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
596 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
903 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
904 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
597 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
905 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
598 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
906 ColorsNormal)
599 ColorsNormal)
907 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
600 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
908 (Colors.vName, Colors.valEm, ColorsNormal)
601 (Colors.vName, Colors.valEm, ColorsNormal)
909 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
602 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
910 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
911 Colors.vName, ColorsNormal)
912 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
603 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
913
604
914 if not file:
605 file = frame_info.filename
915 file = '?'
916 elif file.startswith(str("<")) and file.endswith(str(">")):
917 # Not a real filename, no problem...
918 pass
919 elif not os.path.isabs(file):
920 # Try to make the filename absolute by trying all
921 # sys.path entries (which is also what linecache does)
922 for dirname in sys.path:
923 try:
924 fullname = os.path.join(dirname, file)
925 if os.path.isfile(fullname):
926 file = os.path.abspath(fullname)
927 break
928 except Exception:
929 # Just in case that sys.path contains very
930 # strange entries...
931 pass
932
933 file = py3compat.cast_unicode(file, util_path.fs_encoding)
606 file = py3compat.cast_unicode(file, util_path.fs_encoding)
934 link = tpl_link % util_path.compress_user(file)
607 link = tpl_link % util_path.compress_user(file)
935 args, varargs, varkw, locals_ = inspect.getargvalues(frame)
608 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
936
609
937 if func == '?':
610 func = frame_info.executing.code_qualname()
938 call = ''
611 if func == '<module>':
939 elif func == '<module>':
940 call = tpl_call % (func, '')
612 call = tpl_call % (func, '')
941 else:
613 else:
942 # Decide whether to include variable details or not
614 # Decide whether to include variable details or not
@@ -964,111 +636,19 b' class VerboseTB(TBTools):'
964 # disabled.
636 # disabled.
965 call = tpl_call_fail % func
637 call = tpl_call_fail % func
966
638
967 # Don't attempt to tokenize binary files.
968 if file.endswith(('.so', '.pyd', '.dll')):
969 return '%s %s\n' % (link, call)
970
971 elif file.endswith(('.pyc', '.pyo')):
972 # Look up the corresponding source file.
973 try:
974 file = source_from_cache(file)
975 except ValueError:
976 # Failed to get the source file for some reason
977 # E.g. https://github.com/ipython/ipython/issues/9486
978 return '%s %s\n' % (link, call)
979
980 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
981 line = getline(file, lnum[0])
982 lnum[0] += 1
983 return line
984
985 # Build the list of names on this line of code where the exception
986 # occurred.
987 try:
988 names = []
989 name_cont = False
990
991 for token_type, token, start, end, line in generate_tokens(linereader):
992 # build composite names
993 if token_type == tokenize.NAME and token not in keyword.kwlist:
994 if name_cont:
995 # Continuation of a dotted name
996 try:
997 names[-1].append(token)
998 except IndexError:
999 names.append([token])
1000 name_cont = False
1001 else:
1002 # Regular new names. We append everything, the caller
1003 # will be responsible for pruning the list later. It's
1004 # very tricky to try to prune as we go, b/c composite
1005 # names can fool us. The pruning at the end is easy
1006 # to do (or the caller can print a list with repeated
1007 # names if so desired.
1008 names.append([token])
1009 elif token == '.':
1010 name_cont = True
1011 elif token_type == tokenize.NEWLINE:
1012 break
1013
1014 except (IndexError, UnicodeDecodeError, SyntaxError):
1015 # signals exit of tokenizer
1016 # SyntaxError can occur if the file is not actually Python
1017 # - see gh-6300
1018 pass
1019 except tokenize.TokenError as msg:
1020 # Tokenizing may fail for various reasons, many of which are
1021 # harmless. (A good example is when the line in question is the
1022 # close of a triple-quoted string, cf gh-6864). We don't want to
1023 # show this to users, but want make it available for debugging
1024 # purposes.
1025 _m = ("An unexpected error occurred while tokenizing input\n"
1026 "The following traceback may be corrupted or invalid\n"
1027 "The error message is: %s\n" % msg)
1028 debug(_m)
1029
1030 # Join composite names (e.g. "dict.fromkeys")
1031 names = ['.'.join(n) for n in names]
1032 # prune names list of duplicates, but keep the right order
1033 unique_names = uniq_stable(names)
1034
1035 # Start loop over vars
1036 lvals = ''
639 lvals = ''
1037 lvals_list = []
640 lvals_list = []
1038 if self.include_vars:
641 if self.include_vars:
1039 for name_full in unique_names:
642 for var in frame_info.variables_in_executing_piece:
1040 name_base = name_full.split('.', 1)[0]
643 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
1041 if name_base in frame.f_code.co_varnames:
1042 if name_base in locals_:
1043 try:
1044 value = repr(eval(name_full, locals_))
1045 except:
1046 value = undefined
1047 else:
1048 value = undefined
1049 name = tpl_local_var % name_full
1050 else:
1051 if name_base in frame.f_globals:
1052 try:
1053 value = repr(eval(name_full, frame.f_globals))
1054 except:
1055 value = undefined
1056 else:
1057 value = undefined
1058 name = tpl_global_var % name_full
1059 lvals_list.append(tpl_name_val % (name, value))
1060 if lvals_list:
644 if lvals_list:
1061 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
645 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
1062
646
1063 level = '%s %s\n' % (link, call)
647 result = '%s %s\n' % (link, call)
1064
648
1065 if index is None:
1066 return level
1067 else:
1068 _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2
649 _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2
1069 return '%s%s' % (level, ''.join(
650 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, lvals, _line_format))
1070 _format_traceback_lines(lnum, index, lines, Colors, lvals,
651 return result
1071 _line_format)))
1072
652
1073 def prepare_header(self, etype, long_version=False):
653 def prepare_header(self, etype, long_version=False):
1074 colors = self.Colors # just a shorthand + quicker name lookup
654 colors = self.Colors # just a shorthand + quicker name lookup
@@ -1123,46 +703,23 b' class VerboseTB(TBTools):'
1123 head = self.prepare_header(etype, self.long_header)
703 head = self.prepare_header(etype, self.long_header)
1124 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
704 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1125
705
1126 if records is None:
706 frames = list(map(self.format_record, records))
1127 return ""
1128
1129 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1130
1131 frames = self.format_records(records, last_unique, recursion_repeat)
1132
707
1133 formatted_exception = self.format_exception(etype, evalue)
708 formatted_exception = self.format_exception(etype, evalue)
1134 if records:
709 if records:
1135 filepath, lnum = records[-1][1:3]
710 frame_info = records[-1]
1136 filepath = os.path.abspath(filepath)
1137 ipinst = get_ipython()
711 ipinst = get_ipython()
1138 if ipinst is not None:
712 if ipinst is not None:
1139 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
713 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
1140
714
1141 return [[head] + frames + [''.join(formatted_exception[0])]]
715 return [[head] + frames + [''.join(formatted_exception[0])]]
1142
716
1143 def get_records(self, etb, number_of_lines_of_context, tb_offset):
717 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1144 try:
718 context = number_of_lines_of_context - 1
1145 # Try the default getinnerframes and Alex's: Alex's fixes some
719 after = context // 2
1146 # problems, but it generates empty tracebacks for console errors
720 before = context - after
1147 # (5 blanks lines) where none should be returned.
721 options = stack_data.Options(before=before, after=after)
1148 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
722 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1149 except UnicodeDecodeError:
1150 # This can occur if a file's encoding magic comment is wrong.
1151 # I can't see a way to recover without duplicating a bunch of code
1152 # from the stdlib traceback module. --TK
1153 error('\nUnicodeDecodeError while processing traceback.\n')
1154 return None
1155 except:
1156 # FIXME: I've been getting many crash reports from python 2.3
1157 # users, traceable to inspect.py. If I can find a small test-case
1158 # to reproduce this, I should either write a better workaround or
1159 # file a bug report against inspect (if that's the real problem).
1160 # So far, I haven't been able to find an isolated example to
1161 # reproduce the problem.
1162 inspect_error()
1163 traceback.print_exc(file=self.ostream)
1164 info('\nUnfortunately, your original traceback can not be constructed.\n')
1165 return None
1166
723
1167 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
724 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1168 number_of_lines_of_context=5):
725 number_of_lines_of_context=5):
@@ -193,6 +193,7 b' install_requires = ['
193 'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
193 'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
194 'pygments',
194 'pygments',
195 'backcall',
195 'backcall',
196 'stack_data',
196 ]
197 ]
197
198
198 # Platform-specific dependencies:
199 # Platform-specific dependencies:
General Comments 0
You need to be logged in to leave comments. Login now