##// 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 65 ZeroDivisionError Traceback (most recent call last)
66 66 <BLANKLINE>
67 67 ... in <module>
68 29 except IndexError:
68 69 30 mode = 'div'
69 31
70 70 ---> 32 bar(mode)
71 71 <BLANKLINE>
72 72 ... in bar(mode)
@@ -80,8 +80,6 b' ZeroDivisionError Traceback (most recent call last)'
80 80 6 x = 1
81 81 7 y = 0
82 82 ----> 8 x/y
83 9
84 10 def sysexit(stat, mode):
85 83 <BLANKLINE>
86 84 ZeroDivisionError: ...
87 85 """
@@ -97,17 +95,15 b' In [6]: run simpleerr.py'
97 95 ZeroDivisionError Traceback (most recent call last)
98 96 <BLANKLINE>
99 97 ... in <module>
98 29 except IndexError:
100 99 30 mode = 'div'
101 31
102 100 ---> 32 bar(mode)
103 global bar = <function bar at ...>
104 global mode = 'div'
101 mode = 'div'
105 102 <BLANKLINE>
106 103 ... in bar(mode='div')
107 104 14 "bar"
108 105 15 if mode=='div':
109 106 ---> 16 div0()
110 global div0 = <function div0 at ...>
111 107 17 elif mode=='exit':
112 108 18 try:
113 109 <BLANKLINE>
@@ -117,8 +113,6 b' ZeroDivisionError Traceback (most recent call last)'
117 113 ----> 8 x/y
118 114 x = 1
119 115 y = 0
120 9
121 10 def sysexit(stat, mode):
122 116 <BLANKLINE>
123 117 ZeroDivisionError: ...
124 118 """
@@ -154,8 +148,8 b' In [22]: %tb'
154 148 SystemExit Traceback (most recent call last)
155 149 <BLANKLINE>
156 150 ...<module>
151 29 except IndexError:
157 152 30 mode = 'div'
158 31
159 153 ---> 32 bar(mode)
160 154 <BLANKLINE>
161 155 ...bar(mode)
@@ -166,11 +160,8 b' SystemExit Traceback (most recent call last)'
166 160 24 raise ValueError('Unknown mode')
167 161 <BLANKLINE>
168 162 ...sysexit(stat, mode)
169 9
170 163 10 def sysexit(stat, mode):
171 164 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
172 12
173 13 def bar(mode):
174 165 <BLANKLINE>
175 166 SystemExit: (2, 'Mode = exit')
176 167
@@ -182,31 +173,25 b' In [24]: %tb'
182 173 SystemExit Traceback (most recent call last)
183 174 <BLANKLINE>
184 175 ... in <module>
176 29 except IndexError:
185 177 30 mode = 'div'
186 31
187 178 ---> 32 bar(mode)
188 global bar = <function bar at ...>
189 global mode = 'exit'
179 mode = 'exit'
190 180 <BLANKLINE>
191 181 ... in bar(mode='exit')
192 182 20 except:
193 183 21 stat = 1
194 184 ---> 22 sysexit(stat, mode)
195 global sysexit = <function sysexit at ...>
196 stat = 2
197 185 mode = 'exit'
186 stat = 2
198 187 23 else:
199 188 24 raise ValueError('Unknown mode')
200 189 <BLANKLINE>
201 190 ... in sysexit(stat=2, mode='exit')
202 9
203 191 10 def sysexit(stat, mode):
204 192 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
205 global SystemExit = undefined
206 193 stat = 2
207 194 mode = 'exit'
208 12
209 13 def bar(mode):
210 195 <BLANKLINE>
211 196 SystemExit: (2, 'Mode = exit')
212 197 """
@@ -3,15 +3,14 b''
3 3 """
4 4 import io
5 5 import logging
6 import re
6 7 import sys
7 8 import os.path
8 9 from textwrap import dedent
9 10 import traceback
10 11 import unittest
11 from unittest import mock
12 12
13 import IPython.core.ultratb as ultratb
14 from IPython.core.ultratb import ColorTB, VerboseTB, find_recursion
13 from IPython.core.ultratb import ColorTB, VerboseTB
15 14
16 15
17 16 from IPython.testing import tools as tt
@@ -38,16 +37,12 b' def recursionlimit(frames):'
38 37
39 38 def inner(test_function):
40 39 def wrapper(*args, **kwargs):
41 _orig_rec_limit = ultratb._FRAME_RECURSION_LIMIT
42 ultratb._FRAME_RECURSION_LIMIT = 50
43
44 40 rl = sys.getrecursionlimit()
45 41 sys.setrecursionlimit(frames)
46 42 try:
47 43 return test_function(*args, **kwargs)
48 44 finally:
49 45 sys.setrecursionlimit(rl)
50 ultratb._FRAME_RECURSION_LIMIT = _orig_rec_limit
51 46
52 47 return wrapper
53 48
@@ -350,45 +345,24 b' def r3o2():'
350 345 ip.run_cell(self.DEFINITIONS)
351 346
352 347 def test_no_recursion(self):
353 with tt.AssertNotPrints("frames repeated"):
348 with tt.AssertNotPrints("skipping similar frames"):
354 349 ip.run_cell("non_recurs()")
355 350
356 351 @recursionlimit(150)
357 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 356 ip.run_cell("r1()")
360 357
361 358 @recursionlimit(150)
362 359 def test_recursion_three_frames(self):
363 with tt.AssertPrints("3 frames repeated"):
364 ip.run_cell("r3o2()")
365
366 @recursionlimit(150)
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):
360 with tt.AssertPrints("[... skipping similar frames: "), \
361 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
362 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
363 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
372 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 406 except:
433 407 handler(*sys.exc_info())
434 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 92 import inspect
94 import keyword
95 93 import linecache
96 import os
97 94 import pydoc
98 import re
99 95 import sys
100 96 import time
101 import tokenize
102 97 import traceback
103 98
104 from tokenize import generate_tokens
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
99 import stack_data
109 100
110 101 # IPython's own modules
111 102 from IPython import get_ipython
@@ -115,13 +106,8 b' from IPython.core.excolors import exception_colors'
115 106 from IPython.utils import PyColorize
116 107 from IPython.utils import path as util_path
117 108 from IPython.utils import py3compat
118 from IPython.utils.data import uniq_stable
119 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 111 import IPython.utils.colorable as colorable
126 112
127 113 # Globals
@@ -134,264 +120,26 b' INDENT_SIZE = 8'
134 120 # to users of ultratb who are NOT running inside ipython.
135 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 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 126 # Helper function -- largely belongs to VerboseTB, but we need the same
377 127 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
378 128 # can be recognized properly by ipython.el's py-traceback-line-re
379 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 134 Format tracebacks lines with pointing arrow, leading numbers...
385 135
386 136 Parameters
387 137 ==========
388 138
389 lnum: int
390 index: int
391 lines: list[string]
139 lines: list[Line]
392 140 Colors:
393 141 ColorScheme used.
394 lvals: bytes
142 lvals: str
395 143 Values of local variables, already colored, to inject just after the error line.
396 144 _line_format: f (str) -> (str, bool)
397 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 147 numbers_width = INDENT_SIZE - 1
400 148 res = []
401 149
402 for i,line in enumerate(lines, lnum-index):
403 line = py3compat.cast_unicode(line)
150 for stack_line in lines:
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 157 new_line, err = _line_format(line, 'str')
406 158 if not err:
407 159 line = new_line
408 160
409 if i == lnum:
161 lineno = stack_line.lineno
162 if stack_line.is_current:
410 163 # This is the line with the error
411 pad = numbers_width - len(str(i))
412 num = '%s%s' % (debugger.make_arrow(pad), str(lnum))
164 pad = numbers_width - len(str(lineno))
165 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
413 166 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
414 167 Colors.line, line, Colors.Normal)
415 168 else:
416 num = '%*s' % (numbers_width, i)
169 num = '%*s' % (numbers_width, lineno)
417 170 line = '%s%s%s %s' % (Colors.lineno, num,
418 171 Colors.Normal, line)
419 172
420 173 res.append(line)
421 if lvals and i == lnum:
174 if lvals and stack_line.is_current:
422 175 res.append(lvals + '\n')
423 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 180 # Module classes
@@ -880,63 +582,33 b' class VerboseTB(TBTools):'
880 582
881 583 self.debugger_cls = debugger_cls or debugger.Pdb
882 584
883 def format_records(self, records, last_unique, recursion_repeat):
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):
585 def format_record(self, frame_info):
897 586 """Format a single stack frame"""
898 587 Colors = self.Colors # just a shorthand + quicker name lookup
899 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 594 col_scheme = self.color_scheme_table.active_scheme_name
901 595 indent = ' ' * INDENT_SIZE
902 596 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
903 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
904 597 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
905 598 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
906 599 ColorsNormal)
907 600 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
908 601 (Colors.vName, Colors.valEm, ColorsNormal)
909 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 603 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
913 604
914 if not file:
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
605 file = frame_info.filename
933 606 file = py3compat.cast_unicode(file, util_path.fs_encoding)
934 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 == '?':
938 call = ''
939 elif func == '<module>':
610 func = frame_info.executing.code_qualname()
611 if func == '<module>':
940 612 call = tpl_call % (func, '')
941 613 else:
942 614 # Decide whether to include variable details or not
@@ -964,111 +636,19 b' class VerboseTB(TBTools):'
964 636 # disabled.
965 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 639 lvals = ''
1037 640 lvals_list = []
1038 641 if self.include_vars:
1039 for name_full in unique_names:
1040 name_base = name_full.split('.', 1)[0]
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))
642 for var in frame_info.variables_in_executing_piece:
643 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
1060 644 if lvals_list:
1061 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 649 _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2
1069 return '%s%s' % (level, ''.join(
1070 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1071 _line_format)))
650 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, lvals, _line_format))
651 return result
1072 652
1073 653 def prepare_header(self, etype, long_version=False):
1074 654 colors = self.Colors # just a shorthand + quicker name lookup
@@ -1123,46 +703,23 b' class VerboseTB(TBTools):'
1123 703 head = self.prepare_header(etype, self.long_header)
1124 704 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1125 705
1126 if records is None:
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)
706 frames = list(map(self.format_record, records))
1132 707
1133 708 formatted_exception = self.format_exception(etype, evalue)
1134 709 if records:
1135 filepath, lnum = records[-1][1:3]
1136 filepath = os.path.abspath(filepath)
710 frame_info = records[-1]
1137 711 ipinst = get_ipython()
1138 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 715 return [[head] + frames + [''.join(formatted_exception[0])]]
1142 716
1143 717 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1144 try:
1145 # Try the default getinnerframes and Alex's: Alex's fixes some
1146 # problems, but it generates empty tracebacks for console errors
1147 # (5 blanks lines) where none should be returned.
1148 return _fixed_getinnerframes(etb, number_of_lines_of_context, 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
718 context = number_of_lines_of_context - 1
719 after = context // 2
720 before = context - after
721 options = stack_data.Options(before=before, after=after)
722 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1166 723
1167 724 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1168 725 number_of_lines_of_context=5):
@@ -193,6 +193,7 b' install_requires = ['
193 193 'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
194 194 'pygments',
195 195 'backcall',
196 'stack_data',
196 197 ]
197 198
198 199 # Platform-specific dependencies:
General Comments 0
You need to be logged in to leave comments. Login now