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