Show More
This diff has been collapsed as it changes many lines, (516 lines changed) Show them Hide them | |||
@@ -89,23 +89,20 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 | 97 | import tokenize |
|
102 | 98 | import traceback |
|
103 | 99 | |
|
104 | from tokenize import generate_tokens | |
|
100 | import stack_data | |
|
105 | 101 | |
|
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 | |
|
102 | try: # Python 2 | |
|
103 | generate_tokens = tokenize.generate_tokens | |
|
104 | except AttributeError: # Python 3 | |
|
105 | generate_tokens = tokenize.tokenize | |
|
109 | 106 | |
|
110 | 107 | # IPython's own modules |
|
111 | 108 | from IPython import get_ipython |
@@ -115,13 +112,8 b' from IPython.core.excolors import exception_colors' | |||
|
115 | 112 | from IPython.utils import PyColorize |
|
116 | 113 | from IPython.utils import path as util_path |
|
117 | 114 | from IPython.utils import py3compat |
|
118 | from IPython.utils.data import uniq_stable | |
|
119 | 115 | from IPython.utils.terminal import get_terminal_size |
|
120 | 116 | |
|
121 | from logging import info, error, debug | |
|
122 | ||
|
123 | from importlib.util import source_from_cache | |
|
124 | ||
|
125 | 117 | import IPython.utils.colorable as colorable |
|
126 | 118 | |
|
127 | 119 | # Globals |
@@ -134,264 +126,26 b' INDENT_SIZE = 8' | |||
|
134 | 126 | # to users of ultratb who are NOT running inside ipython. |
|
135 | 127 | DEFAULT_SCHEME = 'NoColor' |
|
136 | 128 | |
|
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 | 129 | # --------------------------------------------------------------------------- |
|
144 | 130 | # Code begins |
|
145 | 131 | |
|
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 | 132 | # Helper function -- largely belongs to VerboseTB, but we need the same |
|
377 | 133 | # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they |
|
378 | 134 | # can be recognized properly by ipython.el's py-traceback-line-re |
|
379 | 135 | # (SyntaxErrors have to be treated specially because they have no traceback) |
|
380 | 136 | |
|
381 | 137 | |
|
382 |
def _format_traceback_lines( |
|
|
138 | def _format_traceback_lines(lines, Colors, lvals, _line_format): | |
|
383 | 139 | """ |
|
384 | 140 | Format tracebacks lines with pointing arrow, leading numbers... |
|
385 | 141 | |
|
386 | 142 | Parameters |
|
387 | 143 | ========== |
|
388 | 144 | |
|
389 | lnum: int | |
|
390 | index: int | |
|
391 | lines: list[string] | |
|
145 | lines: list[Line] | |
|
392 | 146 | Colors: |
|
393 | 147 | ColorScheme used. |
|
394 |
lvals: |
|
|
148 | lvals: str | |
|
395 | 149 | Values of local variables, already colored, to inject just after the error line. |
|
396 | 150 | _line_format: f (str) -> (str, bool) |
|
397 | 151 | return (colorized version of str, failure to do so) |
@@ -399,80 +153,30 b' def _format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):' | |||
|
399 | 153 | numbers_width = INDENT_SIZE - 1 |
|
400 | 154 | res = [] |
|
401 | 155 | |
|
402 | for i,line in enumerate(lines, lnum-index): | |
|
403 | line = py3compat.cast_unicode(line) | |
|
156 | for stack_line in lines: | |
|
157 | line = stack_line.text.rstrip('\n') + '\n' | |
|
404 | 158 | |
|
405 | 159 | new_line, err = _line_format(line, 'str') |
|
406 | 160 | if not err: |
|
407 | 161 | line = new_line |
|
408 | 162 | |
|
409 | if i == lnum: | |
|
163 | lineno = stack_line.lineno | |
|
164 | if stack_line.is_current: | |
|
410 | 165 | # This is the line with the error |
|
411 | pad = numbers_width - len(str(i)) | |
|
412 |
num = '%s%s' % (debugger.make_arrow(pad), str(l |
|
|
166 | pad = numbers_width - len(str(lineno)) | |
|
167 | num = '%s%s' % (debugger.make_arrow(pad), str(lineno)) | |
|
413 | 168 | line = '%s%s%s %s%s' % (Colors.linenoEm, num, |
|
414 | 169 | Colors.line, line, Colors.Normal) |
|
415 | 170 | else: |
|
416 | num = '%*s' % (numbers_width, i) | |
|
171 | num = '%*s' % (numbers_width, lineno) | |
|
417 | 172 | line = '%s%s%s %s' % (Colors.lineno, num, |
|
418 | 173 | Colors.Normal, line) |
|
419 | 174 | |
|
420 | 175 | res.append(line) |
|
421 |
if lvals and |
|
|
176 | if lvals and stack_line.is_current: | |
|
422 | 177 | res.append(lvals + '\n') |
|
423 | 178 | return res |
|
424 | 179 | |
|
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 | 180 | |
|
477 | 181 | #--------------------------------------------------------------------------- |
|
478 | 182 | # Module classes |
@@ -880,63 +584,28 b' class VerboseTB(TBTools):' | |||
|
880 | 584 | |
|
881 | 585 | self.debugger_cls = debugger_cls or debugger.Pdb |
|
882 | 586 | |
|
883 |
def format_record |
|
|
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): | |
|
587 | def format_record(self, frame_info): | |
|
897 | 588 | """Format a single stack frame""" |
|
898 | 589 | Colors = self.Colors # just a shorthand + quicker name lookup |
|
899 | 590 | ColorsNormal = Colors.Normal # used a lot |
|
900 | 591 | col_scheme = self.color_scheme_table.active_scheme_name |
|
901 | 592 | indent = ' ' * INDENT_SIZE |
|
902 | 593 | em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal) |
|
903 | undefined = '%sundefined%s' % (Colors.em, ColorsNormal) | |
|
904 | 594 | tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) |
|
905 | 595 | tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm, |
|
906 | 596 | ColorsNormal) |
|
907 | 597 | tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \ |
|
908 | 598 | (Colors.vName, Colors.valEm, ColorsNormal) |
|
909 | 599 | 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 | 600 | tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal) |
|
913 | 601 | |
|
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 | ||
|
602 | file = frame_info.filename | |
|
933 | 603 | file = py3compat.cast_unicode(file, util_path.fs_encoding) |
|
934 | 604 | link = tpl_link % util_path.compress_user(file) |
|
935 | args, varargs, varkw, locals_ = inspect.getargvalues(frame) | |
|
605 | args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame) | |
|
936 | 606 | |
|
937 | if func == '?': | |
|
938 | call = '' | |
|
939 | elif func == '<module>': | |
|
607 | func = frame_info.executing.code_qualname() | |
|
608 | if func == '<module>': | |
|
940 | 609 | call = tpl_call % (func, '') |
|
941 | 610 | else: |
|
942 | 611 | # Decide whether to include variable details or not |
@@ -964,111 +633,19 b' class VerboseTB(TBTools):' | |||
|
964 | 633 | # disabled. |
|
965 | 634 | call = tpl_call_fail % func |
|
966 | 635 | |
|
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 | 636 | lvals = '' |
|
1037 | 637 | lvals_list = [] |
|
1038 | 638 | 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)) | |
|
639 | for var in frame_info.variables_in_executing_piece: | |
|
640 | lvals_list.append(tpl_name_val % (var.name, var.value)) | |
|
1060 | 641 | if lvals_list: |
|
1061 | 642 | lvals = '%s%s' % (indent, em_normal.join(lvals_list)) |
|
1062 | 643 | |
|
1063 |
|
|
|
644 | result = '%s %s\n' % (link, call) | |
|
1064 | 645 | |
|
1065 | if index is None: | |
|
1066 | return level | |
|
1067 | else: | |
|
1068 | _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))) | |
|
646 | _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2 | |
|
647 | result += ''.join(_format_traceback_lines(frame_info.lines, Colors, lvals, _line_format)) | |
|
648 | return result | |
|
1072 | 649 | |
|
1073 | 650 | def prepare_header(self, etype, long_version=False): |
|
1074 | 651 | colors = self.Colors # just a shorthand + quicker name lookup |
@@ -1123,46 +700,23 b' class VerboseTB(TBTools):' | |||
|
1123 | 700 | head = self.prepare_header(etype, self.long_header) |
|
1124 | 701 | records = self.get_records(etb, number_of_lines_of_context, tb_offset) |
|
1125 | 702 | |
|
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) | |
|
703 | frames = list(map(self.format_record, records)) | |
|
1132 | 704 | |
|
1133 | 705 | formatted_exception = self.format_exception(etype, evalue) |
|
1134 | 706 | if records: |
|
1135 |
f |
|
|
1136 | filepath = os.path.abspath(filepath) | |
|
707 | frame_info = records[-1] | |
|
1137 | 708 | ipinst = get_ipython() |
|
1138 | 709 | if ipinst is not None: |
|
1139 |
ipinst.hooks.synchronize_with_editor(f |
|
|
710 | ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0) | |
|
1140 | 711 | |
|
1141 | 712 | return [[head] + frames + [''.join(formatted_exception[0])]] |
|
1142 | 713 | |
|
1143 | 714 | 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 | |
|
715 | context = number_of_lines_of_context - 1 | |
|
716 | after = context // 2 | |
|
717 | before = context - after | |
|
718 | options = stack_data.Options(before=before, after=after) | |
|
719 | return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:] | |
|
1166 | 720 | |
|
1167 | 721 | def structured_traceback(self, etype, evalue, etb, tb_offset=None, |
|
1168 | 722 | number_of_lines_of_context=5): |
General Comments 0
You need to be logged in to leave comments.
Login now