##// END OF EJS Templates
Fix traceback handling of SyntaxErrors without linenos.
Jez Ng -
Show More
@@ -1,95 +1,101 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.core.ultratb
2 """Tests for IPython.core.ultratb
3 """
3 """
4 import io
4 import io
5 import os.path
5 import os.path
6 import unittest
6 import unittest
7
7
8 from IPython.testing import tools as tt
8 from IPython.testing import tools as tt
9 from IPython.utils.syspathcontext import prepended_to_syspath
9 from IPython.utils.syspathcontext import prepended_to_syspath
10 from IPython.utils.tempdir import TemporaryDirectory
10 from IPython.utils.tempdir import TemporaryDirectory
11
11
12 ip = get_ipython()
12 ip = get_ipython()
13
13
14 file_1 = """1
14 file_1 = """1
15 2
15 2
16 3
16 3
17 def f():
17 def f():
18 1/0
18 1/0
19 """
19 """
20
20
21 file_2 = """def f():
21 file_2 = """def f():
22 1/0
22 1/0
23 """
23 """
24
24
25 class ChangedPyFileTest(unittest.TestCase):
25 class ChangedPyFileTest(unittest.TestCase):
26 def test_changing_py_file(self):
26 def test_changing_py_file(self):
27 """Traceback produced if the line where the error occurred is missing?
27 """Traceback produced if the line where the error occurred is missing?
28
28
29 https://github.com/ipython/ipython/issues/1456
29 https://github.com/ipython/ipython/issues/1456
30 """
30 """
31 with TemporaryDirectory() as td:
31 with TemporaryDirectory() as td:
32 fname = os.path.join(td, "foo.py")
32 fname = os.path.join(td, "foo.py")
33 with open(fname, "w") as f:
33 with open(fname, "w") as f:
34 f.write(file_1)
34 f.write(file_1)
35
35
36 with prepended_to_syspath(td):
36 with prepended_to_syspath(td):
37 ip.run_cell("import foo")
37 ip.run_cell("import foo")
38
38
39 with tt.AssertPrints("ZeroDivisionError"):
39 with tt.AssertPrints("ZeroDivisionError"):
40 ip.run_cell("foo.f()")
40 ip.run_cell("foo.f()")
41
41
42 # Make the file shorter, so the line of the error is missing.
42 # Make the file shorter, so the line of the error is missing.
43 with open(fname, "w") as f:
43 with open(fname, "w") as f:
44 f.write(file_2)
44 f.write(file_2)
45
45
46 # For some reason, this was failing on the *second* call after
46 # For some reason, this was failing on the *second* call after
47 # changing the file, so we call f() twice.
47 # changing the file, so we call f() twice.
48 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
48 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
49 with tt.AssertPrints("ZeroDivisionError"):
49 with tt.AssertPrints("ZeroDivisionError"):
50 ip.run_cell("foo.f()")
50 ip.run_cell("foo.f()")
51 with tt.AssertPrints("ZeroDivisionError"):
51 with tt.AssertPrints("ZeroDivisionError"):
52 ip.run_cell("foo.f()")
52 ip.run_cell("foo.f()")
53
53
54 iso_8859_5_file = u'''# coding: iso-8859-5
54 iso_8859_5_file = u'''# coding: iso-8859-5
55
55
56 def fail():
56 def fail():
57 """Π΄Π±Π˜Π–"""
57 """Π΄Π±Π˜Π–"""
58 1/0 # Π΄Π±Π˜Π–
58 1/0 # Π΄Π±Π˜Π–
59 '''
59 '''
60
60
61 class NonAsciiTest(unittest.TestCase):
61 class NonAsciiTest(unittest.TestCase):
62 def test_iso8859_5(self):
62 def test_iso8859_5(self):
63 # Non-ascii directory name as well.
63 # Non-ascii directory name as well.
64 with TemporaryDirectory(suffix=u'Γ©') as td:
64 with TemporaryDirectory(suffix=u'Γ©') as td:
65 fname = os.path.join(td, 'dfghjkl.py')
65 fname = os.path.join(td, 'dfghjkl.py')
66
66
67 with io.open(fname, 'w', encoding='iso-8859-5') as f:
67 with io.open(fname, 'w', encoding='iso-8859-5') as f:
68 f.write(iso_8859_5_file)
68 f.write(iso_8859_5_file)
69
69
70 with prepended_to_syspath(td):
70 with prepended_to_syspath(td):
71 ip.run_cell("from dfghjkl import fail")
71 ip.run_cell("from dfghjkl import fail")
72
72
73 with tt.AssertPrints("ZeroDivisionError"):
73 with tt.AssertPrints("ZeroDivisionError"):
74 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
74 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
75 ip.run_cell('fail()')
75 ip.run_cell('fail()')
76
76
77 indentationerror_file = """if True:
77 indentationerror_file = """if True:
78 zoon()
78 zoon()
79 """
79 """
80
80
81 class IndentationErrorTest(unittest.TestCase):
81 class IndentationErrorTest(unittest.TestCase):
82 def test_indentationerror_shows_line(self):
82 def test_indentationerror_shows_line(self):
83 # See issue gh-2398
83 # See issue gh-2398
84 with tt.AssertPrints("IndentationError"):
84 with tt.AssertPrints("IndentationError"):
85 with tt.AssertPrints("zoon()", suppress=False):
85 with tt.AssertPrints("zoon()", suppress=False):
86 ip.run_cell(indentationerror_file)
86 ip.run_cell(indentationerror_file)
87
87
88 with TemporaryDirectory() as td:
88 with TemporaryDirectory() as td:
89 fname = os.path.join(td, "foo.py")
89 fname = os.path.join(td, "foo.py")
90 with open(fname, "w") as f:
90 with open(fname, "w") as f:
91 f.write(indentationerror_file)
91 f.write(indentationerror_file)
92
92
93 with tt.AssertPrints("IndentationError"):
93 with tt.AssertPrints("IndentationError"):
94 with tt.AssertPrints("zoon()", suppress=False):
94 with tt.AssertPrints("zoon()", suppress=False):
95 ip.magic('run %s' % fname)
95 ip.magic('run %s' % fname)
96
97 class SyntaxErrorTest(unittest.TestCase):
98 def test_syntaxerror_without_lineno(self):
99 with tt.AssertNotPrints("TypeError"):
100 with tt.AssertPrints("line unknown"):
101 ip.run_cell("raise SyntaxError()")
@@ -1,1237 +1,1238 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 ultratb.py -- Spice up your tracebacks!
3 ultratb.py -- Spice up your tracebacks!
4
4
5 * ColorTB
5 * ColorTB
6 I've always found it a bit hard to visually parse tracebacks in Python. The
6 I've always found it a bit hard to visually parse tracebacks in Python. The
7 ColorTB class is a solution to that problem. It colors the different parts of a
7 ColorTB class is a solution to that problem. It colors the different parts of a
8 traceback in a manner similar to what you would expect from a syntax-highlighting
8 traceback in a manner similar to what you would expect from a syntax-highlighting
9 text editor.
9 text editor.
10
10
11 Installation instructions for ColorTB:
11 Installation instructions for ColorTB:
12 import sys,ultratb
12 import sys,ultratb
13 sys.excepthook = ultratb.ColorTB()
13 sys.excepthook = ultratb.ColorTB()
14
14
15 * VerboseTB
15 * VerboseTB
16 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
16 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
17 of useful info when a traceback occurs. Ping originally had it spit out HTML
17 of useful info when a traceback occurs. Ping originally had it spit out HTML
18 and intended it for CGI programmers, but why should they have all the fun? I
18 and intended it for CGI programmers, but why should they have all the fun? I
19 altered it to spit out colored text to the terminal. It's a bit overwhelming,
19 altered it to spit out colored text to the terminal. It's a bit overwhelming,
20 but kind of neat, and maybe useful for long-running programs that you believe
20 but kind of neat, and maybe useful for long-running programs that you believe
21 are bug-free. If a crash *does* occur in that type of program you want details.
21 are bug-free. If a crash *does* occur in that type of program you want details.
22 Give it a shot--you'll love it or you'll hate it.
22 Give it a shot--you'll love it or you'll hate it.
23
23
24 Note:
24 Note:
25
25
26 The Verbose mode prints the variables currently visible where the exception
26 The Verbose mode prints the variables currently visible where the exception
27 happened (shortening their strings if too long). This can potentially be
27 happened (shortening their strings if too long). This can potentially be
28 very slow, if you happen to have a huge data structure whose string
28 very slow, if you happen to have a huge data structure whose string
29 representation is complex to compute. Your computer may appear to freeze for
29 representation is complex to compute. Your computer may appear to freeze for
30 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
30 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
31 with Ctrl-C (maybe hitting it more than once).
31 with Ctrl-C (maybe hitting it more than once).
32
32
33 If you encounter this kind of situation often, you may want to use the
33 If you encounter this kind of situation often, you may want to use the
34 Verbose_novars mode instead of the regular Verbose, which avoids formatting
34 Verbose_novars mode instead of the regular Verbose, which avoids formatting
35 variables (but otherwise includes the information and context given by
35 variables (but otherwise includes the information and context given by
36 Verbose).
36 Verbose).
37
37
38
38
39 Installation instructions for ColorTB:
39 Installation instructions for ColorTB:
40 import sys,ultratb
40 import sys,ultratb
41 sys.excepthook = ultratb.VerboseTB()
41 sys.excepthook = ultratb.VerboseTB()
42
42
43 Note: Much of the code in this module was lifted verbatim from the standard
43 Note: Much of the code in this module was lifted verbatim from the standard
44 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
44 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
45
45
46 * Color schemes
46 * Color schemes
47 The colors are defined in the class TBTools through the use of the
47 The colors are defined in the class TBTools through the use of the
48 ColorSchemeTable class. Currently the following exist:
48 ColorSchemeTable class. Currently the following exist:
49
49
50 - NoColor: allows all of this module to be used in any terminal (the color
50 - NoColor: allows all of this module to be used in any terminal (the color
51 escapes are just dummy blank strings).
51 escapes are just dummy blank strings).
52
52
53 - Linux: is meant to look good in a terminal like the Linux console (black
53 - Linux: is meant to look good in a terminal like the Linux console (black
54 or very dark background).
54 or very dark background).
55
55
56 - LightBG: similar to Linux but swaps dark/light colors to be more readable
56 - LightBG: similar to Linux but swaps dark/light colors to be more readable
57 in light background terminals.
57 in light background terminals.
58
58
59 You can implement other color schemes easily, the syntax is fairly
59 You can implement other color schemes easily, the syntax is fairly
60 self-explanatory. Please send back new schemes you develop to the author for
60 self-explanatory. Please send back new schemes you develop to the author for
61 possible inclusion in future releases.
61 possible inclusion in future releases.
62 """
62 """
63
63
64 #*****************************************************************************
64 #*****************************************************************************
65 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
65 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
66 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
66 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
67 #
67 #
68 # Distributed under the terms of the BSD License. The full license is in
68 # Distributed under the terms of the BSD License. The full license is in
69 # the file COPYING, distributed as part of this software.
69 # the file COPYING, distributed as part of this software.
70 #*****************************************************************************
70 #*****************************************************************************
71
71
72 from __future__ import unicode_literals
72 from __future__ import unicode_literals
73
73
74 import inspect
74 import inspect
75 import keyword
75 import keyword
76 import linecache
76 import linecache
77 import os
77 import os
78 import pydoc
78 import pydoc
79 import re
79 import re
80 import sys
80 import sys
81 import time
81 import time
82 import tokenize
82 import tokenize
83 import traceback
83 import traceback
84 import types
84 import types
85
85
86 try: # Python 2
86 try: # Python 2
87 generate_tokens = tokenize.generate_tokens
87 generate_tokens = tokenize.generate_tokens
88 except AttributeError: # Python 3
88 except AttributeError: # Python 3
89 generate_tokens = tokenize.tokenize
89 generate_tokens = tokenize.tokenize
90
90
91 # For purposes of monkeypatching inspect to fix a bug in it.
91 # For purposes of monkeypatching inspect to fix a bug in it.
92 from inspect import getsourcefile, getfile, getmodule,\
92 from inspect import getsourcefile, getfile, getmodule,\
93 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
93 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
94
94
95 # IPython's own modules
95 # IPython's own modules
96 # Modified pdb which doesn't damage IPython's readline handling
96 # Modified pdb which doesn't damage IPython's readline handling
97 from IPython.core import debugger, ipapi
97 from IPython.core import debugger, ipapi
98 from IPython.core.display_trap import DisplayTrap
98 from IPython.core.display_trap import DisplayTrap
99 from IPython.core.excolors import exception_colors
99 from IPython.core.excolors import exception_colors
100 from IPython.utils import PyColorize
100 from IPython.utils import PyColorize
101 from IPython.utils import io
101 from IPython.utils import io
102 from IPython.utils import path as util_path
102 from IPython.utils import path as util_path
103 from IPython.utils import py3compat
103 from IPython.utils import py3compat
104 from IPython.utils import pyfile
104 from IPython.utils import pyfile
105 from IPython.utils import ulinecache
105 from IPython.utils import ulinecache
106 from IPython.utils.data import uniq_stable
106 from IPython.utils.data import uniq_stable
107 from IPython.utils.openpy import read_py_file
107 from IPython.utils.openpy import read_py_file
108 from IPython.utils.warn import info, error
108 from IPython.utils.warn import info, error
109
109
110 # Globals
110 # Globals
111 # amount of space to put line numbers before verbose tracebacks
111 # amount of space to put line numbers before verbose tracebacks
112 INDENT_SIZE = 8
112 INDENT_SIZE = 8
113
113
114 # Default color scheme. This is used, for example, by the traceback
114 # Default color scheme. This is used, for example, by the traceback
115 # formatter. When running in an actual IPython instance, the user's rc.colors
115 # formatter. When running in an actual IPython instance, the user's rc.colors
116 # value is used, but havinga module global makes this functionality available
116 # value is used, but havinga module global makes this functionality available
117 # to users of ultratb who are NOT running inside ipython.
117 # to users of ultratb who are NOT running inside ipython.
118 DEFAULT_SCHEME = 'NoColor'
118 DEFAULT_SCHEME = 'NoColor'
119
119
120 #---------------------------------------------------------------------------
120 #---------------------------------------------------------------------------
121 # Code begins
121 # Code begins
122
122
123 # Utility functions
123 # Utility functions
124 def inspect_error():
124 def inspect_error():
125 """Print a message about internal inspect errors.
125 """Print a message about internal inspect errors.
126
126
127 These are unfortunately quite common."""
127 These are unfortunately quite common."""
128
128
129 error('Internal Python error in the inspect module.\n'
129 error('Internal Python error in the inspect module.\n'
130 'Below is the traceback from this internal error.\n')
130 'Below is the traceback from this internal error.\n')
131
131
132 # This function is a monkeypatch we apply to the Python inspect module. We have
132 # This function is a monkeypatch we apply to the Python inspect module. We have
133 # now found when it's needed (see discussion on issue gh-1456), and we have a
133 # now found when it's needed (see discussion on issue gh-1456), and we have a
134 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
134 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
135 # the monkeypatch is not applied. TK, Aug 2012.
135 # the monkeypatch is not applied. TK, Aug 2012.
136 def findsource(object):
136 def findsource(object):
137 """Return the entire source file and starting line number for an object.
137 """Return the entire source file and starting line number for an object.
138
138
139 The argument may be a module, class, method, function, traceback, frame,
139 The argument may be a module, class, method, function, traceback, frame,
140 or code object. The source code is returned as a list of all the lines
140 or code object. The source code is returned as a list of all the lines
141 in the file and the line number indexes a line in that list. An IOError
141 in the file and the line number indexes a line in that list. An IOError
142 is raised if the source code cannot be retrieved.
142 is raised if the source code cannot be retrieved.
143
143
144 FIXED version with which we monkeypatch the stdlib to work around a bug."""
144 FIXED version with which we monkeypatch the stdlib to work around a bug."""
145
145
146 file = getsourcefile(object) or getfile(object)
146 file = getsourcefile(object) or getfile(object)
147 # If the object is a frame, then trying to get the globals dict from its
147 # If the object is a frame, then trying to get the globals dict from its
148 # module won't work. Instead, the frame object itself has the globals
148 # module won't work. Instead, the frame object itself has the globals
149 # dictionary.
149 # dictionary.
150 globals_dict = None
150 globals_dict = None
151 if inspect.isframe(object):
151 if inspect.isframe(object):
152 # XXX: can this ever be false?
152 # XXX: can this ever be false?
153 globals_dict = object.f_globals
153 globals_dict = object.f_globals
154 else:
154 else:
155 module = getmodule(object, file)
155 module = getmodule(object, file)
156 if module:
156 if module:
157 globals_dict = module.__dict__
157 globals_dict = module.__dict__
158 lines = linecache.getlines(file, globals_dict)
158 lines = linecache.getlines(file, globals_dict)
159 if not lines:
159 if not lines:
160 raise IOError('could not get source code')
160 raise IOError('could not get source code')
161
161
162 if ismodule(object):
162 if ismodule(object):
163 return lines, 0
163 return lines, 0
164
164
165 if isclass(object):
165 if isclass(object):
166 name = object.__name__
166 name = object.__name__
167 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
167 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
168 # make some effort to find the best matching class definition:
168 # make some effort to find the best matching class definition:
169 # use the one with the least indentation, which is the one
169 # use the one with the least indentation, which is the one
170 # that's most probably not inside a function definition.
170 # that's most probably not inside a function definition.
171 candidates = []
171 candidates = []
172 for i in range(len(lines)):
172 for i in range(len(lines)):
173 match = pat.match(lines[i])
173 match = pat.match(lines[i])
174 if match:
174 if match:
175 # if it's at toplevel, it's already the best one
175 # if it's at toplevel, it's already the best one
176 if lines[i][0] == 'c':
176 if lines[i][0] == 'c':
177 return lines, i
177 return lines, i
178 # else add whitespace to candidate list
178 # else add whitespace to candidate list
179 candidates.append((match.group(1), i))
179 candidates.append((match.group(1), i))
180 if candidates:
180 if candidates:
181 # this will sort by whitespace, and by line number,
181 # this will sort by whitespace, and by line number,
182 # less whitespace first
182 # less whitespace first
183 candidates.sort()
183 candidates.sort()
184 return lines, candidates[0][1]
184 return lines, candidates[0][1]
185 else:
185 else:
186 raise IOError('could not find class definition')
186 raise IOError('could not find class definition')
187
187
188 if ismethod(object):
188 if ismethod(object):
189 object = object.im_func
189 object = object.im_func
190 if isfunction(object):
190 if isfunction(object):
191 object = object.func_code
191 object = object.func_code
192 if istraceback(object):
192 if istraceback(object):
193 object = object.tb_frame
193 object = object.tb_frame
194 if isframe(object):
194 if isframe(object):
195 object = object.f_code
195 object = object.f_code
196 if iscode(object):
196 if iscode(object):
197 if not hasattr(object, 'co_firstlineno'):
197 if not hasattr(object, 'co_firstlineno'):
198 raise IOError('could not find function definition')
198 raise IOError('could not find function definition')
199 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
199 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
200 pmatch = pat.match
200 pmatch = pat.match
201 # fperez - fix: sometimes, co_firstlineno can give a number larger than
201 # fperez - fix: sometimes, co_firstlineno can give a number larger than
202 # the length of lines, which causes an error. Safeguard against that.
202 # the length of lines, which causes an error. Safeguard against that.
203 lnum = min(object.co_firstlineno,len(lines))-1
203 lnum = min(object.co_firstlineno,len(lines))-1
204 while lnum > 0:
204 while lnum > 0:
205 if pmatch(lines[lnum]): break
205 if pmatch(lines[lnum]): break
206 lnum -= 1
206 lnum -= 1
207
207
208 return lines, lnum
208 return lines, lnum
209 raise IOError('could not find code object')
209 raise IOError('could not find code object')
210
210
211 # Monkeypatch inspect to apply our bugfix. This code only works with Python >= 2.5
211 # Monkeypatch inspect to apply our bugfix. This code only works with Python >= 2.5
212 inspect.findsource = findsource
212 inspect.findsource = findsource
213
213
214 def fix_frame_records_filenames(records):
214 def fix_frame_records_filenames(records):
215 """Try to fix the filenames in each record from inspect.getinnerframes().
215 """Try to fix the filenames in each record from inspect.getinnerframes().
216
216
217 Particularly, modules loaded from within zip files have useless filenames
217 Particularly, modules loaded from within zip files have useless filenames
218 attached to their code object, and inspect.getinnerframes() just uses it.
218 attached to their code object, and inspect.getinnerframes() just uses it.
219 """
219 """
220 fixed_records = []
220 fixed_records = []
221 for frame, filename, line_no, func_name, lines, index in records:
221 for frame, filename, line_no, func_name, lines, index in records:
222 # Look inside the frame's globals dictionary for __file__, which should
222 # Look inside the frame's globals dictionary for __file__, which should
223 # be better.
223 # be better.
224 better_fn = frame.f_globals.get('__file__', None)
224 better_fn = frame.f_globals.get('__file__', None)
225 if isinstance(better_fn, str):
225 if isinstance(better_fn, str):
226 # Check the type just in case someone did something weird with
226 # Check the type just in case someone did something weird with
227 # __file__. It might also be None if the error occurred during
227 # __file__. It might also be None if the error occurred during
228 # import.
228 # import.
229 filename = better_fn
229 filename = better_fn
230 fixed_records.append((frame, filename, line_no, func_name, lines, index))
230 fixed_records.append((frame, filename, line_no, func_name, lines, index))
231 return fixed_records
231 return fixed_records
232
232
233
233
234 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
234 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
235 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
235 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
236
236
237 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
237 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
238
238
239 # If the error is at the console, don't build any context, since it would
239 # If the error is at the console, don't build any context, since it would
240 # otherwise produce 5 blank lines printed out (there is no file at the
240 # otherwise produce 5 blank lines printed out (there is no file at the
241 # console)
241 # console)
242 rec_check = records[tb_offset:]
242 rec_check = records[tb_offset:]
243 try:
243 try:
244 rname = rec_check[0][1]
244 rname = rec_check[0][1]
245 if rname == '<ipython console>' or rname.endswith('<string>'):
245 if rname == '<ipython console>' or rname.endswith('<string>'):
246 return rec_check
246 return rec_check
247 except IndexError:
247 except IndexError:
248 pass
248 pass
249
249
250 aux = traceback.extract_tb(etb)
250 aux = traceback.extract_tb(etb)
251 assert len(records) == len(aux)
251 assert len(records) == len(aux)
252 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
252 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
253 maybeStart = lnum-1 - context//2
253 maybeStart = lnum-1 - context//2
254 start = max(maybeStart, 0)
254 start = max(maybeStart, 0)
255 end = start + context
255 end = start + context
256 lines = ulinecache.getlines(file)[start:end]
256 lines = ulinecache.getlines(file)[start:end]
257 buf = list(records[i])
257 buf = list(records[i])
258 buf[LNUM_POS] = lnum
258 buf[LNUM_POS] = lnum
259 buf[INDEX_POS] = lnum - 1 - start
259 buf[INDEX_POS] = lnum - 1 - start
260 buf[LINES_POS] = lines
260 buf[LINES_POS] = lines
261 records[i] = tuple(buf)
261 records[i] = tuple(buf)
262 return records[tb_offset:]
262 return records[tb_offset:]
263
263
264 # Helper function -- largely belongs to VerboseTB, but we need the same
264 # Helper function -- largely belongs to VerboseTB, but we need the same
265 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
265 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
266 # can be recognized properly by ipython.el's py-traceback-line-re
266 # can be recognized properly by ipython.el's py-traceback-line-re
267 # (SyntaxErrors have to be treated specially because they have no traceback)
267 # (SyntaxErrors have to be treated specially because they have no traceback)
268
268
269 _parser = PyColorize.Parser()
269 _parser = PyColorize.Parser()
270
270
271 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
271 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
272 numbers_width = INDENT_SIZE - 1
272 numbers_width = INDENT_SIZE - 1
273 res = []
273 res = []
274 i = lnum - index
274 i = lnum - index
275
275
276 # This lets us get fully syntax-highlighted tracebacks.
276 # This lets us get fully syntax-highlighted tracebacks.
277 if scheme is None:
277 if scheme is None:
278 ipinst = ipapi.get()
278 ipinst = ipapi.get()
279 if ipinst is not None:
279 if ipinst is not None:
280 scheme = ipinst.colors
280 scheme = ipinst.colors
281 else:
281 else:
282 scheme = DEFAULT_SCHEME
282 scheme = DEFAULT_SCHEME
283
283
284 _line_format = _parser.format2
284 _line_format = _parser.format2
285
285
286 for line in lines:
286 for line in lines:
287 line = py3compat.cast_unicode(line)
287 line = py3compat.cast_unicode(line)
288
288
289 new_line, err = _line_format(line, 'str', scheme)
289 new_line, err = _line_format(line, 'str', scheme)
290 if not err: line = new_line
290 if not err: line = new_line
291
291
292 if i == lnum:
292 if i == lnum:
293 # This is the line with the error
293 # This is the line with the error
294 pad = numbers_width - len(str(i))
294 pad = numbers_width - len(str(i))
295 if pad >= 3:
295 if pad >= 3:
296 marker = '-'*(pad-3) + '-> '
296 marker = '-'*(pad-3) + '-> '
297 elif pad == 2:
297 elif pad == 2:
298 marker = '> '
298 marker = '> '
299 elif pad == 1:
299 elif pad == 1:
300 marker = '>'
300 marker = '>'
301 else:
301 else:
302 marker = ''
302 marker = ''
303 num = marker + str(i)
303 num = marker + str(i)
304 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
304 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
305 Colors.line, line, Colors.Normal)
305 Colors.line, line, Colors.Normal)
306 else:
306 else:
307 num = '%*s' % (numbers_width,i)
307 num = '%*s' % (numbers_width,i)
308 line = '%s%s%s %s' %(Colors.lineno, num,
308 line = '%s%s%s %s' %(Colors.lineno, num,
309 Colors.Normal, line)
309 Colors.Normal, line)
310
310
311 res.append(line)
311 res.append(line)
312 if lvals and i == lnum:
312 if lvals and i == lnum:
313 res.append(lvals + '\n')
313 res.append(lvals + '\n')
314 i = i + 1
314 i = i + 1
315 return res
315 return res
316
316
317
317
318 #---------------------------------------------------------------------------
318 #---------------------------------------------------------------------------
319 # Module classes
319 # Module classes
320 class TBTools(object):
320 class TBTools(object):
321 """Basic tools used by all traceback printer classes."""
321 """Basic tools used by all traceback printer classes."""
322
322
323 # Number of frames to skip when reporting tracebacks
323 # Number of frames to skip when reporting tracebacks
324 tb_offset = 0
324 tb_offset = 0
325
325
326 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
326 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
327 # Whether to call the interactive pdb debugger after printing
327 # Whether to call the interactive pdb debugger after printing
328 # tracebacks or not
328 # tracebacks or not
329 self.call_pdb = call_pdb
329 self.call_pdb = call_pdb
330
330
331 # Output stream to write to. Note that we store the original value in
331 # Output stream to write to. Note that we store the original value in
332 # a private attribute and then make the public ostream a property, so
332 # a private attribute and then make the public ostream a property, so
333 # that we can delay accessing io.stdout until runtime. The way
333 # that we can delay accessing io.stdout until runtime. The way
334 # things are written now, the io.stdout object is dynamically managed
334 # things are written now, the io.stdout object is dynamically managed
335 # so a reference to it should NEVER be stored statically. This
335 # so a reference to it should NEVER be stored statically. This
336 # property approach confines this detail to a single location, and all
336 # property approach confines this detail to a single location, and all
337 # subclasses can simply access self.ostream for writing.
337 # subclasses can simply access self.ostream for writing.
338 self._ostream = ostream
338 self._ostream = ostream
339
339
340 # Create color table
340 # Create color table
341 self.color_scheme_table = exception_colors()
341 self.color_scheme_table = exception_colors()
342
342
343 self.set_colors(color_scheme)
343 self.set_colors(color_scheme)
344 self.old_scheme = color_scheme # save initial value for toggles
344 self.old_scheme = color_scheme # save initial value for toggles
345
345
346 if call_pdb:
346 if call_pdb:
347 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
347 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
348 else:
348 else:
349 self.pdb = None
349 self.pdb = None
350
350
351 def _get_ostream(self):
351 def _get_ostream(self):
352 """Output stream that exceptions are written to.
352 """Output stream that exceptions are written to.
353
353
354 Valid values are:
354 Valid values are:
355
355
356 - None: the default, which means that IPython will dynamically resolve
356 - None: the default, which means that IPython will dynamically resolve
357 to io.stdout. This ensures compatibility with most tools, including
357 to io.stdout. This ensures compatibility with most tools, including
358 Windows (where plain stdout doesn't recognize ANSI escapes).
358 Windows (where plain stdout doesn't recognize ANSI escapes).
359
359
360 - Any object with 'write' and 'flush' attributes.
360 - Any object with 'write' and 'flush' attributes.
361 """
361 """
362 return io.stdout if self._ostream is None else self._ostream
362 return io.stdout if self._ostream is None else self._ostream
363
363
364 def _set_ostream(self, val):
364 def _set_ostream(self, val):
365 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
365 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
366 self._ostream = val
366 self._ostream = val
367
367
368 ostream = property(_get_ostream, _set_ostream)
368 ostream = property(_get_ostream, _set_ostream)
369
369
370 def set_colors(self,*args,**kw):
370 def set_colors(self,*args,**kw):
371 """Shorthand access to the color table scheme selector method."""
371 """Shorthand access to the color table scheme selector method."""
372
372
373 # Set own color table
373 # Set own color table
374 self.color_scheme_table.set_active_scheme(*args,**kw)
374 self.color_scheme_table.set_active_scheme(*args,**kw)
375 # for convenience, set Colors to the active scheme
375 # for convenience, set Colors to the active scheme
376 self.Colors = self.color_scheme_table.active_colors
376 self.Colors = self.color_scheme_table.active_colors
377 # Also set colors of debugger
377 # Also set colors of debugger
378 if hasattr(self,'pdb') and self.pdb is not None:
378 if hasattr(self,'pdb') and self.pdb is not None:
379 self.pdb.set_colors(*args,**kw)
379 self.pdb.set_colors(*args,**kw)
380
380
381 def color_toggle(self):
381 def color_toggle(self):
382 """Toggle between the currently active color scheme and NoColor."""
382 """Toggle between the currently active color scheme and NoColor."""
383
383
384 if self.color_scheme_table.active_scheme_name == 'NoColor':
384 if self.color_scheme_table.active_scheme_name == 'NoColor':
385 self.color_scheme_table.set_active_scheme(self.old_scheme)
385 self.color_scheme_table.set_active_scheme(self.old_scheme)
386 self.Colors = self.color_scheme_table.active_colors
386 self.Colors = self.color_scheme_table.active_colors
387 else:
387 else:
388 self.old_scheme = self.color_scheme_table.active_scheme_name
388 self.old_scheme = self.color_scheme_table.active_scheme_name
389 self.color_scheme_table.set_active_scheme('NoColor')
389 self.color_scheme_table.set_active_scheme('NoColor')
390 self.Colors = self.color_scheme_table.active_colors
390 self.Colors = self.color_scheme_table.active_colors
391
391
392 def stb2text(self, stb):
392 def stb2text(self, stb):
393 """Convert a structured traceback (a list) to a string."""
393 """Convert a structured traceback (a list) to a string."""
394 return '\n'.join(stb)
394 return '\n'.join(stb)
395
395
396 def text(self, etype, value, tb, tb_offset=None, context=5):
396 def text(self, etype, value, tb, tb_offset=None, context=5):
397 """Return formatted traceback.
397 """Return formatted traceback.
398
398
399 Subclasses may override this if they add extra arguments.
399 Subclasses may override this if they add extra arguments.
400 """
400 """
401 tb_list = self.structured_traceback(etype, value, tb,
401 tb_list = self.structured_traceback(etype, value, tb,
402 tb_offset, context)
402 tb_offset, context)
403 return self.stb2text(tb_list)
403 return self.stb2text(tb_list)
404
404
405 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
405 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
406 context=5, mode=None):
406 context=5, mode=None):
407 """Return a list of traceback frames.
407 """Return a list of traceback frames.
408
408
409 Must be implemented by each class.
409 Must be implemented by each class.
410 """
410 """
411 raise NotImplementedError()
411 raise NotImplementedError()
412
412
413
413
414 #---------------------------------------------------------------------------
414 #---------------------------------------------------------------------------
415 class ListTB(TBTools):
415 class ListTB(TBTools):
416 """Print traceback information from a traceback list, with optional color.
416 """Print traceback information from a traceback list, with optional color.
417
417
418 Calling: requires 3 arguments:
418 Calling: requires 3 arguments:
419 (etype, evalue, elist)
419 (etype, evalue, elist)
420 as would be obtained by:
420 as would be obtained by:
421 etype, evalue, tb = sys.exc_info()
421 etype, evalue, tb = sys.exc_info()
422 if tb:
422 if tb:
423 elist = traceback.extract_tb(tb)
423 elist = traceback.extract_tb(tb)
424 else:
424 else:
425 elist = None
425 elist = None
426
426
427 It can thus be used by programs which need to process the traceback before
427 It can thus be used by programs which need to process the traceback before
428 printing (such as console replacements based on the code module from the
428 printing (such as console replacements based on the code module from the
429 standard library).
429 standard library).
430
430
431 Because they are meant to be called without a full traceback (only a
431 Because they are meant to be called without a full traceback (only a
432 list), instances of this class can't call the interactive pdb debugger."""
432 list), instances of this class can't call the interactive pdb debugger."""
433
433
434 def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None):
434 def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None):
435 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
435 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
436 ostream=ostream)
436 ostream=ostream)
437
437
438 def __call__(self, etype, value, elist):
438 def __call__(self, etype, value, elist):
439 self.ostream.flush()
439 self.ostream.flush()
440 self.ostream.write(self.text(etype, value, elist))
440 self.ostream.write(self.text(etype, value, elist))
441 self.ostream.write('\n')
441 self.ostream.write('\n')
442
442
443 def structured_traceback(self, etype, value, elist, tb_offset=None,
443 def structured_traceback(self, etype, value, elist, tb_offset=None,
444 context=5):
444 context=5):
445 """Return a color formatted string with the traceback info.
445 """Return a color formatted string with the traceback info.
446
446
447 Parameters
447 Parameters
448 ----------
448 ----------
449 etype : exception type
449 etype : exception type
450 Type of the exception raised.
450 Type of the exception raised.
451
451
452 value : object
452 value : object
453 Data stored in the exception
453 Data stored in the exception
454
454
455 elist : list
455 elist : list
456 List of frames, see class docstring for details.
456 List of frames, see class docstring for details.
457
457
458 tb_offset : int, optional
458 tb_offset : int, optional
459 Number of frames in the traceback to skip. If not given, the
459 Number of frames in the traceback to skip. If not given, the
460 instance value is used (set in constructor).
460 instance value is used (set in constructor).
461
461
462 context : int, optional
462 context : int, optional
463 Number of lines of context information to print.
463 Number of lines of context information to print.
464
464
465 Returns
465 Returns
466 -------
466 -------
467 String with formatted exception.
467 String with formatted exception.
468 """
468 """
469 tb_offset = self.tb_offset if tb_offset is None else tb_offset
469 tb_offset = self.tb_offset if tb_offset is None else tb_offset
470 Colors = self.Colors
470 Colors = self.Colors
471 out_list = []
471 out_list = []
472 if elist:
472 if elist:
473
473
474 if tb_offset and len(elist) > tb_offset:
474 if tb_offset and len(elist) > tb_offset:
475 elist = elist[tb_offset:]
475 elist = elist[tb_offset:]
476
476
477 out_list.append('Traceback %s(most recent call last)%s:' %
477 out_list.append('Traceback %s(most recent call last)%s:' %
478 (Colors.normalEm, Colors.Normal) + '\n')
478 (Colors.normalEm, Colors.Normal) + '\n')
479 out_list.extend(self._format_list(elist))
479 out_list.extend(self._format_list(elist))
480 # The exception info should be a single entry in the list.
480 # The exception info should be a single entry in the list.
481 lines = ''.join(self._format_exception_only(etype, value))
481 lines = ''.join(self._format_exception_only(etype, value))
482 out_list.append(lines)
482 out_list.append(lines)
483
483
484 # Note: this code originally read:
484 # Note: this code originally read:
485
485
486 ## for line in lines[:-1]:
486 ## for line in lines[:-1]:
487 ## out_list.append(" "+line)
487 ## out_list.append(" "+line)
488 ## out_list.append(lines[-1])
488 ## out_list.append(lines[-1])
489
489
490 # This means it was indenting everything but the last line by a little
490 # This means it was indenting everything but the last line by a little
491 # bit. I've disabled this for now, but if we see ugliness somewhre we
491 # bit. I've disabled this for now, but if we see ugliness somewhre we
492 # can restore it.
492 # can restore it.
493
493
494 return out_list
494 return out_list
495
495
496 def _format_list(self, extracted_list):
496 def _format_list(self, extracted_list):
497 """Format a list of traceback entry tuples for printing.
497 """Format a list of traceback entry tuples for printing.
498
498
499 Given a list of tuples as returned by extract_tb() or
499 Given a list of tuples as returned by extract_tb() or
500 extract_stack(), return a list of strings ready for printing.
500 extract_stack(), return a list of strings ready for printing.
501 Each string in the resulting list corresponds to the item with the
501 Each string in the resulting list corresponds to the item with the
502 same index in the argument list. Each string ends in a newline;
502 same index in the argument list. Each string ends in a newline;
503 the strings may contain internal newlines as well, for those items
503 the strings may contain internal newlines as well, for those items
504 whose source text line is not None.
504 whose source text line is not None.
505
505
506 Lifted almost verbatim from traceback.py
506 Lifted almost verbatim from traceback.py
507 """
507 """
508
508
509 Colors = self.Colors
509 Colors = self.Colors
510 list = []
510 list = []
511 for filename, lineno, name, line in extracted_list[:-1]:
511 for filename, lineno, name, line in extracted_list[:-1]:
512 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
512 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
513 (Colors.filename, filename, Colors.Normal,
513 (Colors.filename, filename, Colors.Normal,
514 Colors.lineno, lineno, Colors.Normal,
514 Colors.lineno, lineno, Colors.Normal,
515 Colors.name, name, Colors.Normal)
515 Colors.name, name, Colors.Normal)
516 if line:
516 if line:
517 item += ' %s\n' % line.strip()
517 item += ' %s\n' % line.strip()
518 list.append(item)
518 list.append(item)
519 # Emphasize the last entry
519 # Emphasize the last entry
520 filename, lineno, name, line = extracted_list[-1]
520 filename, lineno, name, line = extracted_list[-1]
521 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
521 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
522 (Colors.normalEm,
522 (Colors.normalEm,
523 Colors.filenameEm, filename, Colors.normalEm,
523 Colors.filenameEm, filename, Colors.normalEm,
524 Colors.linenoEm, lineno, Colors.normalEm,
524 Colors.linenoEm, lineno, Colors.normalEm,
525 Colors.nameEm, name, Colors.normalEm,
525 Colors.nameEm, name, Colors.normalEm,
526 Colors.Normal)
526 Colors.Normal)
527 if line:
527 if line:
528 item += '%s %s%s\n' % (Colors.line, line.strip(),
528 item += '%s %s%s\n' % (Colors.line, line.strip(),
529 Colors.Normal)
529 Colors.Normal)
530 list.append(item)
530 list.append(item)
531 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
531 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
532 return list
532 return list
533
533
534 def _format_exception_only(self, etype, value):
534 def _format_exception_only(self, etype, value):
535 """Format the exception part of a traceback.
535 """Format the exception part of a traceback.
536
536
537 The arguments are the exception type and value such as given by
537 The arguments are the exception type and value such as given by
538 sys.exc_info()[:2]. The return value is a list of strings, each ending
538 sys.exc_info()[:2]. The return value is a list of strings, each ending
539 in a newline. Normally, the list contains a single string; however,
539 in a newline. Normally, the list contains a single string; however,
540 for SyntaxError exceptions, it contains several lines that (when
540 for SyntaxError exceptions, it contains several lines that (when
541 printed) display detailed information about where the syntax error
541 printed) display detailed information about where the syntax error
542 occurred. The message indicating which exception occurred is the
542 occurred. The message indicating which exception occurred is the
543 always last string in the list.
543 always last string in the list.
544
544
545 Also lifted nearly verbatim from traceback.py
545 Also lifted nearly verbatim from traceback.py
546 """
546 """
547 have_filedata = False
547 have_filedata = False
548 Colors = self.Colors
548 Colors = self.Colors
549 list = []
549 list = []
550 stype = Colors.excName + etype.__name__ + Colors.Normal
550 stype = Colors.excName + etype.__name__ + Colors.Normal
551 if value is None:
551 if value is None:
552 # Not sure if this can still happen in Python 2.6 and above
552 # Not sure if this can still happen in Python 2.6 and above
553 list.append( py3compat.cast_unicode(stype) + '\n')
553 list.append( py3compat.cast_unicode(stype) + '\n')
554 else:
554 else:
555 if issubclass(etype, SyntaxError):
555 if issubclass(etype, SyntaxError):
556 have_filedata = True
556 have_filedata = True
557 #print 'filename is',filename # dbg
557 #print 'filename is',filename # dbg
558 if not value.filename: value.filename = "<string>"
558 if not value.filename: value.filename = "<string>"
559 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
559 if not value.lineno: value.lineno = "unknown"
560 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
560 (Colors.normalEm,
561 (Colors.normalEm,
561 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
562 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
562 Colors.linenoEm, value.lineno, Colors.Normal ))
563 Colors.linenoEm, value.lineno, Colors.Normal ))
563 textline = ulinecache.getline(value.filename, value.lineno)
564 textline = ulinecache.getline(value.filename, value.lineno)
564 if textline == '':
565 if textline == '':
565 textline = py3compat.cast_unicode(value.text, "utf-8")
566 textline = py3compat.cast_unicode(value.text, "utf-8")
566
567
567 if textline is not None:
568 if textline is not None:
568 i = 0
569 i = 0
569 while i < len(textline) and textline[i].isspace():
570 while i < len(textline) and textline[i].isspace():
570 i += 1
571 i += 1
571 list.append('%s %s%s\n' % (Colors.line,
572 list.append('%s %s%s\n' % (Colors.line,
572 textline.strip(),
573 textline.strip(),
573 Colors.Normal))
574 Colors.Normal))
574 if value.offset is not None:
575 if value.offset is not None:
575 s = ' '
576 s = ' '
576 for c in textline[i:value.offset-1]:
577 for c in textline[i:value.offset-1]:
577 if c.isspace():
578 if c.isspace():
578 s += c
579 s += c
579 else:
580 else:
580 s += ' '
581 s += ' '
581 list.append('%s%s^%s\n' % (Colors.caret, s,
582 list.append('%s%s^%s\n' % (Colors.caret, s,
582 Colors.Normal) )
583 Colors.Normal) )
583
584
584 try:
585 try:
585 s = value.msg
586 s = value.msg
586 except Exception:
587 except Exception:
587 s = self._some_str(value)
588 s = self._some_str(value)
588 if s:
589 if s:
589 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
590 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
590 Colors.Normal, s))
591 Colors.Normal, s))
591 else:
592 else:
592 list.append('%s\n' % str(stype))
593 list.append('%s\n' % str(stype))
593
594
594 # sync with user hooks
595 # sync with user hooks
595 if have_filedata:
596 if have_filedata:
596 ipinst = ipapi.get()
597 ipinst = ipapi.get()
597 if ipinst is not None:
598 if ipinst is not None:
598 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
599 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
599
600
600 return list
601 return list
601
602
602 def get_exception_only(self, etype, value):
603 def get_exception_only(self, etype, value):
603 """Only print the exception type and message, without a traceback.
604 """Only print the exception type and message, without a traceback.
604
605
605 Parameters
606 Parameters
606 ----------
607 ----------
607 etype : exception type
608 etype : exception type
608 value : exception value
609 value : exception value
609 """
610 """
610 return ListTB.structured_traceback(self, etype, value, [])
611 return ListTB.structured_traceback(self, etype, value, [])
611
612
612
613
613 def show_exception_only(self, etype, evalue):
614 def show_exception_only(self, etype, evalue):
614 """Only print the exception type and message, without a traceback.
615 """Only print the exception type and message, without a traceback.
615
616
616 Parameters
617 Parameters
617 ----------
618 ----------
618 etype : exception type
619 etype : exception type
619 value : exception value
620 value : exception value
620 """
621 """
621 # This method needs to use __call__ from *this* class, not the one from
622 # This method needs to use __call__ from *this* class, not the one from
622 # a subclass whose signature or behavior may be different
623 # a subclass whose signature or behavior may be different
623 ostream = self.ostream
624 ostream = self.ostream
624 ostream.flush()
625 ostream.flush()
625 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
626 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
626 ostream.flush()
627 ostream.flush()
627
628
628 def _some_str(self, value):
629 def _some_str(self, value):
629 # Lifted from traceback.py
630 # Lifted from traceback.py
630 try:
631 try:
631 return str(value)
632 return str(value)
632 except:
633 except:
633 return '<unprintable %s object>' % type(value).__name__
634 return '<unprintable %s object>' % type(value).__name__
634
635
635 #----------------------------------------------------------------------------
636 #----------------------------------------------------------------------------
636 class VerboseTB(TBTools):
637 class VerboseTB(TBTools):
637 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
638 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
638 of HTML. Requires inspect and pydoc. Crazy, man.
639 of HTML. Requires inspect and pydoc. Crazy, man.
639
640
640 Modified version which optionally strips the topmost entries from the
641 Modified version which optionally strips the topmost entries from the
641 traceback, to be used with alternate interpreters (because their own code
642 traceback, to be used with alternate interpreters (because their own code
642 would appear in the traceback)."""
643 would appear in the traceback)."""
643
644
644 def __init__(self,color_scheme = 'Linux', call_pdb=False, ostream=None,
645 def __init__(self,color_scheme = 'Linux', call_pdb=False, ostream=None,
645 tb_offset=0, long_header=False, include_vars=True,
646 tb_offset=0, long_header=False, include_vars=True,
646 check_cache=None):
647 check_cache=None):
647 """Specify traceback offset, headers and color scheme.
648 """Specify traceback offset, headers and color scheme.
648
649
649 Define how many frames to drop from the tracebacks. Calling it with
650 Define how many frames to drop from the tracebacks. Calling it with
650 tb_offset=1 allows use of this handler in interpreters which will have
651 tb_offset=1 allows use of this handler in interpreters which will have
651 their own code at the top of the traceback (VerboseTB will first
652 their own code at the top of the traceback (VerboseTB will first
652 remove that frame before printing the traceback info)."""
653 remove that frame before printing the traceback info)."""
653 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
654 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
654 ostream=ostream)
655 ostream=ostream)
655 self.tb_offset = tb_offset
656 self.tb_offset = tb_offset
656 self.long_header = long_header
657 self.long_header = long_header
657 self.include_vars = include_vars
658 self.include_vars = include_vars
658 # By default we use linecache.checkcache, but the user can provide a
659 # By default we use linecache.checkcache, but the user can provide a
659 # different check_cache implementation. This is used by the IPython
660 # different check_cache implementation. This is used by the IPython
660 # kernel to provide tracebacks for interactive code that is cached,
661 # kernel to provide tracebacks for interactive code that is cached,
661 # by a compiler instance that flushes the linecache but preserves its
662 # by a compiler instance that flushes the linecache but preserves its
662 # own code cache.
663 # own code cache.
663 if check_cache is None:
664 if check_cache is None:
664 check_cache = linecache.checkcache
665 check_cache = linecache.checkcache
665 self.check_cache = check_cache
666 self.check_cache = check_cache
666
667
667 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
668 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
668 context=5):
669 context=5):
669 """Return a nice text document describing the traceback."""
670 """Return a nice text document describing the traceback."""
670
671
671 tb_offset = self.tb_offset if tb_offset is None else tb_offset
672 tb_offset = self.tb_offset if tb_offset is None else tb_offset
672
673
673 # some locals
674 # some locals
674 try:
675 try:
675 etype = etype.__name__
676 etype = etype.__name__
676 except AttributeError:
677 except AttributeError:
677 pass
678 pass
678 Colors = self.Colors # just a shorthand + quicker name lookup
679 Colors = self.Colors # just a shorthand + quicker name lookup
679 ColorsNormal = Colors.Normal # used a lot
680 ColorsNormal = Colors.Normal # used a lot
680 col_scheme = self.color_scheme_table.active_scheme_name
681 col_scheme = self.color_scheme_table.active_scheme_name
681 indent = ' '*INDENT_SIZE
682 indent = ' '*INDENT_SIZE
682 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
683 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
683 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
684 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
684 exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal)
685 exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal)
685
686
686 # some internal-use functions
687 # some internal-use functions
687 def text_repr(value):
688 def text_repr(value):
688 """Hopefully pretty robust repr equivalent."""
689 """Hopefully pretty robust repr equivalent."""
689 # this is pretty horrible but should always return *something*
690 # this is pretty horrible but should always return *something*
690 try:
691 try:
691 return pydoc.text.repr(value)
692 return pydoc.text.repr(value)
692 except KeyboardInterrupt:
693 except KeyboardInterrupt:
693 raise
694 raise
694 except:
695 except:
695 try:
696 try:
696 return repr(value)
697 return repr(value)
697 except KeyboardInterrupt:
698 except KeyboardInterrupt:
698 raise
699 raise
699 except:
700 except:
700 try:
701 try:
701 # all still in an except block so we catch
702 # all still in an except block so we catch
702 # getattr raising
703 # getattr raising
703 name = getattr(value, '__name__', None)
704 name = getattr(value, '__name__', None)
704 if name:
705 if name:
705 # ick, recursion
706 # ick, recursion
706 return text_repr(name)
707 return text_repr(name)
707 klass = getattr(value, '__class__', None)
708 klass = getattr(value, '__class__', None)
708 if klass:
709 if klass:
709 return '%s instance' % text_repr(klass)
710 return '%s instance' % text_repr(klass)
710 except KeyboardInterrupt:
711 except KeyboardInterrupt:
711 raise
712 raise
712 except:
713 except:
713 return 'UNRECOVERABLE REPR FAILURE'
714 return 'UNRECOVERABLE REPR FAILURE'
714 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
715 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
715 def nullrepr(value, repr=text_repr): return ''
716 def nullrepr(value, repr=text_repr): return ''
716
717
717 # meat of the code begins
718 # meat of the code begins
718 try:
719 try:
719 etype = etype.__name__
720 etype = etype.__name__
720 except AttributeError:
721 except AttributeError:
721 pass
722 pass
722
723
723 if self.long_header:
724 if self.long_header:
724 # Header with the exception type, python version, and date
725 # Header with the exception type, python version, and date
725 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
726 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
726 date = time.ctime(time.time())
727 date = time.ctime(time.time())
727
728
728 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
729 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
729 exc, ' '*(75-len(str(etype))-len(pyver)),
730 exc, ' '*(75-len(str(etype))-len(pyver)),
730 pyver, date.rjust(75) )
731 pyver, date.rjust(75) )
731 head += "\nA problem occured executing Python code. Here is the sequence of function"\
732 head += "\nA problem occured executing Python code. Here is the sequence of function"\
732 "\ncalls leading up to the error, with the most recent (innermost) call last."
733 "\ncalls leading up to the error, with the most recent (innermost) call last."
733 else:
734 else:
734 # Simplified header
735 # Simplified header
735 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
736 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
736 'Traceback (most recent call last)'.\
737 'Traceback (most recent call last)'.\
737 rjust(75 - len(str(etype)) ) )
738 rjust(75 - len(str(etype)) ) )
738 frames = []
739 frames = []
739 # Flush cache before calling inspect. This helps alleviate some of the
740 # Flush cache before calling inspect. This helps alleviate some of the
740 # problems with python 2.3's inspect.py.
741 # problems with python 2.3's inspect.py.
741 ##self.check_cache()
742 ##self.check_cache()
742 # Drop topmost frames if requested
743 # Drop topmost frames if requested
743 try:
744 try:
744 # Try the default getinnerframes and Alex's: Alex's fixes some
745 # Try the default getinnerframes and Alex's: Alex's fixes some
745 # problems, but it generates empty tracebacks for console errors
746 # problems, but it generates empty tracebacks for console errors
746 # (5 blanks lines) where none should be returned.
747 # (5 blanks lines) where none should be returned.
747 #records = inspect.getinnerframes(etb, context)[tb_offset:]
748 #records = inspect.getinnerframes(etb, context)[tb_offset:]
748 #print 'python records:', records # dbg
749 #print 'python records:', records # dbg
749 records = _fixed_getinnerframes(etb, context, tb_offset)
750 records = _fixed_getinnerframes(etb, context, tb_offset)
750 #print 'alex records:', records # dbg
751 #print 'alex records:', records # dbg
751 except:
752 except:
752
753
753 # FIXME: I've been getting many crash reports from python 2.3
754 # FIXME: I've been getting many crash reports from python 2.3
754 # users, traceable to inspect.py. If I can find a small test-case
755 # users, traceable to inspect.py. If I can find a small test-case
755 # to reproduce this, I should either write a better workaround or
756 # to reproduce this, I should either write a better workaround or
756 # file a bug report against inspect (if that's the real problem).
757 # file a bug report against inspect (if that's the real problem).
757 # So far, I haven't been able to find an isolated example to
758 # So far, I haven't been able to find an isolated example to
758 # reproduce the problem.
759 # reproduce the problem.
759 inspect_error()
760 inspect_error()
760 traceback.print_exc(file=self.ostream)
761 traceback.print_exc(file=self.ostream)
761 info('\nUnfortunately, your original traceback can not be constructed.\n')
762 info('\nUnfortunately, your original traceback can not be constructed.\n')
762 return ''
763 return ''
763
764
764 # build some color string templates outside these nested loops
765 # build some color string templates outside these nested loops
765 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
766 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
766 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
767 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
767 ColorsNormal)
768 ColorsNormal)
768 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
769 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
769 (Colors.vName, Colors.valEm, ColorsNormal)
770 (Colors.vName, Colors.valEm, ColorsNormal)
770 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
771 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
771 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
772 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
772 Colors.vName, ColorsNormal)
773 Colors.vName, ColorsNormal)
773 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
774 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
774 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
775 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
775 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
776 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
776 ColorsNormal)
777 ColorsNormal)
777
778
778 # now, loop over all records printing context and info
779 # now, loop over all records printing context and info
779 abspath = os.path.abspath
780 abspath = os.path.abspath
780 for frame, file, lnum, func, lines, index in records:
781 for frame, file, lnum, func, lines, index in records:
781 #print '*** record:',file,lnum,func,lines,index # dbg
782 #print '*** record:',file,lnum,func,lines,index # dbg
782 if not file:
783 if not file:
783 file = '?'
784 file = '?'
784 elif not(file.startswith(str("<")) and file.endswith(str(">"))):
785 elif not(file.startswith(str("<")) and file.endswith(str(">"))):
785 # Guess that filenames like <string> aren't real filenames, so
786 # Guess that filenames like <string> aren't real filenames, so
786 # don't call abspath on them.
787 # don't call abspath on them.
787 try:
788 try:
788 file = abspath(file)
789 file = abspath(file)
789 except OSError:
790 except OSError:
790 # Not sure if this can still happen: abspath now works with
791 # Not sure if this can still happen: abspath now works with
791 # file names like <string>
792 # file names like <string>
792 pass
793 pass
793 file = py3compat.cast_unicode(file, util_path.fs_encoding)
794 file = py3compat.cast_unicode(file, util_path.fs_encoding)
794 link = tpl_link % file
795 link = tpl_link % file
795 args, varargs, varkw, locals = inspect.getargvalues(frame)
796 args, varargs, varkw, locals = inspect.getargvalues(frame)
796
797
797 if func == '?':
798 if func == '?':
798 call = ''
799 call = ''
799 else:
800 else:
800 # Decide whether to include variable details or not
801 # Decide whether to include variable details or not
801 var_repr = self.include_vars and eqrepr or nullrepr
802 var_repr = self.include_vars and eqrepr or nullrepr
802 try:
803 try:
803 call = tpl_call % (func,inspect.formatargvalues(args,
804 call = tpl_call % (func,inspect.formatargvalues(args,
804 varargs, varkw,
805 varargs, varkw,
805 locals,formatvalue=var_repr))
806 locals,formatvalue=var_repr))
806 except KeyError:
807 except KeyError:
807 # This happens in situations like errors inside generator
808 # This happens in situations like errors inside generator
808 # expressions, where local variables are listed in the
809 # expressions, where local variables are listed in the
809 # line, but can't be extracted from the frame. I'm not
810 # line, but can't be extracted from the frame. I'm not
810 # 100% sure this isn't actually a bug in inspect itself,
811 # 100% sure this isn't actually a bug in inspect itself,
811 # but since there's no info for us to compute with, the
812 # but since there's no info for us to compute with, the
812 # best we can do is report the failure and move on. Here
813 # best we can do is report the failure and move on. Here
813 # we must *not* call any traceback construction again,
814 # we must *not* call any traceback construction again,
814 # because that would mess up use of %debug later on. So we
815 # because that would mess up use of %debug later on. So we
815 # simply report the failure and move on. The only
816 # simply report the failure and move on. The only
816 # limitation will be that this frame won't have locals
817 # limitation will be that this frame won't have locals
817 # listed in the call signature. Quite subtle problem...
818 # listed in the call signature. Quite subtle problem...
818 # I can't think of a good way to validate this in a unit
819 # I can't think of a good way to validate this in a unit
819 # test, but running a script consisting of:
820 # test, but running a script consisting of:
820 # dict( (k,v.strip()) for (k,v) in range(10) )
821 # dict( (k,v.strip()) for (k,v) in range(10) )
821 # will illustrate the error, if this exception catch is
822 # will illustrate the error, if this exception catch is
822 # disabled.
823 # disabled.
823 call = tpl_call_fail % func
824 call = tpl_call_fail % func
824
825
825 # Don't attempt to tokenize binary files.
826 # Don't attempt to tokenize binary files.
826 if file.endswith(('.so', '.pyd', '.dll')):
827 if file.endswith(('.so', '.pyd', '.dll')):
827 frames.append('%s %s\n' % (link,call))
828 frames.append('%s %s\n' % (link,call))
828 continue
829 continue
829 elif file.endswith(('.pyc','.pyo')):
830 elif file.endswith(('.pyc','.pyo')):
830 # Look up the corresponding source file.
831 # Look up the corresponding source file.
831 file = pyfile.source_from_cache(file)
832 file = pyfile.source_from_cache(file)
832
833
833 def linereader(file=file, lnum=[lnum], getline=ulinecache.getline):
834 def linereader(file=file, lnum=[lnum], getline=ulinecache.getline):
834 line = getline(file, lnum[0])
835 line = getline(file, lnum[0])
835 lnum[0] += 1
836 lnum[0] += 1
836 return line
837 return line
837
838
838 # Build the list of names on this line of code where the exception
839 # Build the list of names on this line of code where the exception
839 # occurred.
840 # occurred.
840 try:
841 try:
841 names = []
842 names = []
842 name_cont = False
843 name_cont = False
843
844
844 for token_type, token, start, end, line in generate_tokens(linereader):
845 for token_type, token, start, end, line in generate_tokens(linereader):
845 # build composite names
846 # build composite names
846 if token_type == tokenize.NAME and token not in keyword.kwlist:
847 if token_type == tokenize.NAME and token not in keyword.kwlist:
847 if name_cont:
848 if name_cont:
848 # Continuation of a dotted name
849 # Continuation of a dotted name
849 try:
850 try:
850 names[-1].append(token)
851 names[-1].append(token)
851 except IndexError:
852 except IndexError:
852 names.append([token])
853 names.append([token])
853 name_cont = False
854 name_cont = False
854 else:
855 else:
855 # Regular new names. We append everything, the caller
856 # Regular new names. We append everything, the caller
856 # will be responsible for pruning the list later. It's
857 # will be responsible for pruning the list later. It's
857 # very tricky to try to prune as we go, b/c composite
858 # very tricky to try to prune as we go, b/c composite
858 # names can fool us. The pruning at the end is easy
859 # names can fool us. The pruning at the end is easy
859 # to do (or the caller can print a list with repeated
860 # to do (or the caller can print a list with repeated
860 # names if so desired.
861 # names if so desired.
861 names.append([token])
862 names.append([token])
862 elif token == '.':
863 elif token == '.':
863 name_cont = True
864 name_cont = True
864 elif token_type == tokenize.NEWLINE:
865 elif token_type == tokenize.NEWLINE:
865 break
866 break
866
867
867 except (IndexError, UnicodeDecodeError):
868 except (IndexError, UnicodeDecodeError):
868 # signals exit of tokenizer
869 # signals exit of tokenizer
869 pass
870 pass
870 except tokenize.TokenError as msg:
871 except tokenize.TokenError as msg:
871 _m = ("An unexpected error occurred while tokenizing input\n"
872 _m = ("An unexpected error occurred while tokenizing input\n"
872 "The following traceback may be corrupted or invalid\n"
873 "The following traceback may be corrupted or invalid\n"
873 "The error message is: %s\n" % msg)
874 "The error message is: %s\n" % msg)
874 error(_m)
875 error(_m)
875
876
876 # Join composite names (e.g. "dict.fromkeys")
877 # Join composite names (e.g. "dict.fromkeys")
877 names = ['.'.join(n) for n in names]
878 names = ['.'.join(n) for n in names]
878 # prune names list of duplicates, but keep the right order
879 # prune names list of duplicates, but keep the right order
879 unique_names = uniq_stable(names)
880 unique_names = uniq_stable(names)
880
881
881 # Start loop over vars
882 # Start loop over vars
882 lvals = []
883 lvals = []
883 if self.include_vars:
884 if self.include_vars:
884 for name_full in unique_names:
885 for name_full in unique_names:
885 name_base = name_full.split('.',1)[0]
886 name_base = name_full.split('.',1)[0]
886 if name_base in frame.f_code.co_varnames:
887 if name_base in frame.f_code.co_varnames:
887 if name_base in locals:
888 if name_base in locals:
888 try:
889 try:
889 value = repr(eval(name_full,locals))
890 value = repr(eval(name_full,locals))
890 except:
891 except:
891 value = undefined
892 value = undefined
892 else:
893 else:
893 value = undefined
894 value = undefined
894 name = tpl_local_var % name_full
895 name = tpl_local_var % name_full
895 else:
896 else:
896 if name_base in frame.f_globals:
897 if name_base in frame.f_globals:
897 try:
898 try:
898 value = repr(eval(name_full,frame.f_globals))
899 value = repr(eval(name_full,frame.f_globals))
899 except:
900 except:
900 value = undefined
901 value = undefined
901 else:
902 else:
902 value = undefined
903 value = undefined
903 name = tpl_global_var % name_full
904 name = tpl_global_var % name_full
904 lvals.append(tpl_name_val % (name,value))
905 lvals.append(tpl_name_val % (name,value))
905 if lvals:
906 if lvals:
906 lvals = '%s%s' % (indent,em_normal.join(lvals))
907 lvals = '%s%s' % (indent,em_normal.join(lvals))
907 else:
908 else:
908 lvals = ''
909 lvals = ''
909
910
910 level = '%s %s\n' % (link,call)
911 level = '%s %s\n' % (link,call)
911
912
912 if index is None:
913 if index is None:
913 frames.append(level)
914 frames.append(level)
914 else:
915 else:
915 frames.append('%s%s' % (level,''.join(
916 frames.append('%s%s' % (level,''.join(
916 _format_traceback_lines(lnum,index,lines,Colors,lvals,
917 _format_traceback_lines(lnum,index,lines,Colors,lvals,
917 col_scheme))))
918 col_scheme))))
918
919
919 # Get (safely) a string form of the exception info
920 # Get (safely) a string form of the exception info
920 try:
921 try:
921 etype_str,evalue_str = map(str,(etype,evalue))
922 etype_str,evalue_str = map(str,(etype,evalue))
922 except:
923 except:
923 # User exception is improperly defined.
924 # User exception is improperly defined.
924 etype,evalue = str,sys.exc_info()[:2]
925 etype,evalue = str,sys.exc_info()[:2]
925 etype_str,evalue_str = map(str,(etype,evalue))
926 etype_str,evalue_str = map(str,(etype,evalue))
926 # ... and format it
927 # ... and format it
927 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
928 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
928 ColorsNormal, py3compat.cast_unicode(evalue_str))]
929 ColorsNormal, py3compat.cast_unicode(evalue_str))]
929 if (not py3compat.PY3) and type(evalue) is types.InstanceType:
930 if (not py3compat.PY3) and type(evalue) is types.InstanceType:
930 try:
931 try:
931 names = [w for w in dir(evalue) if isinstance(w, basestring)]
932 names = [w for w in dir(evalue) if isinstance(w, basestring)]
932 except:
933 except:
933 # Every now and then, an object with funny inernals blows up
934 # Every now and then, an object with funny inernals blows up
934 # when dir() is called on it. We do the best we can to report
935 # when dir() is called on it. We do the best we can to report
935 # the problem and continue
936 # the problem and continue
936 _m = '%sException reporting error (object with broken dir())%s:'
937 _m = '%sException reporting error (object with broken dir())%s:'
937 exception.append(_m % (Colors.excName,ColorsNormal))
938 exception.append(_m % (Colors.excName,ColorsNormal))
938 etype_str,evalue_str = map(str,sys.exc_info()[:2])
939 etype_str,evalue_str = map(str,sys.exc_info()[:2])
939 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
940 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
940 ColorsNormal, py3compat.cast_unicode(evalue_str)))
941 ColorsNormal, py3compat.cast_unicode(evalue_str)))
941 names = []
942 names = []
942 for name in names:
943 for name in names:
943 value = text_repr(getattr(evalue, name))
944 value = text_repr(getattr(evalue, name))
944 exception.append('\n%s%s = %s' % (indent, name, value))
945 exception.append('\n%s%s = %s' % (indent, name, value))
945
946
946 # vds: >>
947 # vds: >>
947 if records:
948 if records:
948 filepath, lnum = records[-1][1:3]
949 filepath, lnum = records[-1][1:3]
949 #print "file:", str(file), "linenb", str(lnum) # dbg
950 #print "file:", str(file), "linenb", str(lnum) # dbg
950 filepath = os.path.abspath(filepath)
951 filepath = os.path.abspath(filepath)
951 ipinst = ipapi.get()
952 ipinst = ipapi.get()
952 if ipinst is not None:
953 if ipinst is not None:
953 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
954 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
954 # vds: <<
955 # vds: <<
955
956
956 # return all our info assembled as a single string
957 # return all our info assembled as a single string
957 # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
958 # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
958 return [head] + frames + [''.join(exception[0])]
959 return [head] + frames + [''.join(exception[0])]
959
960
960 def debugger(self,force=False):
961 def debugger(self,force=False):
961 """Call up the pdb debugger if desired, always clean up the tb
962 """Call up the pdb debugger if desired, always clean up the tb
962 reference.
963 reference.
963
964
964 Keywords:
965 Keywords:
965
966
966 - force(False): by default, this routine checks the instance call_pdb
967 - force(False): by default, this routine checks the instance call_pdb
967 flag and does not actually invoke the debugger if the flag is false.
968 flag and does not actually invoke the debugger if the flag is false.
968 The 'force' option forces the debugger to activate even if the flag
969 The 'force' option forces the debugger to activate even if the flag
969 is false.
970 is false.
970
971
971 If the call_pdb flag is set, the pdb interactive debugger is
972 If the call_pdb flag is set, the pdb interactive debugger is
972 invoked. In all cases, the self.tb reference to the current traceback
973 invoked. In all cases, the self.tb reference to the current traceback
973 is deleted to prevent lingering references which hamper memory
974 is deleted to prevent lingering references which hamper memory
974 management.
975 management.
975
976
976 Note that each call to pdb() does an 'import readline', so if your app
977 Note that each call to pdb() does an 'import readline', so if your app
977 requires a special setup for the readline completers, you'll have to
978 requires a special setup for the readline completers, you'll have to
978 fix that by hand after invoking the exception handler."""
979 fix that by hand after invoking the exception handler."""
979
980
980 if force or self.call_pdb:
981 if force or self.call_pdb:
981 if self.pdb is None:
982 if self.pdb is None:
982 self.pdb = debugger.Pdb(
983 self.pdb = debugger.Pdb(
983 self.color_scheme_table.active_scheme_name)
984 self.color_scheme_table.active_scheme_name)
984 # the system displayhook may have changed, restore the original
985 # the system displayhook may have changed, restore the original
985 # for pdb
986 # for pdb
986 display_trap = DisplayTrap(hook=sys.__displayhook__)
987 display_trap = DisplayTrap(hook=sys.__displayhook__)
987 with display_trap:
988 with display_trap:
988 self.pdb.reset()
989 self.pdb.reset()
989 # Find the right frame so we don't pop up inside ipython itself
990 # Find the right frame so we don't pop up inside ipython itself
990 if hasattr(self,'tb') and self.tb is not None:
991 if hasattr(self,'tb') and self.tb is not None:
991 etb = self.tb
992 etb = self.tb
992 else:
993 else:
993 etb = self.tb = sys.last_traceback
994 etb = self.tb = sys.last_traceback
994 while self.tb is not None and self.tb.tb_next is not None:
995 while self.tb is not None and self.tb.tb_next is not None:
995 self.tb = self.tb.tb_next
996 self.tb = self.tb.tb_next
996 if etb and etb.tb_next:
997 if etb and etb.tb_next:
997 etb = etb.tb_next
998 etb = etb.tb_next
998 self.pdb.botframe = etb.tb_frame
999 self.pdb.botframe = etb.tb_frame
999 self.pdb.interaction(self.tb.tb_frame, self.tb)
1000 self.pdb.interaction(self.tb.tb_frame, self.tb)
1000
1001
1001 if hasattr(self,'tb'):
1002 if hasattr(self,'tb'):
1002 del self.tb
1003 del self.tb
1003
1004
1004 def handler(self, info=None):
1005 def handler(self, info=None):
1005 (etype, evalue, etb) = info or sys.exc_info()
1006 (etype, evalue, etb) = info or sys.exc_info()
1006 self.tb = etb
1007 self.tb = etb
1007 ostream = self.ostream
1008 ostream = self.ostream
1008 ostream.flush()
1009 ostream.flush()
1009 ostream.write(self.text(etype, evalue, etb))
1010 ostream.write(self.text(etype, evalue, etb))
1010 ostream.write('\n')
1011 ostream.write('\n')
1011 ostream.flush()
1012 ostream.flush()
1012
1013
1013 # Changed so an instance can just be called as VerboseTB_inst() and print
1014 # Changed so an instance can just be called as VerboseTB_inst() and print
1014 # out the right info on its own.
1015 # out the right info on its own.
1015 def __call__(self, etype=None, evalue=None, etb=None):
1016 def __call__(self, etype=None, evalue=None, etb=None):
1016 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1017 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1017 if etb is None:
1018 if etb is None:
1018 self.handler()
1019 self.handler()
1019 else:
1020 else:
1020 self.handler((etype, evalue, etb))
1021 self.handler((etype, evalue, etb))
1021 try:
1022 try:
1022 self.debugger()
1023 self.debugger()
1023 except KeyboardInterrupt:
1024 except KeyboardInterrupt:
1024 print "\nKeyboardInterrupt"
1025 print "\nKeyboardInterrupt"
1025
1026
1026 #----------------------------------------------------------------------------
1027 #----------------------------------------------------------------------------
1027 class FormattedTB(VerboseTB, ListTB):
1028 class FormattedTB(VerboseTB, ListTB):
1028 """Subclass ListTB but allow calling with a traceback.
1029 """Subclass ListTB but allow calling with a traceback.
1029
1030
1030 It can thus be used as a sys.excepthook for Python > 2.1.
1031 It can thus be used as a sys.excepthook for Python > 2.1.
1031
1032
1032 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1033 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1033
1034
1034 Allows a tb_offset to be specified. This is useful for situations where
1035 Allows a tb_offset to be specified. This is useful for situations where
1035 one needs to remove a number of topmost frames from the traceback (such as
1036 one needs to remove a number of topmost frames from the traceback (such as
1036 occurs with python programs that themselves execute other python code,
1037 occurs with python programs that themselves execute other python code,
1037 like Python shells). """
1038 like Python shells). """
1038
1039
1039 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1040 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1040 ostream=None,
1041 ostream=None,
1041 tb_offset=0, long_header=False, include_vars=False,
1042 tb_offset=0, long_header=False, include_vars=False,
1042 check_cache=None):
1043 check_cache=None):
1043
1044
1044 # NEVER change the order of this list. Put new modes at the end:
1045 # NEVER change the order of this list. Put new modes at the end:
1045 self.valid_modes = ['Plain','Context','Verbose']
1046 self.valid_modes = ['Plain','Context','Verbose']
1046 self.verbose_modes = self.valid_modes[1:3]
1047 self.verbose_modes = self.valid_modes[1:3]
1047
1048
1048 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1049 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1049 ostream=ostream, tb_offset=tb_offset,
1050 ostream=ostream, tb_offset=tb_offset,
1050 long_header=long_header, include_vars=include_vars,
1051 long_header=long_header, include_vars=include_vars,
1051 check_cache=check_cache)
1052 check_cache=check_cache)
1052
1053
1053 # Different types of tracebacks are joined with different separators to
1054 # Different types of tracebacks are joined with different separators to
1054 # form a single string. They are taken from this dict
1055 # form a single string. They are taken from this dict
1055 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1056 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1056 # set_mode also sets the tb_join_char attribute
1057 # set_mode also sets the tb_join_char attribute
1057 self.set_mode(mode)
1058 self.set_mode(mode)
1058
1059
1059 def _extract_tb(self,tb):
1060 def _extract_tb(self,tb):
1060 if tb:
1061 if tb:
1061 return traceback.extract_tb(tb)
1062 return traceback.extract_tb(tb)
1062 else:
1063 else:
1063 return None
1064 return None
1064
1065
1065 def structured_traceback(self, etype, value, tb, tb_offset=None, context=5):
1066 def structured_traceback(self, etype, value, tb, tb_offset=None, context=5):
1066 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1067 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1067 mode = self.mode
1068 mode = self.mode
1068 if mode in self.verbose_modes:
1069 if mode in self.verbose_modes:
1069 # Verbose modes need a full traceback
1070 # Verbose modes need a full traceback
1070 return VerboseTB.structured_traceback(
1071 return VerboseTB.structured_traceback(
1071 self, etype, value, tb, tb_offset, context
1072 self, etype, value, tb, tb_offset, context
1072 )
1073 )
1073 else:
1074 else:
1074 # We must check the source cache because otherwise we can print
1075 # We must check the source cache because otherwise we can print
1075 # out-of-date source code.
1076 # out-of-date source code.
1076 self.check_cache()
1077 self.check_cache()
1077 # Now we can extract and format the exception
1078 # Now we can extract and format the exception
1078 elist = self._extract_tb(tb)
1079 elist = self._extract_tb(tb)
1079 return ListTB.structured_traceback(
1080 return ListTB.structured_traceback(
1080 self, etype, value, elist, tb_offset, context
1081 self, etype, value, elist, tb_offset, context
1081 )
1082 )
1082
1083
1083 def stb2text(self, stb):
1084 def stb2text(self, stb):
1084 """Convert a structured traceback (a list) to a string."""
1085 """Convert a structured traceback (a list) to a string."""
1085 return self.tb_join_char.join(stb)
1086 return self.tb_join_char.join(stb)
1086
1087
1087
1088
1088 def set_mode(self,mode=None):
1089 def set_mode(self,mode=None):
1089 """Switch to the desired mode.
1090 """Switch to the desired mode.
1090
1091
1091 If mode is not specified, cycles through the available modes."""
1092 If mode is not specified, cycles through the available modes."""
1092
1093
1093 if not mode:
1094 if not mode:
1094 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
1095 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
1095 len(self.valid_modes)
1096 len(self.valid_modes)
1096 self.mode = self.valid_modes[new_idx]
1097 self.mode = self.valid_modes[new_idx]
1097 elif mode not in self.valid_modes:
1098 elif mode not in self.valid_modes:
1098 raise ValueError('Unrecognized mode in FormattedTB: <'+mode+'>\n'
1099 raise ValueError('Unrecognized mode in FormattedTB: <'+mode+'>\n'
1099 'Valid modes: '+str(self.valid_modes))
1100 'Valid modes: '+str(self.valid_modes))
1100 else:
1101 else:
1101 self.mode = mode
1102 self.mode = mode
1102 # include variable details only in 'Verbose' mode
1103 # include variable details only in 'Verbose' mode
1103 self.include_vars = (self.mode == self.valid_modes[2])
1104 self.include_vars = (self.mode == self.valid_modes[2])
1104 # Set the join character for generating text tracebacks
1105 # Set the join character for generating text tracebacks
1105 self.tb_join_char = self._join_chars[self.mode]
1106 self.tb_join_char = self._join_chars[self.mode]
1106
1107
1107 # some convenient shorcuts
1108 # some convenient shorcuts
1108 def plain(self):
1109 def plain(self):
1109 self.set_mode(self.valid_modes[0])
1110 self.set_mode(self.valid_modes[0])
1110
1111
1111 def context(self):
1112 def context(self):
1112 self.set_mode(self.valid_modes[1])
1113 self.set_mode(self.valid_modes[1])
1113
1114
1114 def verbose(self):
1115 def verbose(self):
1115 self.set_mode(self.valid_modes[2])
1116 self.set_mode(self.valid_modes[2])
1116
1117
1117 #----------------------------------------------------------------------------
1118 #----------------------------------------------------------------------------
1118 class AutoFormattedTB(FormattedTB):
1119 class AutoFormattedTB(FormattedTB):
1119 """A traceback printer which can be called on the fly.
1120 """A traceback printer which can be called on the fly.
1120
1121
1121 It will find out about exceptions by itself.
1122 It will find out about exceptions by itself.
1122
1123
1123 A brief example:
1124 A brief example:
1124
1125
1125 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1126 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1126 try:
1127 try:
1127 ...
1128 ...
1128 except:
1129 except:
1129 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1130 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1130 """
1131 """
1131
1132
1132 def __call__(self,etype=None,evalue=None,etb=None,
1133 def __call__(self,etype=None,evalue=None,etb=None,
1133 out=None,tb_offset=None):
1134 out=None,tb_offset=None):
1134 """Print out a formatted exception traceback.
1135 """Print out a formatted exception traceback.
1135
1136
1136 Optional arguments:
1137 Optional arguments:
1137 - out: an open file-like object to direct output to.
1138 - out: an open file-like object to direct output to.
1138
1139
1139 - tb_offset: the number of frames to skip over in the stack, on a
1140 - tb_offset: the number of frames to skip over in the stack, on a
1140 per-call basis (this overrides temporarily the instance's tb_offset
1141 per-call basis (this overrides temporarily the instance's tb_offset
1141 given at initialization time. """
1142 given at initialization time. """
1142
1143
1143
1144
1144 if out is None:
1145 if out is None:
1145 out = self.ostream
1146 out = self.ostream
1146 out.flush()
1147 out.flush()
1147 out.write(self.text(etype, evalue, etb, tb_offset))
1148 out.write(self.text(etype, evalue, etb, tb_offset))
1148 out.write('\n')
1149 out.write('\n')
1149 out.flush()
1150 out.flush()
1150 # FIXME: we should remove the auto pdb behavior from here and leave
1151 # FIXME: we should remove the auto pdb behavior from here and leave
1151 # that to the clients.
1152 # that to the clients.
1152 try:
1153 try:
1153 self.debugger()
1154 self.debugger()
1154 except KeyboardInterrupt:
1155 except KeyboardInterrupt:
1155 print "\nKeyboardInterrupt"
1156 print "\nKeyboardInterrupt"
1156
1157
1157 def structured_traceback(self, etype=None, value=None, tb=None,
1158 def structured_traceback(self, etype=None, value=None, tb=None,
1158 tb_offset=None, context=5):
1159 tb_offset=None, context=5):
1159 if etype is None:
1160 if etype is None:
1160 etype,value,tb = sys.exc_info()
1161 etype,value,tb = sys.exc_info()
1161 self.tb = tb
1162 self.tb = tb
1162 return FormattedTB.structured_traceback(
1163 return FormattedTB.structured_traceback(
1163 self, etype, value, tb, tb_offset, context)
1164 self, etype, value, tb, tb_offset, context)
1164
1165
1165 #---------------------------------------------------------------------------
1166 #---------------------------------------------------------------------------
1166
1167
1167 # A simple class to preserve Nathan's original functionality.
1168 # A simple class to preserve Nathan's original functionality.
1168 class ColorTB(FormattedTB):
1169 class ColorTB(FormattedTB):
1169 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1170 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1170 def __init__(self,color_scheme='Linux',call_pdb=0):
1171 def __init__(self,color_scheme='Linux',call_pdb=0):
1171 FormattedTB.__init__(self,color_scheme=color_scheme,
1172 FormattedTB.__init__(self,color_scheme=color_scheme,
1172 call_pdb=call_pdb)
1173 call_pdb=call_pdb)
1173
1174
1174
1175
1175 class SyntaxTB(ListTB):
1176 class SyntaxTB(ListTB):
1176 """Extension which holds some state: the last exception value"""
1177 """Extension which holds some state: the last exception value"""
1177
1178
1178 def __init__(self,color_scheme = 'NoColor'):
1179 def __init__(self,color_scheme = 'NoColor'):
1179 ListTB.__init__(self,color_scheme)
1180 ListTB.__init__(self,color_scheme)
1180 self.last_syntax_error = None
1181 self.last_syntax_error = None
1181
1182
1182 def __call__(self, etype, value, elist):
1183 def __call__(self, etype, value, elist):
1183 self.last_syntax_error = value
1184 self.last_syntax_error = value
1184 ListTB.__call__(self,etype,value,elist)
1185 ListTB.__call__(self,etype,value,elist)
1185
1186
1186 def clear_err_state(self):
1187 def clear_err_state(self):
1187 """Return the current error state and clear it"""
1188 """Return the current error state and clear it"""
1188 e = self.last_syntax_error
1189 e = self.last_syntax_error
1189 self.last_syntax_error = None
1190 self.last_syntax_error = None
1190 return e
1191 return e
1191
1192
1192 def stb2text(self, stb):
1193 def stb2text(self, stb):
1193 """Convert a structured traceback (a list) to a string."""
1194 """Convert a structured traceback (a list) to a string."""
1194 return ''.join(stb)
1195 return ''.join(stb)
1195
1196
1196
1197
1197 #----------------------------------------------------------------------------
1198 #----------------------------------------------------------------------------
1198 # module testing (minimal)
1199 # module testing (minimal)
1199 if __name__ == "__main__":
1200 if __name__ == "__main__":
1200 def spam(c, d_e):
1201 def spam(c, d_e):
1201 (d, e) = d_e
1202 (d, e) = d_e
1202 x = c + d
1203 x = c + d
1203 y = c * d
1204 y = c * d
1204 foo(x, y)
1205 foo(x, y)
1205
1206
1206 def foo(a, b, bar=1):
1207 def foo(a, b, bar=1):
1207 eggs(a, b + bar)
1208 eggs(a, b + bar)
1208
1209
1209 def eggs(f, g, z=globals()):
1210 def eggs(f, g, z=globals()):
1210 h = f + g
1211 h = f + g
1211 i = f - g
1212 i = f - g
1212 return h / i
1213 return h / i
1213
1214
1214 print ''
1215 print ''
1215 print '*** Before ***'
1216 print '*** Before ***'
1216 try:
1217 try:
1217 print spam(1, (2, 3))
1218 print spam(1, (2, 3))
1218 except:
1219 except:
1219 traceback.print_exc()
1220 traceback.print_exc()
1220 print ''
1221 print ''
1221
1222
1222 handler = ColorTB()
1223 handler = ColorTB()
1223 print '*** ColorTB ***'
1224 print '*** ColorTB ***'
1224 try:
1225 try:
1225 print spam(1, (2, 3))
1226 print spam(1, (2, 3))
1226 except:
1227 except:
1227 handler(*sys.exc_info())
1228 handler(*sys.exc_info())
1228 print ''
1229 print ''
1229
1230
1230 handler = VerboseTB()
1231 handler = VerboseTB()
1231 print '*** VerboseTB ***'
1232 print '*** VerboseTB ***'
1232 try:
1233 try:
1233 print spam(1, (2, 3))
1234 print spam(1, (2, 3))
1234 except:
1235 except:
1235 handler(*sys.exc_info())
1236 handler(*sys.exc_info())
1236 print ''
1237 print ''
1237
1238
General Comments 0
You need to be logged in to leave comments. Login now