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 |
|
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( |
|
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(" |
|
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, (523 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( |
|
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: |
|
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(l |
|
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 |
|
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_record |
|
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 |
|
|
647 | result = '%s %s\n' % (link, call) | |
1064 |
|
648 | |||
1065 | if index is None: |
|
649 | _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2 | |
1066 | return level |
|
650 | result += ''.join(_format_traceback_lines(frame_info.lines, Colors, lvals, _line_format)) | |
1067 | else: |
|
651 | return result | |
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))) |
|
|||
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 |
f |
|
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(f |
|
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