##// END OF EJS Templates
Start refactoring handling of color....
Matthias Bussonnier -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,636 +1,637 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 the command line completion of other programs which include this isn't
6 the command line completion of other programs which include this isn't
7 damaged.
7 damaged.
8
8
9 In the future, this class will be expanded with improvements over the standard
9 In the future, this class will be expanded with improvements over the standard
10 pdb.
10 pdb.
11
11
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 changes. Licensing should therefore be under the standard Python terms. For
13 changes. Licensing should therefore be under the standard Python terms. For
14 details on the PSF (Python Software Foundation) standard license, see:
14 details on the PSF (Python Software Foundation) standard license, see:
15
15
16 http://www.python.org/2.2.3/license.html"""
16 http://www.python.org/2.2.3/license.html"""
17
17
18 #*****************************************************************************
18 #*****************************************************************************
19 #
19 #
20 # This file is licensed under the PSF license.
20 # This file is licensed under the PSF license.
21 #
21 #
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 #
24 #
25 #
25 #
26 #*****************************************************************************
26 #*****************************************************************************
27 from __future__ import print_function
27 from __future__ import print_function
28
28
29 import bdb
29 import bdb
30 import functools
30 import functools
31 import inspect
31 import inspect
32 import sys
32 import sys
33 import warnings
33 import warnings
34
34
35 from IPython import get_ipython
35 from IPython import get_ipython
36 from IPython.utils import PyColorize, ulinecache
36 from IPython.utils import PyColorize, ulinecache
37 from IPython.utils import coloransi, py3compat
37 from IPython.utils import coloransi, py3compat
38 from IPython.core.excolors import exception_colors
38 from IPython.core.excolors import exception_colors
39 from IPython.testing.skipdoctest import skip_doctest
39 from IPython.testing.skipdoctest import skip_doctest
40
40
41
41
42 prompt = 'ipdb> '
42 prompt = 'ipdb> '
43
43
44 #We have to check this directly from sys.argv, config struct not yet available
44 #We have to check this directly from sys.argv, config struct not yet available
45 from pdb import Pdb as OldPdb
45 from pdb import Pdb as OldPdb
46
46
47 # Allow the set_trace code to operate outside of an ipython instance, even if
47 # Allow the set_trace code to operate outside of an ipython instance, even if
48 # it does so with some limitations. The rest of this support is implemented in
48 # it does so with some limitations. The rest of this support is implemented in
49 # the Tracer constructor.
49 # the Tracer constructor.
50
50
51 def make_arrow(pad):
51 def make_arrow(pad):
52 """generate the leading arrow in front of traceback or debugger"""
52 """generate the leading arrow in front of traceback or debugger"""
53 if pad >= 2:
53 if pad >= 2:
54 return '-'*(pad-2) + '> '
54 return '-'*(pad-2) + '> '
55 elif pad == 1:
55 elif pad == 1:
56 return '>'
56 return '>'
57 return ''
57 return ''
58
58
59
59
60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
61 """Exception hook which handles `BdbQuit` exceptions.
61 """Exception hook which handles `BdbQuit` exceptions.
62
62
63 All other exceptions are processed using the `excepthook`
63 All other exceptions are processed using the `excepthook`
64 parameter.
64 parameter.
65 """
65 """
66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
67 DeprecationWarning)
67 DeprecationWarning)
68 if et==bdb.BdbQuit:
68 if et==bdb.BdbQuit:
69 print('Exiting Debugger.')
69 print('Exiting Debugger.')
70 elif excepthook is not None:
70 elif excepthook is not None:
71 excepthook(et, ev, tb)
71 excepthook(et, ev, tb)
72 else:
72 else:
73 # Backwards compatibility. Raise deprecation warning?
73 # Backwards compatibility. Raise deprecation warning?
74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
75
75
76
76
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
78 warnings.warn(
78 warnings.warn(
79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
80 DeprecationWarning)
80 DeprecationWarning)
81 print('Exiting Debugger.')
81 print('Exiting Debugger.')
82
82
83
83
84 class Tracer(object):
84 class Tracer(object):
85 """
85 """
86 DEPRECATED
86 DEPRECATED
87
87
88 Class for local debugging, similar to pdb.set_trace.
88 Class for local debugging, similar to pdb.set_trace.
89
89
90 Instances of this class, when called, behave like pdb.set_trace, but
90 Instances of this class, when called, behave like pdb.set_trace, but
91 providing IPython's enhanced capabilities.
91 providing IPython's enhanced capabilities.
92
92
93 This is implemented as a class which must be initialized in your own code
93 This is implemented as a class which must be initialized in your own code
94 and not as a standalone function because we need to detect at runtime
94 and not as a standalone function because we need to detect at runtime
95 whether IPython is already active or not. That detection is done in the
95 whether IPython is already active or not. That detection is done in the
96 constructor, ensuring that this code plays nicely with a running IPython,
96 constructor, ensuring that this code plays nicely with a running IPython,
97 while functioning acceptably (though with limitations) if outside of it.
97 while functioning acceptably (though with limitations) if outside of it.
98 """
98 """
99
99
100 @skip_doctest
100 @skip_doctest
101 def __init__(self, colors=None):
101 def __init__(self, colors=None):
102 """
102 """
103 DEPRECATED
103 DEPRECATED
104
104
105 Create a local debugger instance.
105 Create a local debugger instance.
106
106
107 Parameters
107 Parameters
108 ----------
108 ----------
109
109
110 colors : str, optional
110 colors : str, optional
111 The name of the color scheme to use, it must be one of IPython's
111 The name of the color scheme to use, it must be one of IPython's
112 valid color schemes. If not given, the function will default to
112 valid color schemes. If not given, the function will default to
113 the current IPython scheme when running inside IPython, and to
113 the current IPython scheme when running inside IPython, and to
114 'NoColor' otherwise.
114 'NoColor' otherwise.
115
115
116 Examples
116 Examples
117 --------
117 --------
118 ::
118 ::
119
119
120 from IPython.core.debugger import Tracer; debug_here = Tracer()
120 from IPython.core.debugger import Tracer; debug_here = Tracer()
121
121
122 Later in your code::
122 Later in your code::
123
123
124 debug_here() # -> will open up the debugger at that point.
124 debug_here() # -> will open up the debugger at that point.
125
125
126 Once the debugger activates, you can use all of its regular commands to
126 Once the debugger activates, you can use all of its regular commands to
127 step through code, set breakpoints, etc. See the pdb documentation
127 step through code, set breakpoints, etc. See the pdb documentation
128 from the Python standard library for usage details.
128 from the Python standard library for usage details.
129 """
129 """
130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
131 "`IPython.core.debugger.Pdb.set_trace()`",
131 "`IPython.core.debugger.Pdb.set_trace()`",
132 DeprecationWarning)
132 DeprecationWarning)
133
133
134 ip = get_ipython()
134 ip = get_ipython()
135 if ip is None:
135 if ip is None:
136 # Outside of ipython, we set our own exception hook manually
136 # Outside of ipython, we set our own exception hook manually
137 sys.excepthook = functools.partial(BdbQuit_excepthook,
137 sys.excepthook = functools.partial(BdbQuit_excepthook,
138 excepthook=sys.excepthook)
138 excepthook=sys.excepthook)
139 def_colors = 'NoColor'
139 def_colors = 'NoColor'
140 else:
140 else:
141 # In ipython, we use its custom exception handler mechanism
141 # In ipython, we use its custom exception handler mechanism
142 def_colors = ip.colors
142 def_colors = ip.colors
143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
144
144
145 if colors is None:
145 if colors is None:
146 colors = def_colors
146 colors = def_colors
147
147
148 # The stdlib debugger internally uses a modified repr from the `repr`
148 # The stdlib debugger internally uses a modified repr from the `repr`
149 # module, that limits the length of printed strings to a hardcoded
149 # module, that limits the length of printed strings to a hardcoded
150 # limit of 30 characters. That much trimming is too aggressive, let's
150 # limit of 30 characters. That much trimming is too aggressive, let's
151 # at least raise that limit to 80 chars, which should be enough for
151 # at least raise that limit to 80 chars, which should be enough for
152 # most interactive uses.
152 # most interactive uses.
153 try:
153 try:
154 try:
154 try:
155 from reprlib import aRepr # Py 3
155 from reprlib import aRepr # Py 3
156 except ImportError:
156 except ImportError:
157 from repr import aRepr # Py 2
157 from repr import aRepr # Py 2
158 aRepr.maxstring = 80
158 aRepr.maxstring = 80
159 except:
159 except:
160 # This is only a user-facing convenience, so any error we encounter
160 # This is only a user-facing convenience, so any error we encounter
161 # here can be warned about but can be otherwise ignored. These
161 # here can be warned about but can be otherwise ignored. These
162 # printouts will tell us about problems if this API changes
162 # printouts will tell us about problems if this API changes
163 import traceback
163 import traceback
164 traceback.print_exc()
164 traceback.print_exc()
165
165
166 self.debugger = Pdb(colors)
166 self.debugger = Pdb(colors)
167
167
168 def __call__(self):
168 def __call__(self):
169 """Starts an interactive debugger at the point where called.
169 """Starts an interactive debugger at the point where called.
170
170
171 This is similar to the pdb.set_trace() function from the std lib, but
171 This is similar to the pdb.set_trace() function from the std lib, but
172 using IPython's enhanced debugger."""
172 using IPython's enhanced debugger."""
173
173
174 self.debugger.set_trace(sys._getframe().f_back)
174 self.debugger.set_trace(sys._getframe().f_back)
175
175
176
176
177 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
177 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
178 """Make new_fn have old_fn's doc string. This is particularly useful
178 """Make new_fn have old_fn's doc string. This is particularly useful
179 for the ``do_...`` commands that hook into the help system.
179 for the ``do_...`` commands that hook into the help system.
180 Adapted from from a comp.lang.python posting
180 Adapted from from a comp.lang.python posting
181 by Duncan Booth."""
181 by Duncan Booth."""
182 def wrapper(*args, **kw):
182 def wrapper(*args, **kw):
183 return new_fn(*args, **kw)
183 return new_fn(*args, **kw)
184 if old_fn.__doc__:
184 if old_fn.__doc__:
185 wrapper.__doc__ = old_fn.__doc__ + additional_text
185 wrapper.__doc__ = old_fn.__doc__ + additional_text
186 return wrapper
186 return wrapper
187
187
188
188
189 def _file_lines(fname):
189 def _file_lines(fname):
190 """Return the contents of a named file as a list of lines.
190 """Return the contents of a named file as a list of lines.
191
191
192 This function never raises an IOError exception: if the file can't be
192 This function never raises an IOError exception: if the file can't be
193 read, it simply returns an empty list."""
193 read, it simply returns an empty list."""
194
194
195 try:
195 try:
196 outfile = open(fname)
196 outfile = open(fname)
197 except IOError:
197 except IOError:
198 return []
198 return []
199 else:
199 else:
200 out = outfile.readlines()
200 out = outfile.readlines()
201 outfile.close()
201 outfile.close()
202 return out
202 return out
203
203
204
204
205 class Pdb(OldPdb, object):
205 class Pdb(OldPdb, object):
206 """Modified Pdb class, does not load readline.
206 """Modified Pdb class, does not load readline.
207
207
208 for a standalone version that uses prompt_toolkit, see
208 for a standalone version that uses prompt_toolkit, see
209 `IPython.terminal.debugger.TerminalPdb` and
209 `IPython.terminal.debugger.TerminalPdb` and
210 `IPython.terminal.debugger.set_trace()`
210 `IPython.terminal.debugger.set_trace()`
211 """
211 """
212
212
213 def __init__(self, color_scheme=None, completekey=None,
213 def __init__(self, color_scheme=None, completekey=None,
214 stdin=None, stdout=None, context=5):
214 stdin=None, stdout=None, context=5):
215
215
216 # Parent constructor:
216 # Parent constructor:
217 try:
217 try:
218 self.context = int(context)
218 self.context = int(context)
219 if self.context <= 0:
219 if self.context <= 0:
220 raise ValueError("Context must be a positive integer")
220 raise ValueError("Context must be a positive integer")
221 except (TypeError, ValueError):
221 except (TypeError, ValueError):
222 raise ValueError("Context must be a positive integer")
222 raise ValueError("Context must be a positive integer")
223
223
224 OldPdb.__init__(self, completekey, stdin, stdout)
224 OldPdb.__init__(self, completekey, stdin, stdout)
225
225
226 # IPython changes...
226 # IPython changes...
227 self.shell = get_ipython()
227 self.shell = get_ipython()
228
228
229 if self.shell is None:
229 if self.shell is None:
230 save_main = sys.modules['__main__']
230 save_main = sys.modules['__main__']
231 # No IPython instance running, we must create one
231 # No IPython instance running, we must create one
232 from IPython.terminal.interactiveshell import \
232 from IPython.terminal.interactiveshell import \
233 TerminalInteractiveShell
233 TerminalInteractiveShell
234 self.shell = TerminalInteractiveShell.instance()
234 self.shell = TerminalInteractiveShell.instance()
235 # needed by any code which calls __import__("__main__") after
235 # needed by any code which calls __import__("__main__") after
236 # the debugger was entered. See also #9941.
236 # the debugger was entered. See also #9941.
237 sys.modules['__main__'] = save_main
237 sys.modules['__main__'] = save_main
238
238
239 if color_scheme is not None:
239 if color_scheme is not None:
240 warnings.warn(
240 warnings.warn(
241 "The `color_scheme` argument is deprecated since version 5.1",
241 "The `color_scheme` argument is deprecated since version 5.1",
242 DeprecationWarning)
242 DeprecationWarning, stacklevel=2)
243 else:
243 else:
244 color_scheme = self.shell.colors
244 color_scheme = self.shell.colors
245
245
246 self.aliases = {}
246 self.aliases = {}
247
247
248 # Create color table: we copy the default one from the traceback
248 # Create color table: we copy the default one from the traceback
249 # module and add a few attributes needed for debugging
249 # module and add a few attributes needed for debugging
250 self.color_scheme_table = exception_colors()
250 self.color_scheme_table = exception_colors()
251
251
252 # shorthands
252 # shorthands
253 C = coloransi.TermColors
253 C = coloransi.TermColors
254 cst = self.color_scheme_table
254 cst = self.color_scheme_table
255
255
256 cst['NoColor'].colors.prompt = C.NoColor
256 cst['NoColor'].colors.prompt = C.NoColor
257 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
257 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
258 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
258 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
259
259
260 cst['Linux'].colors.prompt = C.Green
260 cst['Linux'].colors.prompt = C.Green
261 cst['Linux'].colors.breakpoint_enabled = C.LightRed
261 cst['Linux'].colors.breakpoint_enabled = C.LightRed
262 cst['Linux'].colors.breakpoint_disabled = C.Red
262 cst['Linux'].colors.breakpoint_disabled = C.Red
263
263
264 cst['LightBG'].colors.prompt = C.Blue
264 cst['LightBG'].colors.prompt = C.Blue
265 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
265 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
266 cst['LightBG'].colors.breakpoint_disabled = C.Red
266 cst['LightBG'].colors.breakpoint_disabled = C.Red
267
267
268 cst['Neutral'].colors.prompt = C.Blue
268 cst['Neutral'].colors.prompt = C.Blue
269 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
269 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
270 cst['Neutral'].colors.breakpoint_disabled = C.Red
270 cst['Neutral'].colors.breakpoint_disabled = C.Red
271
271
272 self.set_colors(color_scheme)
273
272
274 # Add a python parser so we can syntax highlight source while
273 # Add a python parser so we can syntax highlight source while
275 # debugging.
274 # debugging.
276 self.parser = PyColorize.Parser()
275 self.parser = PyColorize.Parser(style=color_scheme)
276 self.set_colors(color_scheme)
277
277
278 # Set the prompt - the default prompt is '(Pdb)'
278 # Set the prompt - the default prompt is '(Pdb)'
279 self.prompt = prompt
279 self.prompt = prompt
280
280
281 def set_colors(self, scheme):
281 def set_colors(self, scheme):
282 """Shorthand access to the color table scheme selector method."""
282 """Shorthand access to the color table scheme selector method."""
283 self.color_scheme_table.set_active_scheme(scheme)
283 self.color_scheme_table.set_active_scheme(scheme)
284 self.parser.style = scheme
284
285
285 def trace_dispatch(self, frame, event, arg):
286 def trace_dispatch(self, frame, event, arg):
286 try:
287 try:
287 return super(Pdb, self).trace_dispatch(frame, event, arg)
288 return super(Pdb, self).trace_dispatch(frame, event, arg)
288 except bdb.BdbQuit:
289 except bdb.BdbQuit:
289 pass
290 pass
290
291
291 def interaction(self, frame, traceback):
292 def interaction(self, frame, traceback):
292 try:
293 try:
293 OldPdb.interaction(self, frame, traceback)
294 OldPdb.interaction(self, frame, traceback)
294 except KeyboardInterrupt:
295 except KeyboardInterrupt:
295 sys.stdout.write('\n' + self.shell.get_exception_only())
296 sys.stdout.write('\n' + self.shell.get_exception_only())
296
297
297 def parseline(self, line):
298 def parseline(self, line):
298 if line.startswith("!!"):
299 if line.startswith("!!"):
299 # Force standard behavior.
300 # Force standard behavior.
300 return super(Pdb, self).parseline(line[2:])
301 return super(Pdb, self).parseline(line[2:])
301 # "Smart command mode" from pdb++: don't execute commands if a variable
302 # "Smart command mode" from pdb++: don't execute commands if a variable
302 # with the same name exists.
303 # with the same name exists.
303 cmd, arg, newline = super(Pdb, self).parseline(line)
304 cmd, arg, newline = super(Pdb, self).parseline(line)
304 # Fix for #9611: Do not trigger smart command if the command is `exit`
305 # Fix for #9611: Do not trigger smart command if the command is `exit`
305 # or `quit` and it would resolve to their *global* value (the
306 # or `quit` and it would resolve to their *global* value (the
306 # `ExitAutocall` object). Just checking that it is not present in the
307 # `ExitAutocall` object). Just checking that it is not present in the
307 # locals dict is not enough as locals and globals match at the
308 # locals dict is not enough as locals and globals match at the
308 # toplevel.
309 # toplevel.
309 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
310 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
310 and not (cmd in ["exit", "quit"]
311 and not (cmd in ["exit", "quit"]
311 and (self.curframe.f_locals is self.curframe.f_globals
312 and (self.curframe.f_locals is self.curframe.f_globals
312 or cmd not in self.curframe.f_locals))):
313 or cmd not in self.curframe.f_locals))):
313 return super(Pdb, self).parseline("!" + line)
314 return super(Pdb, self).parseline("!" + line)
314 return super(Pdb, self).parseline(line)
315 return super(Pdb, self).parseline(line)
315
316
316 def new_do_up(self, arg):
317 def new_do_up(self, arg):
317 OldPdb.do_up(self, arg)
318 OldPdb.do_up(self, arg)
318 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
319 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
319
320
320 def new_do_down(self, arg):
321 def new_do_down(self, arg):
321 OldPdb.do_down(self, arg)
322 OldPdb.do_down(self, arg)
322
323
323 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
324 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
324
325
325 def new_do_frame(self, arg):
326 def new_do_frame(self, arg):
326 OldPdb.do_frame(self, arg)
327 OldPdb.do_frame(self, arg)
327
328
328 def new_do_quit(self, arg):
329 def new_do_quit(self, arg):
329
330
330 if hasattr(self, 'old_all_completions'):
331 if hasattr(self, 'old_all_completions'):
331 self.shell.Completer.all_completions=self.old_all_completions
332 self.shell.Completer.all_completions=self.old_all_completions
332
333
333 return OldPdb.do_quit(self, arg)
334 return OldPdb.do_quit(self, arg)
334
335
335 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
336 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
336
337
337 def new_do_restart(self, arg):
338 def new_do_restart(self, arg):
338 """Restart command. In the context of ipython this is exactly the same
339 """Restart command. In the context of ipython this is exactly the same
339 thing as 'quit'."""
340 thing as 'quit'."""
340 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
341 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
341 return self.do_quit(arg)
342 return self.do_quit(arg)
342
343
343 def print_stack_trace(self, context=None):
344 def print_stack_trace(self, context=None):
344 if context is None:
345 if context is None:
345 context = self.context
346 context = self.context
346 try:
347 try:
347 context=int(context)
348 context=int(context)
348 if context <= 0:
349 if context <= 0:
349 raise ValueError("Context must be a positive integer")
350 raise ValueError("Context must be a positive integer")
350 except (TypeError, ValueError):
351 except (TypeError, ValueError):
351 raise ValueError("Context must be a positive integer")
352 raise ValueError("Context must be a positive integer")
352 try:
353 try:
353 for frame_lineno in self.stack:
354 for frame_lineno in self.stack:
354 self.print_stack_entry(frame_lineno, context=context)
355 self.print_stack_entry(frame_lineno, context=context)
355 except KeyboardInterrupt:
356 except KeyboardInterrupt:
356 pass
357 pass
357
358
358 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
359 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
359 context=None):
360 context=None):
360 if context is None:
361 if context is None:
361 context = self.context
362 context = self.context
362 try:
363 try:
363 context=int(context)
364 context=int(context)
364 if context <= 0:
365 if context <= 0:
365 raise ValueError("Context must be a positive integer")
366 raise ValueError("Context must be a positive integer")
366 except (TypeError, ValueError):
367 except (TypeError, ValueError):
367 raise ValueError("Context must be a positive integer")
368 raise ValueError("Context must be a positive integer")
368 print(self.format_stack_entry(frame_lineno, '', context))
369 print(self.format_stack_entry(frame_lineno, '', context))
369
370
370 # vds: >>
371 # vds: >>
371 frame, lineno = frame_lineno
372 frame, lineno = frame_lineno
372 filename = frame.f_code.co_filename
373 filename = frame.f_code.co_filename
373 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
374 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
374 # vds: <<
375 # vds: <<
375
376
376 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
377 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
377 if context is None:
378 if context is None:
378 context = self.context
379 context = self.context
379 try:
380 try:
380 context=int(context)
381 context=int(context)
381 if context <= 0:
382 if context <= 0:
382 print("Context must be a positive integer")
383 print("Context must be a positive integer")
383 except (TypeError, ValueError):
384 except (TypeError, ValueError):
384 print("Context must be a positive integer")
385 print("Context must be a positive integer")
385 try:
386 try:
386 import reprlib # Py 3
387 import reprlib # Py 3
387 except ImportError:
388 except ImportError:
388 import repr as reprlib # Py 2
389 import repr as reprlib # Py 2
389
390
390 ret = []
391 ret = []
391
392
392 Colors = self.color_scheme_table.active_colors
393 Colors = self.color_scheme_table.active_colors
393 ColorsNormal = Colors.Normal
394 ColorsNormal = Colors.Normal
394 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
395 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
395 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
396 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
396 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
397 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
397 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
398 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
398 ColorsNormal)
399 ColorsNormal)
399
400
400 frame, lineno = frame_lineno
401 frame, lineno = frame_lineno
401
402
402 return_value = ''
403 return_value = ''
403 if '__return__' in frame.f_locals:
404 if '__return__' in frame.f_locals:
404 rv = frame.f_locals['__return__']
405 rv = frame.f_locals['__return__']
405 #return_value += '->'
406 #return_value += '->'
406 return_value += reprlib.repr(rv) + '\n'
407 return_value += reprlib.repr(rv) + '\n'
407 ret.append(return_value)
408 ret.append(return_value)
408
409
409 #s = filename + '(' + `lineno` + ')'
410 #s = filename + '(' + `lineno` + ')'
410 filename = self.canonic(frame.f_code.co_filename)
411 filename = self.canonic(frame.f_code.co_filename)
411 link = tpl_link % py3compat.cast_unicode(filename)
412 link = tpl_link % py3compat.cast_unicode(filename)
412
413
413 if frame.f_code.co_name:
414 if frame.f_code.co_name:
414 func = frame.f_code.co_name
415 func = frame.f_code.co_name
415 else:
416 else:
416 func = "<lambda>"
417 func = "<lambda>"
417
418
418 call = ''
419 call = ''
419 if func != '?':
420 if func != '?':
420 if '__args__' in frame.f_locals:
421 if '__args__' in frame.f_locals:
421 args = reprlib.repr(frame.f_locals['__args__'])
422 args = reprlib.repr(frame.f_locals['__args__'])
422 else:
423 else:
423 args = '()'
424 args = '()'
424 call = tpl_call % (func, args)
425 call = tpl_call % (func, args)
425
426
426 # The level info should be generated in the same format pdb uses, to
427 # The level info should be generated in the same format pdb uses, to
427 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
428 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
428 if frame is self.curframe:
429 if frame is self.curframe:
429 ret.append('> ')
430 ret.append('> ')
430 else:
431 else:
431 ret.append(' ')
432 ret.append(' ')
432 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
433 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
433
434
434 start = lineno - 1 - context//2
435 start = lineno - 1 - context//2
435 lines = ulinecache.getlines(filename)
436 lines = ulinecache.getlines(filename)
436 start = min(start, len(lines) - context)
437 start = min(start, len(lines) - context)
437 start = max(start, 0)
438 start = max(start, 0)
438 lines = lines[start : start + context]
439 lines = lines[start : start + context]
439
440
440 for i,line in enumerate(lines):
441 for i,line in enumerate(lines):
441 show_arrow = (start + 1 + i == lineno)
442 show_arrow = (start + 1 + i == lineno)
442 linetpl = (frame is self.curframe or show_arrow) \
443 linetpl = (frame is self.curframe or show_arrow) \
443 and tpl_line_em \
444 and tpl_line_em \
444 or tpl_line
445 or tpl_line
445 ret.append(self.__format_line(linetpl, filename,
446 ret.append(self.__format_line(linetpl, filename,
446 start + 1 + i, line,
447 start + 1 + i, line,
447 arrow = show_arrow) )
448 arrow = show_arrow) )
448 return ''.join(ret)
449 return ''.join(ret)
449
450
450 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
451 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
451 bp_mark = ""
452 bp_mark = ""
452 bp_mark_color = ""
453 bp_mark_color = ""
453
454
454 scheme = self.color_scheme_table.active_scheme_name
455 new_line, err = self.parser.format2(line, 'str')
455 new_line, err = self.parser.format2(line, 'str', scheme)
456 if not err:
456 if not err: line = new_line
457 line = new_line
457
458
458 bp = None
459 bp = None
459 if lineno in self.get_file_breaks(filename):
460 if lineno in self.get_file_breaks(filename):
460 bps = self.get_breaks(filename, lineno)
461 bps = self.get_breaks(filename, lineno)
461 bp = bps[-1]
462 bp = bps[-1]
462
463
463 if bp:
464 if bp:
464 Colors = self.color_scheme_table.active_colors
465 Colors = self.color_scheme_table.active_colors
465 bp_mark = str(bp.number)
466 bp_mark = str(bp.number)
466 bp_mark_color = Colors.breakpoint_enabled
467 bp_mark_color = Colors.breakpoint_enabled
467 if not bp.enabled:
468 if not bp.enabled:
468 bp_mark_color = Colors.breakpoint_disabled
469 bp_mark_color = Colors.breakpoint_disabled
469
470
470 numbers_width = 7
471 numbers_width = 7
471 if arrow:
472 if arrow:
472 # This is the line with the error
473 # This is the line with the error
473 pad = numbers_width - len(str(lineno)) - len(bp_mark)
474 pad = numbers_width - len(str(lineno)) - len(bp_mark)
474 num = '%s%s' % (make_arrow(pad), str(lineno))
475 num = '%s%s' % (make_arrow(pad), str(lineno))
475 else:
476 else:
476 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
477 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
477
478
478 return tpl_line % (bp_mark_color + bp_mark, num, line)
479 return tpl_line % (bp_mark_color + bp_mark, num, line)
479
480
480
481
481 def print_list_lines(self, filename, first, last):
482 def print_list_lines(self, filename, first, last):
482 """The printing (as opposed to the parsing part of a 'list'
483 """The printing (as opposed to the parsing part of a 'list'
483 command."""
484 command."""
484 try:
485 try:
485 Colors = self.color_scheme_table.active_colors
486 Colors = self.color_scheme_table.active_colors
486 ColorsNormal = Colors.Normal
487 ColorsNormal = Colors.Normal
487 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
488 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
488 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
489 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
489 src = []
490 src = []
490 if filename == "<string>" and hasattr(self, "_exec_filename"):
491 if filename == "<string>" and hasattr(self, "_exec_filename"):
491 filename = self._exec_filename
492 filename = self._exec_filename
492
493
493 for lineno in range(first, last+1):
494 for lineno in range(first, last+1):
494 line = ulinecache.getline(filename, lineno)
495 line = ulinecache.getline(filename, lineno)
495 if not line:
496 if not line:
496 break
497 break
497
498
498 if lineno == self.curframe.f_lineno:
499 if lineno == self.curframe.f_lineno:
499 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
500 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
500 else:
501 else:
501 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
502 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
502
503
503 src.append(line)
504 src.append(line)
504 self.lineno = lineno
505 self.lineno = lineno
505
506
506 print(''.join(src))
507 print(''.join(src))
507
508
508 except KeyboardInterrupt:
509 except KeyboardInterrupt:
509 pass
510 pass
510
511
511 def do_list(self, arg):
512 def do_list(self, arg):
512 self.lastcmd = 'list'
513 self.lastcmd = 'list'
513 last = None
514 last = None
514 if arg:
515 if arg:
515 try:
516 try:
516 x = eval(arg, {}, {})
517 x = eval(arg, {}, {})
517 if type(x) == type(()):
518 if type(x) == type(()):
518 first, last = x
519 first, last = x
519 first = int(first)
520 first = int(first)
520 last = int(last)
521 last = int(last)
521 if last < first:
522 if last < first:
522 # Assume it's a count
523 # Assume it's a count
523 last = first + last
524 last = first + last
524 else:
525 else:
525 first = max(1, int(x) - 5)
526 first = max(1, int(x) - 5)
526 except:
527 except:
527 print('*** Error in argument:', repr(arg))
528 print('*** Error in argument:', repr(arg))
528 return
529 return
529 elif self.lineno is None:
530 elif self.lineno is None:
530 first = max(1, self.curframe.f_lineno - 5)
531 first = max(1, self.curframe.f_lineno - 5)
531 else:
532 else:
532 first = self.lineno + 1
533 first = self.lineno + 1
533 if last is None:
534 if last is None:
534 last = first + 10
535 last = first + 10
535 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
536 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
536
537
537 # vds: >>
538 # vds: >>
538 lineno = first
539 lineno = first
539 filename = self.curframe.f_code.co_filename
540 filename = self.curframe.f_code.co_filename
540 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
541 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
541 # vds: <<
542 # vds: <<
542
543
543 do_l = do_list
544 do_l = do_list
544
545
545 def getsourcelines(self, obj):
546 def getsourcelines(self, obj):
546 lines, lineno = inspect.findsource(obj)
547 lines, lineno = inspect.findsource(obj)
547 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
548 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
548 # must be a module frame: do not try to cut a block out of it
549 # must be a module frame: do not try to cut a block out of it
549 return lines, 1
550 return lines, 1
550 elif inspect.ismodule(obj):
551 elif inspect.ismodule(obj):
551 return lines, 1
552 return lines, 1
552 return inspect.getblock(lines[lineno:]), lineno+1
553 return inspect.getblock(lines[lineno:]), lineno+1
553
554
554 def do_longlist(self, arg):
555 def do_longlist(self, arg):
555 self.lastcmd = 'longlist'
556 self.lastcmd = 'longlist'
556 try:
557 try:
557 lines, lineno = self.getsourcelines(self.curframe)
558 lines, lineno = self.getsourcelines(self.curframe)
558 except OSError as err:
559 except OSError as err:
559 self.error(err)
560 self.error(err)
560 return
561 return
561 last = lineno + len(lines)
562 last = lineno + len(lines)
562 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
563 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
563 do_ll = do_longlist
564 do_ll = do_longlist
564
565
565 def do_pdef(self, arg):
566 def do_pdef(self, arg):
566 """Print the call signature for any callable object.
567 """Print the call signature for any callable object.
567
568
568 The debugger interface to %pdef"""
569 The debugger interface to %pdef"""
569 namespaces = [('Locals', self.curframe.f_locals),
570 namespaces = [('Locals', self.curframe.f_locals),
570 ('Globals', self.curframe.f_globals)]
571 ('Globals', self.curframe.f_globals)]
571 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
572 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
572
573
573 def do_pdoc(self, arg):
574 def do_pdoc(self, arg):
574 """Print the docstring for an object.
575 """Print the docstring for an object.
575
576
576 The debugger interface to %pdoc."""
577 The debugger interface to %pdoc."""
577 namespaces = [('Locals', self.curframe.f_locals),
578 namespaces = [('Locals', self.curframe.f_locals),
578 ('Globals', self.curframe.f_globals)]
579 ('Globals', self.curframe.f_globals)]
579 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
580 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
580
581
581 def do_pfile(self, arg):
582 def do_pfile(self, arg):
582 """Print (or run through pager) the file where an object is defined.
583 """Print (or run through pager) the file where an object is defined.
583
584
584 The debugger interface to %pfile.
585 The debugger interface to %pfile.
585 """
586 """
586 namespaces = [('Locals', self.curframe.f_locals),
587 namespaces = [('Locals', self.curframe.f_locals),
587 ('Globals', self.curframe.f_globals)]
588 ('Globals', self.curframe.f_globals)]
588 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
589 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
589
590
590 def do_pinfo(self, arg):
591 def do_pinfo(self, arg):
591 """Provide detailed information about an object.
592 """Provide detailed information about an object.
592
593
593 The debugger interface to %pinfo, i.e., obj?."""
594 The debugger interface to %pinfo, i.e., obj?."""
594 namespaces = [('Locals', self.curframe.f_locals),
595 namespaces = [('Locals', self.curframe.f_locals),
595 ('Globals', self.curframe.f_globals)]
596 ('Globals', self.curframe.f_globals)]
596 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
597 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
597
598
598 def do_pinfo2(self, arg):
599 def do_pinfo2(self, arg):
599 """Provide extra detailed information about an object.
600 """Provide extra detailed information about an object.
600
601
601 The debugger interface to %pinfo2, i.e., obj??."""
602 The debugger interface to %pinfo2, i.e., obj??."""
602 namespaces = [('Locals', self.curframe.f_locals),
603 namespaces = [('Locals', self.curframe.f_locals),
603 ('Globals', self.curframe.f_globals)]
604 ('Globals', self.curframe.f_globals)]
604 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
605 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
605
606
606 def do_psource(self, arg):
607 def do_psource(self, arg):
607 """Print (or run through pager) the source code for an object."""
608 """Print (or run through pager) the source code for an object."""
608 namespaces = [('Locals', self.curframe.f_locals),
609 namespaces = [('Locals', self.curframe.f_locals),
609 ('Globals', self.curframe.f_globals)]
610 ('Globals', self.curframe.f_globals)]
610 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
611 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
611
612
612 if sys.version_info > (3, ):
613 if sys.version_info > (3, ):
613 def do_where(self, arg):
614 def do_where(self, arg):
614 """w(here)
615 """w(here)
615 Print a stack trace, with the most recent frame at the bottom.
616 Print a stack trace, with the most recent frame at the bottom.
616 An arrow indicates the "current frame", which determines the
617 An arrow indicates the "current frame", which determines the
617 context of most commands. 'bt' is an alias for this command.
618 context of most commands. 'bt' is an alias for this command.
618
619
619 Take a number as argument as an (optional) number of context line to
620 Take a number as argument as an (optional) number of context line to
620 print"""
621 print"""
621 if arg:
622 if arg:
622 context = int(arg)
623 context = int(arg)
623 self.print_stack_trace(context)
624 self.print_stack_trace(context)
624 else:
625 else:
625 self.print_stack_trace()
626 self.print_stack_trace()
626
627
627 do_w = do_where
628 do_w = do_where
628
629
629
630
630 def set_trace(frame=None):
631 def set_trace(frame=None):
631 """
632 """
632 Start debugging from `frame`.
633 Start debugging from `frame`.
633
634
634 If frame is not specified, debugging starts from caller's frame.
635 If frame is not specified, debugging starts from caller's frame.
635 """
636 """
636 Pdb().set_trace(frame or sys._getframe().f_back)
637 Pdb().set_trace(frame or sys._getframe().f_back)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,807 +1,806 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for the IPython tab-completion machinery."""
2 """Tests for the IPython tab-completion machinery."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 import sys
8 import sys
9 import unittest
9 import unittest
10
10
11 from contextlib import contextmanager
11 from contextlib import contextmanager
12
12
13 import nose.tools as nt
13 import nose.tools as nt
14
14
15 from traitlets.config.loader import Config
15 from traitlets.config.loader import Config
16 from IPython import get_ipython
16 from IPython import get_ipython
17 from IPython.core import completer
17 from IPython.core import completer
18 from IPython.external.decorators import knownfailureif
18 from IPython.external.decorators import knownfailureif
19 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
19 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
20 from IPython.utils.generics import complete_object
20 from IPython.utils.generics import complete_object
21 from IPython.utils.py3compat import string_types, unicode_type
21 from IPython.utils.py3compat import string_types, unicode_type
22 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Test functions
25 # Test functions
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 @contextmanager
28 @contextmanager
29 def greedy_completion():
29 def greedy_completion():
30 ip = get_ipython()
30 ip = get_ipython()
31 greedy_original = ip.Completer.greedy
31 greedy_original = ip.Completer.greedy
32 try:
32 try:
33 ip.Completer.greedy = True
33 ip.Completer.greedy = True
34 yield
34 yield
35 finally:
35 finally:
36 ip.Completer.greedy = greedy_original
36 ip.Completer.greedy = greedy_original
37
37
38 def test_protect_filename():
38 def test_protect_filename():
39 if sys.platform == 'win32':
39 if sys.platform == 'win32':
40 pairs = [('abc','abc'),
40 pairs = [('abc','abc'),
41 (' abc','" abc"'),
41 (' abc','" abc"'),
42 ('a bc','"a bc"'),
42 ('a bc','"a bc"'),
43 ('a bc','"a bc"'),
43 ('a bc','"a bc"'),
44 (' bc','" bc"'),
44 (' bc','" bc"'),
45 ]
45 ]
46 else:
46 else:
47 pairs = [('abc','abc'),
47 pairs = [('abc','abc'),
48 (' abc',r'\ abc'),
48 (' abc',r'\ abc'),
49 ('a bc',r'a\ bc'),
49 ('a bc',r'a\ bc'),
50 ('a bc',r'a\ \ bc'),
50 ('a bc',r'a\ \ bc'),
51 (' bc',r'\ \ bc'),
51 (' bc',r'\ \ bc'),
52 # On posix, we also protect parens and other special characters.
52 # On posix, we also protect parens and other special characters.
53 ('a(bc',r'a\(bc'),
53 ('a(bc',r'a\(bc'),
54 ('a)bc',r'a\)bc'),
54 ('a)bc',r'a\)bc'),
55 ('a( )bc',r'a\(\ \)bc'),
55 ('a( )bc',r'a\(\ \)bc'),
56 ('a[1]bc', r'a\[1\]bc'),
56 ('a[1]bc', r'a\[1\]bc'),
57 ('a{1}bc', r'a\{1\}bc'),
57 ('a{1}bc', r'a\{1\}bc'),
58 ('a#bc', r'a\#bc'),
58 ('a#bc', r'a\#bc'),
59 ('a?bc', r'a\?bc'),
59 ('a?bc', r'a\?bc'),
60 ('a=bc', r'a\=bc'),
60 ('a=bc', r'a\=bc'),
61 ('a\\bc', r'a\\bc'),
61 ('a\\bc', r'a\\bc'),
62 ('a|bc', r'a\|bc'),
62 ('a|bc', r'a\|bc'),
63 ('a;bc', r'a\;bc'),
63 ('a;bc', r'a\;bc'),
64 ('a:bc', r'a\:bc'),
64 ('a:bc', r'a\:bc'),
65 ("a'bc", r"a\'bc"),
65 ("a'bc", r"a\'bc"),
66 ('a*bc', r'a\*bc'),
66 ('a*bc', r'a\*bc'),
67 ('a"bc', r'a\"bc'),
67 ('a"bc', r'a\"bc'),
68 ('a^bc', r'a\^bc'),
68 ('a^bc', r'a\^bc'),
69 ('a&bc', r'a\&bc'),
69 ('a&bc', r'a\&bc'),
70 ]
70 ]
71 # run the actual tests
71 # run the actual tests
72 for s1, s2 in pairs:
72 for s1, s2 in pairs:
73 s1p = completer.protect_filename(s1)
73 s1p = completer.protect_filename(s1)
74 nt.assert_equal(s1p, s2)
74 nt.assert_equal(s1p, s2)
75
75
76
76
77 def check_line_split(splitter, test_specs):
77 def check_line_split(splitter, test_specs):
78 for part1, part2, split in test_specs:
78 for part1, part2, split in test_specs:
79 cursor_pos = len(part1)
79 cursor_pos = len(part1)
80 line = part1+part2
80 line = part1+part2
81 out = splitter.split_line(line, cursor_pos)
81 out = splitter.split_line(line, cursor_pos)
82 nt.assert_equal(out, split)
82 nt.assert_equal(out, split)
83
83
84
84
85 def test_line_split():
85 def test_line_split():
86 """Basic line splitter test with default specs."""
86 """Basic line splitter test with default specs."""
87 sp = completer.CompletionSplitter()
87 sp = completer.CompletionSplitter()
88 # The format of the test specs is: part1, part2, expected answer. Parts 1
88 # The format of the test specs is: part1, part2, expected answer. Parts 1
89 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
89 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
90 # was at the end of part1. So an empty part2 represents someone hitting
90 # was at the end of part1. So an empty part2 represents someone hitting
91 # tab at the end of the line, the most common case.
91 # tab at the end of the line, the most common case.
92 t = [('run some/scrip', '', 'some/scrip'),
92 t = [('run some/scrip', '', 'some/scrip'),
93 ('run scripts/er', 'ror.py foo', 'scripts/er'),
93 ('run scripts/er', 'ror.py foo', 'scripts/er'),
94 ('echo $HOM', '', 'HOM'),
94 ('echo $HOM', '', 'HOM'),
95 ('print sys.pa', '', 'sys.pa'),
95 ('print sys.pa', '', 'sys.pa'),
96 ('print(sys.pa', '', 'sys.pa'),
96 ('print(sys.pa', '', 'sys.pa'),
97 ("execfile('scripts/er", '', 'scripts/er'),
97 ("execfile('scripts/er", '', 'scripts/er'),
98 ('a[x.', '', 'x.'),
98 ('a[x.', '', 'x.'),
99 ('a[x.', 'y', 'x.'),
99 ('a[x.', 'y', 'x.'),
100 ('cd "some_file/', '', 'some_file/'),
100 ('cd "some_file/', '', 'some_file/'),
101 ]
101 ]
102 check_line_split(sp, t)
102 check_line_split(sp, t)
103 # Ensure splitting works OK with unicode by re-running the tests with
103 # Ensure splitting works OK with unicode by re-running the tests with
104 # all inputs turned into unicode
104 # all inputs turned into unicode
105 check_line_split(sp, [ map(unicode_type, p) for p in t] )
105 check_line_split(sp, [ map(unicode_type, p) for p in t] )
106
106
107
107
108 def test_custom_completion_error():
108 def test_custom_completion_error():
109 """Test that errors from custom attribute completers are silenced."""
109 """Test that errors from custom attribute completers are silenced."""
110 ip = get_ipython()
110 ip = get_ipython()
111 class A(object): pass
111 class A(object): pass
112 ip.user_ns['a'] = A()
112 ip.user_ns['a'] = A()
113
113
114 @complete_object.when_type(A)
114 @complete_object.when_type(A)
115 def complete_A(a, existing_completions):
115 def complete_A(a, existing_completions):
116 raise TypeError("this should be silenced")
116 raise TypeError("this should be silenced")
117
117
118 ip.complete("a.")
118 ip.complete("a.")
119
119
120
120
121 def test_unicode_completions():
121 def test_unicode_completions():
122 ip = get_ipython()
122 ip = get_ipython()
123 # Some strings that trigger different types of completion. Check them both
123 # Some strings that trigger different types of completion. Check them both
124 # in str and unicode forms
124 # in str and unicode forms
125 s = ['ru', '%ru', 'cd /', 'floa', 'float(x)/']
125 s = ['ru', '%ru', 'cd /', 'floa', 'float(x)/']
126 for t in s + list(map(unicode_type, s)):
126 for t in s + list(map(unicode_type, s)):
127 # We don't need to check exact completion values (they may change
127 # We don't need to check exact completion values (they may change
128 # depending on the state of the namespace, but at least no exceptions
128 # depending on the state of the namespace, but at least no exceptions
129 # should be thrown and the return value should be a pair of text, list
129 # should be thrown and the return value should be a pair of text, list
130 # values.
130 # values.
131 text, matches = ip.complete(t)
131 text, matches = ip.complete(t)
132 nt.assert_true(isinstance(text, string_types))
132 nt.assert_true(isinstance(text, string_types))
133 nt.assert_true(isinstance(matches, list))
133 nt.assert_true(isinstance(matches, list))
134
134
135 @dec.onlyif(sys.version_info[0] >= 3, 'This test only applies in Py>=3')
136 def test_latex_completions():
135 def test_latex_completions():
137 from IPython.core.latex_symbols import latex_symbols
136 from IPython.core.latex_symbols import latex_symbols
138 import random
137 import random
139 ip = get_ipython()
138 ip = get_ipython()
140 # Test some random unicode symbols
139 # Test some random unicode symbols
141 keys = random.sample(latex_symbols.keys(), 10)
140 keys = random.sample(latex_symbols.keys(), 10)
142 for k in keys:
141 for k in keys:
143 text, matches = ip.complete(k)
142 text, matches = ip.complete(k)
144 nt.assert_equal(len(matches),1)
143 nt.assert_equal(len(matches),1)
145 nt.assert_equal(text, k)
144 nt.assert_equal(text, k)
146 nt.assert_equal(matches[0], latex_symbols[k])
145 nt.assert_equal(matches[0], latex_symbols[k])
147 # Test a more complex line
146 # Test a more complex line
148 text, matches = ip.complete(u'print(\\alpha')
147 text, matches = ip.complete(u'print(\\alpha')
149 nt.assert_equals(text, u'\\alpha')
148 nt.assert_equals(text, u'\\alpha')
150 nt.assert_equals(matches[0], latex_symbols['\\alpha'])
149 nt.assert_equals(matches[0], latex_symbols['\\alpha'])
151 # Test multiple matching latex symbols
150 # Test multiple matching latex symbols
152 text, matches = ip.complete(u'\\al')
151 text, matches = ip.complete(u'\\al')
153 nt.assert_in('\\alpha', matches)
152 nt.assert_in('\\alpha', matches)
154 nt.assert_in('\\aleph', matches)
153 nt.assert_in('\\aleph', matches)
155
154
156
155
157
156
158
157
159 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
158 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
160 def test_back_latex_completion():
159 def test_back_latex_completion():
161 ip = get_ipython()
160 ip = get_ipython()
162
161
163 # do not return more than 1 matches fro \beta, only the latex one.
162 # do not return more than 1 matches fro \beta, only the latex one.
164 name, matches = ip.complete('\\Ξ²')
163 name, matches = ip.complete('\\Ξ²')
165 nt.assert_equal(len(matches), 1)
164 nt.assert_equal(len(matches), 1)
166 nt.assert_equal(matches[0], '\\beta')
165 nt.assert_equal(matches[0], '\\beta')
167
166
168 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
167 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
169 def test_back_unicode_completion():
168 def test_back_unicode_completion():
170 ip = get_ipython()
169 ip = get_ipython()
171
170
172 name, matches = ip.complete('\\β…€')
171 name, matches = ip.complete('\\β…€')
173 nt.assert_equal(len(matches), 1)
172 nt.assert_equal(len(matches), 1)
174 nt.assert_equal(matches[0], '\\ROMAN NUMERAL FIVE')
173 nt.assert_equal(matches[0], '\\ROMAN NUMERAL FIVE')
175
174
176
175
177 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
176 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
178 def test_forward_unicode_completion():
177 def test_forward_unicode_completion():
179 ip = get_ipython()
178 ip = get_ipython()
180
179
181 name, matches = ip.complete('\\ROMAN NUMERAL FIVE')
180 name, matches = ip.complete('\\ROMAN NUMERAL FIVE')
182 nt.assert_equal(len(matches), 1)
181 nt.assert_equal(len(matches), 1)
183 nt.assert_equal(matches[0], 'β…€')
182 nt.assert_equal(matches[0], 'β…€')
184
183
185 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
184 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
186 @dec.knownfailureif(sys.platform == 'win32', 'Fails if there is a C:\\j... path')
185 @dec.knownfailureif(sys.platform == 'win32', 'Fails if there is a C:\\j... path')
187 def test_no_ascii_back_completion():
186 def test_no_ascii_back_completion():
188 ip = get_ipython()
187 ip = get_ipython()
189 with TemporaryWorkingDirectory(): # Avoid any filename completions
188 with TemporaryWorkingDirectory(): # Avoid any filename completions
190 # single ascii letter that don't have yet completions
189 # single ascii letter that don't have yet completions
191 for letter in 'jJ' :
190 for letter in 'jJ' :
192 name, matches = ip.complete('\\'+letter)
191 name, matches = ip.complete('\\'+letter)
193 nt.assert_equal(matches, [])
192 nt.assert_equal(matches, [])
194
193
195
194
196
195
197
196
198 class CompletionSplitterTestCase(unittest.TestCase):
197 class CompletionSplitterTestCase(unittest.TestCase):
199 def setUp(self):
198 def setUp(self):
200 self.sp = completer.CompletionSplitter()
199 self.sp = completer.CompletionSplitter()
201
200
202 def test_delim_setting(self):
201 def test_delim_setting(self):
203 self.sp.delims = ' '
202 self.sp.delims = ' '
204 nt.assert_equal(self.sp.delims, ' ')
203 nt.assert_equal(self.sp.delims, ' ')
205 nt.assert_equal(self.sp._delim_expr, '[\ ]')
204 nt.assert_equal(self.sp._delim_expr, '[\ ]')
206
205
207 def test_spaces(self):
206 def test_spaces(self):
208 """Test with only spaces as split chars."""
207 """Test with only spaces as split chars."""
209 self.sp.delims = ' '
208 self.sp.delims = ' '
210 t = [('foo', '', 'foo'),
209 t = [('foo', '', 'foo'),
211 ('run foo', '', 'foo'),
210 ('run foo', '', 'foo'),
212 ('run foo', 'bar', 'foo'),
211 ('run foo', 'bar', 'foo'),
213 ]
212 ]
214 check_line_split(self.sp, t)
213 check_line_split(self.sp, t)
215
214
216
215
217 def test_has_open_quotes1():
216 def test_has_open_quotes1():
218 for s in ["'", "'''", "'hi' '"]:
217 for s in ["'", "'''", "'hi' '"]:
219 nt.assert_equal(completer.has_open_quotes(s), "'")
218 nt.assert_equal(completer.has_open_quotes(s), "'")
220
219
221
220
222 def test_has_open_quotes2():
221 def test_has_open_quotes2():
223 for s in ['"', '"""', '"hi" "']:
222 for s in ['"', '"""', '"hi" "']:
224 nt.assert_equal(completer.has_open_quotes(s), '"')
223 nt.assert_equal(completer.has_open_quotes(s), '"')
225
224
226
225
227 def test_has_open_quotes3():
226 def test_has_open_quotes3():
228 for s in ["''", "''' '''", "'hi' 'ipython'"]:
227 for s in ["''", "''' '''", "'hi' 'ipython'"]:
229 nt.assert_false(completer.has_open_quotes(s))
228 nt.assert_false(completer.has_open_quotes(s))
230
229
231
230
232 def test_has_open_quotes4():
231 def test_has_open_quotes4():
233 for s in ['""', '""" """', '"hi" "ipython"']:
232 for s in ['""', '""" """', '"hi" "ipython"']:
234 nt.assert_false(completer.has_open_quotes(s))
233 nt.assert_false(completer.has_open_quotes(s))
235
234
236
235
237 @knownfailureif(sys.platform == 'win32', "abspath completions fail on Windows")
236 @knownfailureif(sys.platform == 'win32', "abspath completions fail on Windows")
238 def test_abspath_file_completions():
237 def test_abspath_file_completions():
239 ip = get_ipython()
238 ip = get_ipython()
240 with TemporaryDirectory() as tmpdir:
239 with TemporaryDirectory() as tmpdir:
241 prefix = os.path.join(tmpdir, 'foo')
240 prefix = os.path.join(tmpdir, 'foo')
242 suffixes = ['1', '2']
241 suffixes = ['1', '2']
243 names = [prefix+s for s in suffixes]
242 names = [prefix+s for s in suffixes]
244 for n in names:
243 for n in names:
245 open(n, 'w').close()
244 open(n, 'w').close()
246
245
247 # Check simple completion
246 # Check simple completion
248 c = ip.complete(prefix)[1]
247 c = ip.complete(prefix)[1]
249 nt.assert_equal(c, names)
248 nt.assert_equal(c, names)
250
249
251 # Now check with a function call
250 # Now check with a function call
252 cmd = 'a = f("%s' % prefix
251 cmd = 'a = f("%s' % prefix
253 c = ip.complete(prefix, cmd)[1]
252 c = ip.complete(prefix, cmd)[1]
254 comp = [prefix+s for s in suffixes]
253 comp = [prefix+s for s in suffixes]
255 nt.assert_equal(c, comp)
254 nt.assert_equal(c, comp)
256
255
257
256
258 def test_local_file_completions():
257 def test_local_file_completions():
259 ip = get_ipython()
258 ip = get_ipython()
260 with TemporaryWorkingDirectory():
259 with TemporaryWorkingDirectory():
261 prefix = './foo'
260 prefix = './foo'
262 suffixes = ['1', '2']
261 suffixes = ['1', '2']
263 names = [prefix+s for s in suffixes]
262 names = [prefix+s for s in suffixes]
264 for n in names:
263 for n in names:
265 open(n, 'w').close()
264 open(n, 'w').close()
266
265
267 # Check simple completion
266 # Check simple completion
268 c = ip.complete(prefix)[1]
267 c = ip.complete(prefix)[1]
269 nt.assert_equal(c, names)
268 nt.assert_equal(c, names)
270
269
271 # Now check with a function call
270 # Now check with a function call
272 cmd = 'a = f("%s' % prefix
271 cmd = 'a = f("%s' % prefix
273 c = ip.complete(prefix, cmd)[1]
272 c = ip.complete(prefix, cmd)[1]
274 comp = set(prefix+s for s in suffixes)
273 comp = set(prefix+s for s in suffixes)
275 nt.assert_true(comp.issubset(set(c)))
274 nt.assert_true(comp.issubset(set(c)))
276
275
277
276
278 def test_greedy_completions():
277 def test_greedy_completions():
279 ip = get_ipython()
278 ip = get_ipython()
280 ip.ex('a=list(range(5))')
279 ip.ex('a=list(range(5))')
281 _,c = ip.complete('.',line='a[0].')
280 _,c = ip.complete('.',line='a[0].')
282 nt.assert_false('.real' in c,
281 nt.assert_false('.real' in c,
283 "Shouldn't have completed on a[0]: %s"%c)
282 "Shouldn't have completed on a[0]: %s"%c)
284 with greedy_completion():
283 with greedy_completion():
285 def _(line, cursor_pos, expect, message):
284 def _(line, cursor_pos, expect, message):
286 _,c = ip.complete('.', line=line, cursor_pos=cursor_pos)
285 _,c = ip.complete('.', line=line, cursor_pos=cursor_pos)
287 nt.assert_in(expect, c, message%c)
286 nt.assert_in(expect, c, message%c)
288
287
289 yield _, 'a[0].', 5, 'a[0].real', "Should have completed on a[0].: %s"
288 yield _, 'a[0].', 5, 'a[0].real', "Should have completed on a[0].: %s"
290 yield _, 'a[0].r', 6, 'a[0].real', "Should have completed on a[0].r: %s"
289 yield _, 'a[0].r', 6, 'a[0].real', "Should have completed on a[0].r: %s"
291
290
292 if sys.version_info > (3,4):
291 if sys.version_info > (3,4):
293 yield _, 'a[0].from_', 10, 'a[0].from_bytes', "Should have completed on a[0].from_: %s"
292 yield _, 'a[0].from_', 10, 'a[0].from_bytes', "Should have completed on a[0].from_: %s"
294
293
295
294
296
295
297 def test_omit__names():
296 def test_omit__names():
298 # also happens to test IPCompleter as a configurable
297 # also happens to test IPCompleter as a configurable
299 ip = get_ipython()
298 ip = get_ipython()
300 ip._hidden_attr = 1
299 ip._hidden_attr = 1
301 ip._x = {}
300 ip._x = {}
302 c = ip.Completer
301 c = ip.Completer
303 ip.ex('ip=get_ipython()')
302 ip.ex('ip=get_ipython()')
304 cfg = Config()
303 cfg = Config()
305 cfg.IPCompleter.omit__names = 0
304 cfg.IPCompleter.omit__names = 0
306 c.update_config(cfg)
305 c.update_config(cfg)
307 s,matches = c.complete('ip.')
306 s,matches = c.complete('ip.')
308 nt.assert_in('ip.__str__', matches)
307 nt.assert_in('ip.__str__', matches)
309 nt.assert_in('ip._hidden_attr', matches)
308 nt.assert_in('ip._hidden_attr', matches)
310 cfg = Config()
309 cfg = Config()
311 cfg.IPCompleter.omit__names = 1
310 cfg.IPCompleter.omit__names = 1
312 c.update_config(cfg)
311 c.update_config(cfg)
313 s,matches = c.complete('ip.')
312 s,matches = c.complete('ip.')
314 nt.assert_not_in('ip.__str__', matches)
313 nt.assert_not_in('ip.__str__', matches)
315 nt.assert_in('ip._hidden_attr', matches)
314 nt.assert_in('ip._hidden_attr', matches)
316 cfg = Config()
315 cfg = Config()
317 cfg.IPCompleter.omit__names = 2
316 cfg.IPCompleter.omit__names = 2
318 c.update_config(cfg)
317 c.update_config(cfg)
319 s,matches = c.complete('ip.')
318 s,matches = c.complete('ip.')
320 nt.assert_not_in('ip.__str__', matches)
319 nt.assert_not_in('ip.__str__', matches)
321 nt.assert_not_in('ip._hidden_attr', matches)
320 nt.assert_not_in('ip._hidden_attr', matches)
322 s,matches = c.complete('ip._x.')
321 s,matches = c.complete('ip._x.')
323 nt.assert_in('ip._x.keys', matches)
322 nt.assert_in('ip._x.keys', matches)
324 del ip._hidden_attr
323 del ip._hidden_attr
325
324
326
325
327 def test_limit_to__all__False_ok():
326 def test_limit_to__all__False_ok():
328 ip = get_ipython()
327 ip = get_ipython()
329 c = ip.Completer
328 c = ip.Completer
330 ip.ex('class D: x=24')
329 ip.ex('class D: x=24')
331 ip.ex('d=D()')
330 ip.ex('d=D()')
332 cfg = Config()
331 cfg = Config()
333 cfg.IPCompleter.limit_to__all__ = False
332 cfg.IPCompleter.limit_to__all__ = False
334 c.update_config(cfg)
333 c.update_config(cfg)
335 s, matches = c.complete('d.')
334 s, matches = c.complete('d.')
336 nt.assert_in('d.x', matches)
335 nt.assert_in('d.x', matches)
337
336
338
337
339 def test_get__all__entries_ok():
338 def test_get__all__entries_ok():
340 class A(object):
339 class A(object):
341 __all__ = ['x', 1]
340 __all__ = ['x', 1]
342 words = completer.get__all__entries(A())
341 words = completer.get__all__entries(A())
343 nt.assert_equal(words, ['x'])
342 nt.assert_equal(words, ['x'])
344
343
345
344
346 def test_get__all__entries_no__all__ok():
345 def test_get__all__entries_no__all__ok():
347 class A(object):
346 class A(object):
348 pass
347 pass
349 words = completer.get__all__entries(A())
348 words = completer.get__all__entries(A())
350 nt.assert_equal(words, [])
349 nt.assert_equal(words, [])
351
350
352
351
353 def test_func_kw_completions():
352 def test_func_kw_completions():
354 ip = get_ipython()
353 ip = get_ipython()
355 c = ip.Completer
354 c = ip.Completer
356 ip.ex('def myfunc(a=1,b=2): return a+b')
355 ip.ex('def myfunc(a=1,b=2): return a+b')
357 s, matches = c.complete(None, 'myfunc(1,b')
356 s, matches = c.complete(None, 'myfunc(1,b')
358 nt.assert_in('b=', matches)
357 nt.assert_in('b=', matches)
359 # Simulate completing with cursor right after b (pos==10):
358 # Simulate completing with cursor right after b (pos==10):
360 s, matches = c.complete(None, 'myfunc(1,b)', 10)
359 s, matches = c.complete(None, 'myfunc(1,b)', 10)
361 nt.assert_in('b=', matches)
360 nt.assert_in('b=', matches)
362 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
361 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
363 nt.assert_in('b=', matches)
362 nt.assert_in('b=', matches)
364 #builtin function
363 #builtin function
365 s, matches = c.complete(None, 'min(k, k')
364 s, matches = c.complete(None, 'min(k, k')
366 nt.assert_in('key=', matches)
365 nt.assert_in('key=', matches)
367
366
368
367
369 def test_default_arguments_from_docstring():
368 def test_default_arguments_from_docstring():
370 ip = get_ipython()
369 ip = get_ipython()
371 c = ip.Completer
370 c = ip.Completer
372 kwd = c._default_arguments_from_docstring(
371 kwd = c._default_arguments_from_docstring(
373 'min(iterable[, key=func]) -> value')
372 'min(iterable[, key=func]) -> value')
374 nt.assert_equal(kwd, ['key'])
373 nt.assert_equal(kwd, ['key'])
375 #with cython type etc
374 #with cython type etc
376 kwd = c._default_arguments_from_docstring(
375 kwd = c._default_arguments_from_docstring(
377 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
376 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
378 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
377 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
379 #white spaces
378 #white spaces
380 kwd = c._default_arguments_from_docstring(
379 kwd = c._default_arguments_from_docstring(
381 '\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
380 '\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
382 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
381 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
383
382
384 def test_line_magics():
383 def test_line_magics():
385 ip = get_ipython()
384 ip = get_ipython()
386 c = ip.Completer
385 c = ip.Completer
387 s, matches = c.complete(None, 'lsmag')
386 s, matches = c.complete(None, 'lsmag')
388 nt.assert_in('%lsmagic', matches)
387 nt.assert_in('%lsmagic', matches)
389 s, matches = c.complete(None, '%lsmag')
388 s, matches = c.complete(None, '%lsmag')
390 nt.assert_in('%lsmagic', matches)
389 nt.assert_in('%lsmagic', matches)
391
390
392
391
393 def test_cell_magics():
392 def test_cell_magics():
394 from IPython.core.magic import register_cell_magic
393 from IPython.core.magic import register_cell_magic
395
394
396 @register_cell_magic
395 @register_cell_magic
397 def _foo_cellm(line, cell):
396 def _foo_cellm(line, cell):
398 pass
397 pass
399
398
400 ip = get_ipython()
399 ip = get_ipython()
401 c = ip.Completer
400 c = ip.Completer
402
401
403 s, matches = c.complete(None, '_foo_ce')
402 s, matches = c.complete(None, '_foo_ce')
404 nt.assert_in('%%_foo_cellm', matches)
403 nt.assert_in('%%_foo_cellm', matches)
405 s, matches = c.complete(None, '%%_foo_ce')
404 s, matches = c.complete(None, '%%_foo_ce')
406 nt.assert_in('%%_foo_cellm', matches)
405 nt.assert_in('%%_foo_cellm', matches)
407
406
408
407
409 def test_line_cell_magics():
408 def test_line_cell_magics():
410 from IPython.core.magic import register_line_cell_magic
409 from IPython.core.magic import register_line_cell_magic
411
410
412 @register_line_cell_magic
411 @register_line_cell_magic
413 def _bar_cellm(line, cell):
412 def _bar_cellm(line, cell):
414 pass
413 pass
415
414
416 ip = get_ipython()
415 ip = get_ipython()
417 c = ip.Completer
416 c = ip.Completer
418
417
419 # The policy here is trickier, see comments in completion code. The
418 # The policy here is trickier, see comments in completion code. The
420 # returned values depend on whether the user passes %% or not explicitly,
419 # returned values depend on whether the user passes %% or not explicitly,
421 # and this will show a difference if the same name is both a line and cell
420 # and this will show a difference if the same name is both a line and cell
422 # magic.
421 # magic.
423 s, matches = c.complete(None, '_bar_ce')
422 s, matches = c.complete(None, '_bar_ce')
424 nt.assert_in('%_bar_cellm', matches)
423 nt.assert_in('%_bar_cellm', matches)
425 nt.assert_in('%%_bar_cellm', matches)
424 nt.assert_in('%%_bar_cellm', matches)
426 s, matches = c.complete(None, '%_bar_ce')
425 s, matches = c.complete(None, '%_bar_ce')
427 nt.assert_in('%_bar_cellm', matches)
426 nt.assert_in('%_bar_cellm', matches)
428 nt.assert_in('%%_bar_cellm', matches)
427 nt.assert_in('%%_bar_cellm', matches)
429 s, matches = c.complete(None, '%%_bar_ce')
428 s, matches = c.complete(None, '%%_bar_ce')
430 nt.assert_not_in('%_bar_cellm', matches)
429 nt.assert_not_in('%_bar_cellm', matches)
431 nt.assert_in('%%_bar_cellm', matches)
430 nt.assert_in('%%_bar_cellm', matches)
432
431
433
432
434 def test_magic_completion_order():
433 def test_magic_completion_order():
435
434
436 ip = get_ipython()
435 ip = get_ipython()
437 c = ip.Completer
436 c = ip.Completer
438
437
439 # Test ordering of magics and non-magics with the same name
438 # Test ordering of magics and non-magics with the same name
440 # We want the non-magic first
439 # We want the non-magic first
441
440
442 # Before importing matplotlib, there should only be one option:
441 # Before importing matplotlib, there should only be one option:
443
442
444 text, matches = c.complete('mat')
443 text, matches = c.complete('mat')
445 nt.assert_equal(matches, ["%matplotlib"])
444 nt.assert_equal(matches, ["%matplotlib"])
446
445
447
446
448 ip.run_cell("matplotlib = 1") # introduce name into namespace
447 ip.run_cell("matplotlib = 1") # introduce name into namespace
449
448
450 # After the import, there should be two options, ordered like this:
449 # After the import, there should be two options, ordered like this:
451 text, matches = c.complete('mat')
450 text, matches = c.complete('mat')
452 nt.assert_equal(matches, ["matplotlib", "%matplotlib"])
451 nt.assert_equal(matches, ["matplotlib", "%matplotlib"])
453
452
454
453
455 ip.run_cell("timeit = 1") # define a user variable called 'timeit'
454 ip.run_cell("timeit = 1") # define a user variable called 'timeit'
456
455
457 # Order of user variable and line and cell magics with same name:
456 # Order of user variable and line and cell magics with same name:
458 text, matches = c.complete('timeit')
457 text, matches = c.complete('timeit')
459 nt.assert_equal(matches, ["timeit", "%timeit","%%timeit"])
458 nt.assert_equal(matches, ["timeit", "%timeit","%%timeit"])
460
459
461
460
462 def test_dict_key_completion_string():
461 def test_dict_key_completion_string():
463 """Test dictionary key completion for string keys"""
462 """Test dictionary key completion for string keys"""
464 ip = get_ipython()
463 ip = get_ipython()
465 complete = ip.Completer.complete
464 complete = ip.Completer.complete
466
465
467 ip.user_ns['d'] = {'abc': None}
466 ip.user_ns['d'] = {'abc': None}
468
467
469 # check completion at different stages
468 # check completion at different stages
470 _, matches = complete(line_buffer="d[")
469 _, matches = complete(line_buffer="d[")
471 nt.assert_in("'abc'", matches)
470 nt.assert_in("'abc'", matches)
472 nt.assert_not_in("'abc']", matches)
471 nt.assert_not_in("'abc']", matches)
473
472
474 _, matches = complete(line_buffer="d['")
473 _, matches = complete(line_buffer="d['")
475 nt.assert_in("abc", matches)
474 nt.assert_in("abc", matches)
476 nt.assert_not_in("abc']", matches)
475 nt.assert_not_in("abc']", matches)
477
476
478 _, matches = complete(line_buffer="d['a")
477 _, matches = complete(line_buffer="d['a")
479 nt.assert_in("abc", matches)
478 nt.assert_in("abc", matches)
480 nt.assert_not_in("abc']", matches)
479 nt.assert_not_in("abc']", matches)
481
480
482 # check use of different quoting
481 # check use of different quoting
483 _, matches = complete(line_buffer="d[\"")
482 _, matches = complete(line_buffer="d[\"")
484 nt.assert_in("abc", matches)
483 nt.assert_in("abc", matches)
485 nt.assert_not_in('abc\"]', matches)
484 nt.assert_not_in('abc\"]', matches)
486
485
487 _, matches = complete(line_buffer="d[\"a")
486 _, matches = complete(line_buffer="d[\"a")
488 nt.assert_in("abc", matches)
487 nt.assert_in("abc", matches)
489 nt.assert_not_in('abc\"]', matches)
488 nt.assert_not_in('abc\"]', matches)
490
489
491 # check sensitivity to following context
490 # check sensitivity to following context
492 _, matches = complete(line_buffer="d[]", cursor_pos=2)
491 _, matches = complete(line_buffer="d[]", cursor_pos=2)
493 nt.assert_in("'abc'", matches)
492 nt.assert_in("'abc'", matches)
494
493
495 _, matches = complete(line_buffer="d['']", cursor_pos=3)
494 _, matches = complete(line_buffer="d['']", cursor_pos=3)
496 nt.assert_in("abc", matches)
495 nt.assert_in("abc", matches)
497 nt.assert_not_in("abc'", matches)
496 nt.assert_not_in("abc'", matches)
498 nt.assert_not_in("abc']", matches)
497 nt.assert_not_in("abc']", matches)
499
498
500 # check multiple solutions are correctly returned and that noise is not
499 # check multiple solutions are correctly returned and that noise is not
501 ip.user_ns['d'] = {'abc': None, 'abd': None, 'bad': None, object(): None,
500 ip.user_ns['d'] = {'abc': None, 'abd': None, 'bad': None, object(): None,
502 5: None}
501 5: None}
503
502
504 _, matches = complete(line_buffer="d['a")
503 _, matches = complete(line_buffer="d['a")
505 nt.assert_in("abc", matches)
504 nt.assert_in("abc", matches)
506 nt.assert_in("abd", matches)
505 nt.assert_in("abd", matches)
507 nt.assert_not_in("bad", matches)
506 nt.assert_not_in("bad", matches)
508 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
507 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
509
508
510 # check escaping and whitespace
509 # check escaping and whitespace
511 ip.user_ns['d'] = {'a\nb': None, 'a\'b': None, 'a"b': None, 'a word': None}
510 ip.user_ns['d'] = {'a\nb': None, 'a\'b': None, 'a"b': None, 'a word': None}
512 _, matches = complete(line_buffer="d['a")
511 _, matches = complete(line_buffer="d['a")
513 nt.assert_in("a\\nb", matches)
512 nt.assert_in("a\\nb", matches)
514 nt.assert_in("a\\'b", matches)
513 nt.assert_in("a\\'b", matches)
515 nt.assert_in("a\"b", matches)
514 nt.assert_in("a\"b", matches)
516 nt.assert_in("a word", matches)
515 nt.assert_in("a word", matches)
517 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
516 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
518
517
519 # - can complete on non-initial word of the string
518 # - can complete on non-initial word of the string
520 _, matches = complete(line_buffer="d['a w")
519 _, matches = complete(line_buffer="d['a w")
521 nt.assert_in("word", matches)
520 nt.assert_in("word", matches)
522
521
523 # - understands quote escaping
522 # - understands quote escaping
524 _, matches = complete(line_buffer="d['a\\'")
523 _, matches = complete(line_buffer="d['a\\'")
525 nt.assert_in("b", matches)
524 nt.assert_in("b", matches)
526
525
527 # - default quoting should work like repr
526 # - default quoting should work like repr
528 _, matches = complete(line_buffer="d[")
527 _, matches = complete(line_buffer="d[")
529 nt.assert_in("\"a'b\"", matches)
528 nt.assert_in("\"a'b\"", matches)
530
529
531 # - when opening quote with ", possible to match with unescaped apostrophe
530 # - when opening quote with ", possible to match with unescaped apostrophe
532 _, matches = complete(line_buffer="d[\"a'")
531 _, matches = complete(line_buffer="d[\"a'")
533 nt.assert_in("b", matches)
532 nt.assert_in("b", matches)
534
533
535 # need to not split at delims that readline won't split at
534 # need to not split at delims that readline won't split at
536 if '-' not in ip.Completer.splitter.delims:
535 if '-' not in ip.Completer.splitter.delims:
537 ip.user_ns['d'] = {'before-after': None}
536 ip.user_ns['d'] = {'before-after': None}
538 _, matches = complete(line_buffer="d['before-af")
537 _, matches = complete(line_buffer="d['before-af")
539 nt.assert_in('before-after', matches)
538 nt.assert_in('before-after', matches)
540
539
541 def test_dict_key_completion_contexts():
540 def test_dict_key_completion_contexts():
542 """Test expression contexts in which dict key completion occurs"""
541 """Test expression contexts in which dict key completion occurs"""
543 ip = get_ipython()
542 ip = get_ipython()
544 complete = ip.Completer.complete
543 complete = ip.Completer.complete
545 d = {'abc': None}
544 d = {'abc': None}
546 ip.user_ns['d'] = d
545 ip.user_ns['d'] = d
547
546
548 class C:
547 class C:
549 data = d
548 data = d
550 ip.user_ns['C'] = C
549 ip.user_ns['C'] = C
551 ip.user_ns['get'] = lambda: d
550 ip.user_ns['get'] = lambda: d
552
551
553 def assert_no_completion(**kwargs):
552 def assert_no_completion(**kwargs):
554 _, matches = complete(**kwargs)
553 _, matches = complete(**kwargs)
555 nt.assert_not_in('abc', matches)
554 nt.assert_not_in('abc', matches)
556 nt.assert_not_in('abc\'', matches)
555 nt.assert_not_in('abc\'', matches)
557 nt.assert_not_in('abc\']', matches)
556 nt.assert_not_in('abc\']', matches)
558 nt.assert_not_in('\'abc\'', matches)
557 nt.assert_not_in('\'abc\'', matches)
559 nt.assert_not_in('\'abc\']', matches)
558 nt.assert_not_in('\'abc\']', matches)
560
559
561 def assert_completion(**kwargs):
560 def assert_completion(**kwargs):
562 _, matches = complete(**kwargs)
561 _, matches = complete(**kwargs)
563 nt.assert_in("'abc'", matches)
562 nt.assert_in("'abc'", matches)
564 nt.assert_not_in("'abc']", matches)
563 nt.assert_not_in("'abc']", matches)
565
564
566 # no completion after string closed, even if reopened
565 # no completion after string closed, even if reopened
567 assert_no_completion(line_buffer="d['a'")
566 assert_no_completion(line_buffer="d['a'")
568 assert_no_completion(line_buffer="d[\"a\"")
567 assert_no_completion(line_buffer="d[\"a\"")
569 assert_no_completion(line_buffer="d['a' + ")
568 assert_no_completion(line_buffer="d['a' + ")
570 assert_no_completion(line_buffer="d['a' + '")
569 assert_no_completion(line_buffer="d['a' + '")
571
570
572 # completion in non-trivial expressions
571 # completion in non-trivial expressions
573 assert_completion(line_buffer="+ d[")
572 assert_completion(line_buffer="+ d[")
574 assert_completion(line_buffer="(d[")
573 assert_completion(line_buffer="(d[")
575 assert_completion(line_buffer="C.data[")
574 assert_completion(line_buffer="C.data[")
576
575
577 # greedy flag
576 # greedy flag
578 def assert_completion(**kwargs):
577 def assert_completion(**kwargs):
579 _, matches = complete(**kwargs)
578 _, matches = complete(**kwargs)
580 nt.assert_in("get()['abc']", matches)
579 nt.assert_in("get()['abc']", matches)
581
580
582 assert_no_completion(line_buffer="get()[")
581 assert_no_completion(line_buffer="get()[")
583 with greedy_completion():
582 with greedy_completion():
584 assert_completion(line_buffer="get()[")
583 assert_completion(line_buffer="get()[")
585 assert_completion(line_buffer="get()['")
584 assert_completion(line_buffer="get()['")
586 assert_completion(line_buffer="get()['a")
585 assert_completion(line_buffer="get()['a")
587 assert_completion(line_buffer="get()['ab")
586 assert_completion(line_buffer="get()['ab")
588 assert_completion(line_buffer="get()['abc")
587 assert_completion(line_buffer="get()['abc")
589
588
590
589
591
590
592 @dec.onlyif(sys.version_info[0] >= 3, 'This test only applies in Py>=3')
591 @dec.onlyif(sys.version_info[0] >= 3, 'This test only applies in Py>=3')
593 def test_dict_key_completion_bytes():
592 def test_dict_key_completion_bytes():
594 """Test handling of bytes in dict key completion"""
593 """Test handling of bytes in dict key completion"""
595 ip = get_ipython()
594 ip = get_ipython()
596 complete = ip.Completer.complete
595 complete = ip.Completer.complete
597
596
598 ip.user_ns['d'] = {'abc': None, b'abd': None}
597 ip.user_ns['d'] = {'abc': None, b'abd': None}
599
598
600 _, matches = complete(line_buffer="d[")
599 _, matches = complete(line_buffer="d[")
601 nt.assert_in("'abc'", matches)
600 nt.assert_in("'abc'", matches)
602 nt.assert_in("b'abd'", matches)
601 nt.assert_in("b'abd'", matches)
603
602
604 if False: # not currently implemented
603 if False: # not currently implemented
605 _, matches = complete(line_buffer="d[b")
604 _, matches = complete(line_buffer="d[b")
606 nt.assert_in("b'abd'", matches)
605 nt.assert_in("b'abd'", matches)
607 nt.assert_not_in("b'abc'", matches)
606 nt.assert_not_in("b'abc'", matches)
608
607
609 _, matches = complete(line_buffer="d[b'")
608 _, matches = complete(line_buffer="d[b'")
610 nt.assert_in("abd", matches)
609 nt.assert_in("abd", matches)
611 nt.assert_not_in("abc", matches)
610 nt.assert_not_in("abc", matches)
612
611
613 _, matches = complete(line_buffer="d[B'")
612 _, matches = complete(line_buffer="d[B'")
614 nt.assert_in("abd", matches)
613 nt.assert_in("abd", matches)
615 nt.assert_not_in("abc", matches)
614 nt.assert_not_in("abc", matches)
616
615
617 _, matches = complete(line_buffer="d['")
616 _, matches = complete(line_buffer="d['")
618 nt.assert_in("abc", matches)
617 nt.assert_in("abc", matches)
619 nt.assert_not_in("abd", matches)
618 nt.assert_not_in("abd", matches)
620
619
621
620
622 @dec.onlyif(sys.version_info[0] < 3, 'This test only applies in Py<3')
621 @dec.onlyif(sys.version_info[0] < 3, 'This test only applies in Py<3')
623 def test_dict_key_completion_unicode_py2():
622 def test_dict_key_completion_unicode_py2():
624 """Test handling of unicode in dict key completion"""
623 """Test handling of unicode in dict key completion"""
625 ip = get_ipython()
624 ip = get_ipython()
626 complete = ip.Completer.complete
625 complete = ip.Completer.complete
627
626
628 ip.user_ns['d'] = {u'abc': None,
627 ip.user_ns['d'] = {u'abc': None,
629 u'a\u05d0b': None}
628 u'a\u05d0b': None}
630
629
631 _, matches = complete(line_buffer="d[")
630 _, matches = complete(line_buffer="d[")
632 nt.assert_in("u'abc'", matches)
631 nt.assert_in("u'abc'", matches)
633 nt.assert_in("u'a\\u05d0b'", matches)
632 nt.assert_in("u'a\\u05d0b'", matches)
634
633
635 _, matches = complete(line_buffer="d['a")
634 _, matches = complete(line_buffer="d['a")
636 nt.assert_in("abc", matches)
635 nt.assert_in("abc", matches)
637 nt.assert_not_in("a\\u05d0b", matches)
636 nt.assert_not_in("a\\u05d0b", matches)
638
637
639 _, matches = complete(line_buffer="d[u'a")
638 _, matches = complete(line_buffer="d[u'a")
640 nt.assert_in("abc", matches)
639 nt.assert_in("abc", matches)
641 nt.assert_in("a\\u05d0b", matches)
640 nt.assert_in("a\\u05d0b", matches)
642
641
643 _, matches = complete(line_buffer="d[U'a")
642 _, matches = complete(line_buffer="d[U'a")
644 nt.assert_in("abc", matches)
643 nt.assert_in("abc", matches)
645 nt.assert_in("a\\u05d0b", matches)
644 nt.assert_in("a\\u05d0b", matches)
646
645
647 # query using escape
646 # query using escape
648 if sys.platform != 'win32':
647 if sys.platform != 'win32':
649 # Known failure on Windows
648 # Known failure on Windows
650 _, matches = complete(line_buffer=u"d[u'a\\u05d0")
649 _, matches = complete(line_buffer=u"d[u'a\\u05d0")
651 nt.assert_in("u05d0b", matches) # tokenized after \\
650 nt.assert_in("u05d0b", matches) # tokenized after \\
652
651
653 # query using character
652 # query using character
654 _, matches = complete(line_buffer=u"d[u'a\u05d0")
653 _, matches = complete(line_buffer=u"d[u'a\u05d0")
655 nt.assert_in(u"a\u05d0b", matches)
654 nt.assert_in(u"a\u05d0b", matches)
656
655
657 with greedy_completion():
656 with greedy_completion():
658 _, matches = complete(line_buffer="d[")
657 _, matches = complete(line_buffer="d[")
659 nt.assert_in("d[u'abc']", matches)
658 nt.assert_in("d[u'abc']", matches)
660 nt.assert_in("d[u'a\\u05d0b']", matches)
659 nt.assert_in("d[u'a\\u05d0b']", matches)
661
660
662 _, matches = complete(line_buffer="d['a")
661 _, matches = complete(line_buffer="d['a")
663 nt.assert_in("d['abc']", matches)
662 nt.assert_in("d['abc']", matches)
664 nt.assert_not_in("d[u'a\\u05d0b']", matches)
663 nt.assert_not_in("d[u'a\\u05d0b']", matches)
665
664
666 _, matches = complete(line_buffer="d[u'a")
665 _, matches = complete(line_buffer="d[u'a")
667 nt.assert_in("d[u'abc']", matches)
666 nt.assert_in("d[u'abc']", matches)
668 nt.assert_in("d[u'a\\u05d0b']", matches)
667 nt.assert_in("d[u'a\\u05d0b']", matches)
669
668
670 _, matches = complete(line_buffer="d[U'a")
669 _, matches = complete(line_buffer="d[U'a")
671 nt.assert_in("d[U'abc']", matches)
670 nt.assert_in("d[U'abc']", matches)
672 nt.assert_in("d[U'a\\u05d0b']", matches)
671 nt.assert_in("d[U'a\\u05d0b']", matches)
673
672
674 # query using escape
673 # query using escape
675 _, matches = complete(line_buffer=u"d[u'a\\u05d0")
674 _, matches = complete(line_buffer=u"d[u'a\\u05d0")
676 nt.assert_in("d[u'a\\u05d0b']", matches) # tokenized after \\
675 nt.assert_in("d[u'a\\u05d0b']", matches) # tokenized after \\
677
676
678 # query using character
677 # query using character
679 _, matches = complete(line_buffer=u"d[u'a\u05d0")
678 _, matches = complete(line_buffer=u"d[u'a\u05d0")
680 nt.assert_in(u"d[u'a\u05d0b']", matches)
679 nt.assert_in(u"d[u'a\u05d0b']", matches)
681
680
682
681
683 @dec.onlyif(sys.version_info[0] >= 3, 'This test only applies in Py>=3')
682 @dec.onlyif(sys.version_info[0] >= 3, 'This test only applies in Py>=3')
684 def test_dict_key_completion_unicode_py3():
683 def test_dict_key_completion_unicode_py3():
685 """Test handling of unicode in dict key completion"""
684 """Test handling of unicode in dict key completion"""
686 ip = get_ipython()
685 ip = get_ipython()
687 complete = ip.Completer.complete
686 complete = ip.Completer.complete
688
687
689 ip.user_ns['d'] = {u'a\u05d0': None}
688 ip.user_ns['d'] = {u'a\u05d0': None}
690
689
691 # query using escape
690 # query using escape
692 if sys.platform != 'win32':
691 if sys.platform != 'win32':
693 # Known failure on Windows
692 # Known failure on Windows
694 _, matches = complete(line_buffer="d['a\\u05d0")
693 _, matches = complete(line_buffer="d['a\\u05d0")
695 nt.assert_in("u05d0", matches) # tokenized after \\
694 nt.assert_in("u05d0", matches) # tokenized after \\
696
695
697 # query using character
696 # query using character
698 _, matches = complete(line_buffer="d['a\u05d0")
697 _, matches = complete(line_buffer="d['a\u05d0")
699 nt.assert_in(u"a\u05d0", matches)
698 nt.assert_in(u"a\u05d0", matches)
700
699
701 with greedy_completion():
700 with greedy_completion():
702 # query using escape
701 # query using escape
703 _, matches = complete(line_buffer="d['a\\u05d0")
702 _, matches = complete(line_buffer="d['a\\u05d0")
704 nt.assert_in("d['a\\u05d0']", matches) # tokenized after \\
703 nt.assert_in("d['a\\u05d0']", matches) # tokenized after \\
705
704
706 # query using character
705 # query using character
707 _, matches = complete(line_buffer="d['a\u05d0")
706 _, matches = complete(line_buffer="d['a\u05d0")
708 nt.assert_in(u"d['a\u05d0']", matches)
707 nt.assert_in(u"d['a\u05d0']", matches)
709
708
710
709
711
710
712 @dec.skip_without('numpy')
711 @dec.skip_without('numpy')
713 def test_struct_array_key_completion():
712 def test_struct_array_key_completion():
714 """Test dict key completion applies to numpy struct arrays"""
713 """Test dict key completion applies to numpy struct arrays"""
715 import numpy
714 import numpy
716 ip = get_ipython()
715 ip = get_ipython()
717 complete = ip.Completer.complete
716 complete = ip.Completer.complete
718 ip.user_ns['d'] = numpy.array([], dtype=[('hello', 'f'), ('world', 'f')])
717 ip.user_ns['d'] = numpy.array([], dtype=[('hello', 'f'), ('world', 'f')])
719 _, matches = complete(line_buffer="d['")
718 _, matches = complete(line_buffer="d['")
720 nt.assert_in("hello", matches)
719 nt.assert_in("hello", matches)
721 nt.assert_in("world", matches)
720 nt.assert_in("world", matches)
722 # complete on the numpy struct itself
721 # complete on the numpy struct itself
723 dt = numpy.dtype([('my_head', [('my_dt', '>u4'), ('my_df', '>u4')]),
722 dt = numpy.dtype([('my_head', [('my_dt', '>u4'), ('my_df', '>u4')]),
724 ('my_data', '>f4', 5)])
723 ('my_data', '>f4', 5)])
725 x = numpy.zeros(2, dtype=dt)
724 x = numpy.zeros(2, dtype=dt)
726 ip.user_ns['d'] = x[1]
725 ip.user_ns['d'] = x[1]
727 _, matches = complete(line_buffer="d['")
726 _, matches = complete(line_buffer="d['")
728 nt.assert_in("my_head", matches)
727 nt.assert_in("my_head", matches)
729 nt.assert_in("my_data", matches)
728 nt.assert_in("my_data", matches)
730 # complete on a nested level
729 # complete on a nested level
731 with greedy_completion():
730 with greedy_completion():
732 ip.user_ns['d'] = numpy.zeros(2, dtype=dt)
731 ip.user_ns['d'] = numpy.zeros(2, dtype=dt)
733 _, matches = complete(line_buffer="d[1]['my_head']['")
732 _, matches = complete(line_buffer="d[1]['my_head']['")
734 nt.assert_true(any(["my_dt" in m for m in matches]))
733 nt.assert_true(any(["my_dt" in m for m in matches]))
735 nt.assert_true(any(["my_df" in m for m in matches]))
734 nt.assert_true(any(["my_df" in m for m in matches]))
736
735
737
736
738 @dec.skip_without('pandas')
737 @dec.skip_without('pandas')
739 def test_dataframe_key_completion():
738 def test_dataframe_key_completion():
740 """Test dict key completion applies to pandas DataFrames"""
739 """Test dict key completion applies to pandas DataFrames"""
741 import pandas
740 import pandas
742 ip = get_ipython()
741 ip = get_ipython()
743 complete = ip.Completer.complete
742 complete = ip.Completer.complete
744 ip.user_ns['d'] = pandas.DataFrame({'hello': [1], 'world': [2]})
743 ip.user_ns['d'] = pandas.DataFrame({'hello': [1], 'world': [2]})
745 _, matches = complete(line_buffer="d['")
744 _, matches = complete(line_buffer="d['")
746 nt.assert_in("hello", matches)
745 nt.assert_in("hello", matches)
747 nt.assert_in("world", matches)
746 nt.assert_in("world", matches)
748
747
749
748
750 def test_dict_key_completion_invalids():
749 def test_dict_key_completion_invalids():
751 """Smoke test cases dict key completion can't handle"""
750 """Smoke test cases dict key completion can't handle"""
752 ip = get_ipython()
751 ip = get_ipython()
753 complete = ip.Completer.complete
752 complete = ip.Completer.complete
754
753
755 ip.user_ns['no_getitem'] = None
754 ip.user_ns['no_getitem'] = None
756 ip.user_ns['no_keys'] = []
755 ip.user_ns['no_keys'] = []
757 ip.user_ns['cant_call_keys'] = dict
756 ip.user_ns['cant_call_keys'] = dict
758 ip.user_ns['empty'] = {}
757 ip.user_ns['empty'] = {}
759 ip.user_ns['d'] = {'abc': 5}
758 ip.user_ns['d'] = {'abc': 5}
760
759
761 _, matches = complete(line_buffer="no_getitem['")
760 _, matches = complete(line_buffer="no_getitem['")
762 _, matches = complete(line_buffer="no_keys['")
761 _, matches = complete(line_buffer="no_keys['")
763 _, matches = complete(line_buffer="cant_call_keys['")
762 _, matches = complete(line_buffer="cant_call_keys['")
764 _, matches = complete(line_buffer="empty['")
763 _, matches = complete(line_buffer="empty['")
765 _, matches = complete(line_buffer="name_error['")
764 _, matches = complete(line_buffer="name_error['")
766 _, matches = complete(line_buffer="d['\\") # incomplete escape
765 _, matches = complete(line_buffer="d['\\") # incomplete escape
767
766
768 class KeyCompletable(object):
767 class KeyCompletable(object):
769 def __init__(self, things=()):
768 def __init__(self, things=()):
770 self.things = things
769 self.things = things
771
770
772 def _ipython_key_completions_(self):
771 def _ipython_key_completions_(self):
773 return list(self.things)
772 return list(self.things)
774
773
775 def test_object_key_completion():
774 def test_object_key_completion():
776 ip = get_ipython()
775 ip = get_ipython()
777 ip.user_ns['key_completable'] = KeyCompletable(['qwerty', 'qwick'])
776 ip.user_ns['key_completable'] = KeyCompletable(['qwerty', 'qwick'])
778
777
779 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
778 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
780 nt.assert_in('qwerty', matches)
779 nt.assert_in('qwerty', matches)
781 nt.assert_in('qwick', matches)
780 nt.assert_in('qwick', matches)
782
781
783
782
784 def test_aimport_module_completer():
783 def test_aimport_module_completer():
785 ip = get_ipython()
784 ip = get_ipython()
786 _, matches = ip.complete('i', '%aimport i')
785 _, matches = ip.complete('i', '%aimport i')
787 nt.assert_in('io', matches)
786 nt.assert_in('io', matches)
788 nt.assert_not_in('int', matches)
787 nt.assert_not_in('int', matches)
789
788
790 def test_nested_import_module_completer():
789 def test_nested_import_module_completer():
791 ip = get_ipython()
790 ip = get_ipython()
792 _, matches = ip.complete(None, 'import IPython.co', 17)
791 _, matches = ip.complete(None, 'import IPython.co', 17)
793 nt.assert_in('IPython.core', matches)
792 nt.assert_in('IPython.core', matches)
794 nt.assert_not_in('import IPython.core', matches)
793 nt.assert_not_in('import IPython.core', matches)
795 nt.assert_not_in('IPython.display', matches)
794 nt.assert_not_in('IPython.display', matches)
796
795
797 def test_import_module_completer():
796 def test_import_module_completer():
798 ip = get_ipython()
797 ip = get_ipython()
799 _, matches = ip.complete('i', 'import i')
798 _, matches = ip.complete('i', 'import i')
800 nt.assert_in('io', matches)
799 nt.assert_in('io', matches)
801 nt.assert_not_in('int', matches)
800 nt.assert_not_in('int', matches)
802
801
803 def test_from_module_completer():
802 def test_from_module_completer():
804 ip = get_ipython()
803 ip = get_ipython()
805 _, matches = ip.complete('B', 'from io import B', 16)
804 _, matches = ip.complete('B', 'from io import B', 16)
806 nt.assert_in('BytesIO', matches)
805 nt.assert_in('BytesIO', matches)
807 nt.assert_not_in('BaseException', matches)
806 nt.assert_not_in('BaseException', matches)
@@ -1,452 +1,456 b''
1 """Tests for the object inspection functionality.
1 """Tests for the object inspection functionality.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 import os
9 import os
10 import re
10 import re
11 import sys
11 import sys
12
12
13 import nose.tools as nt
13 import nose.tools as nt
14
14
15 from .. import oinspect
15 from .. import oinspect
16 from IPython.core.magic import (Magics, magics_class, line_magic,
16 from IPython.core.magic import (Magics, magics_class, line_magic,
17 cell_magic, line_cell_magic,
17 cell_magic, line_cell_magic,
18 register_line_magic, register_cell_magic,
18 register_line_magic, register_cell_magic,
19 register_line_cell_magic)
19 register_line_cell_magic)
20 from decorator import decorator
20 from decorator import decorator
21 from IPython.testing.decorators import skipif
21 from IPython.testing.decorators import skipif
22 from IPython.testing.tools import AssertPrints
22 from IPython.testing.tools import AssertPrints
23 from IPython.utils.path import compress_user
23 from IPython.utils.path import compress_user
24 from IPython.utils import py3compat
24 from IPython.utils import py3compat
25 from IPython.utils.signatures import Signature, Parameter
25 from IPython.utils.signatures import Signature, Parameter
26
26
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Globals and constants
29 # Globals and constants
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32 inspector = oinspect.Inspector()
32 inspector = oinspect.Inspector()
33 ip = get_ipython()
33 ip = get_ipython()
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Local utilities
36 # Local utilities
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 # WARNING: since this test checks the line number where a function is
39 # WARNING: since this test checks the line number where a function is
40 # defined, if any code is inserted above, the following line will need to be
40 # defined, if any code is inserted above, the following line will need to be
41 # updated. Do NOT insert any whitespace between the next line and the function
41 # updated. Do NOT insert any whitespace between the next line and the function
42 # definition below.
42 # definition below.
43 THIS_LINE_NUMBER = 43 # Put here the actual number of this line
43 THIS_LINE_NUMBER = 43 # Put here the actual number of this line
44 def test_find_source_lines():
44
45 nt.assert_equal(oinspect.find_source_lines(test_find_source_lines),
45 from unittest import TestCase
46 THIS_LINE_NUMBER+1)
46
47 class Test(TestCase):
48
49 def test_find_source_lines(self):
50 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
51 THIS_LINE_NUMBER+6)
47
52
48
53
49 # A couple of utilities to ensure these tests work the same from a source or a
54 # A couple of utilities to ensure these tests work the same from a source or a
50 # binary install
55 # binary install
51 def pyfile(fname):
56 def pyfile(fname):
52 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
57 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
53
58
54
59
55 def match_pyfiles(f1, f2):
60 def match_pyfiles(f1, f2):
56 nt.assert_equal(pyfile(f1), pyfile(f2))
61 nt.assert_equal(pyfile(f1), pyfile(f2))
57
62
58
63
59 def test_find_file():
64 def test_find_file():
60 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
65 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
61
66
62
67
63 def test_find_file_decorated1():
68 def test_find_file_decorated1():
64
69
65 @decorator
70 @decorator
66 def noop1(f):
71 def noop1(f):
67 def wrapper():
72 def wrapper():
68 return f(*a, **kw)
73 return f(*a, **kw)
69 return wrapper
74 return wrapper
70
75
71 @noop1
76 @noop1
72 def f(x):
77 def f(x):
73 "My docstring"
78 "My docstring"
74
79
75 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
80 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
76 nt.assert_equal(f.__doc__, "My docstring")
81 nt.assert_equal(f.__doc__, "My docstring")
77
82
78
83
79 def test_find_file_decorated2():
84 def test_find_file_decorated2():
80
85
81 @decorator
86 @decorator
82 def noop2(f, *a, **kw):
87 def noop2(f, *a, **kw):
83 return f(*a, **kw)
88 return f(*a, **kw)
84
89
85 @noop2
90 @noop2
86 @noop2
91 @noop2
87 @noop2
92 @noop2
88 def f(x):
93 def f(x):
89 "My docstring 2"
94 "My docstring 2"
90
95
91 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
96 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
92 nt.assert_equal(f.__doc__, "My docstring 2")
97 nt.assert_equal(f.__doc__, "My docstring 2")
93
98
94
99
95 def test_find_file_magic():
100 def test_find_file_magic():
96 run = ip.find_line_magic('run')
101 run = ip.find_line_magic('run')
97 nt.assert_not_equal(oinspect.find_file(run), None)
102 nt.assert_not_equal(oinspect.find_file(run), None)
98
103
99
104
100 # A few generic objects we can then inspect in the tests below
105 # A few generic objects we can then inspect in the tests below
101
106
102 class Call(object):
107 class Call(object):
103 """This is the class docstring."""
108 """This is the class docstring."""
104
109
105 def __init__(self, x, y=1):
110 def __init__(self, x, y=1):
106 """This is the constructor docstring."""
111 """This is the constructor docstring."""
107
112
108 def __call__(self, *a, **kw):
113 def __call__(self, *a, **kw):
109 """This is the call docstring."""
114 """This is the call docstring."""
110
115
111 def method(self, x, z=2):
116 def method(self, x, z=2):
112 """Some method's docstring"""
117 """Some method's docstring"""
113
118
114 class HasSignature(object):
119 class HasSignature(object):
115 """This is the class docstring."""
120 """This is the class docstring."""
116 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
121 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
117
122
118 def __init__(self, *args):
123 def __init__(self, *args):
119 """This is the init docstring"""
124 """This is the init docstring"""
120
125
121
126
122 class SimpleClass(object):
127 class SimpleClass(object):
123 def method(self, x, z=2):
128 def method(self, x, z=2):
124 """Some method's docstring"""
129 """Some method's docstring"""
125
130
126
131
127 class OldStyle:
132 class OldStyle:
128 """An old-style class for testing."""
133 """An old-style class for testing."""
129 pass
134 pass
130
135
131
136
132 def f(x, y=2, *a, **kw):
137 def f(x, y=2, *a, **kw):
133 """A simple function."""
138 """A simple function."""
134
139
135
140
136 def g(y, z=3, *a, **kw):
141 def g(y, z=3, *a, **kw):
137 pass # no docstring
142 pass # no docstring
138
143
139
144
140 @register_line_magic
145 @register_line_magic
141 def lmagic(line):
146 def lmagic(line):
142 "A line magic"
147 "A line magic"
143
148
144
149
145 @register_cell_magic
150 @register_cell_magic
146 def cmagic(line, cell):
151 def cmagic(line, cell):
147 "A cell magic"
152 "A cell magic"
148
153
149
154
150 @register_line_cell_magic
155 @register_line_cell_magic
151 def lcmagic(line, cell=None):
156 def lcmagic(line, cell=None):
152 "A line/cell magic"
157 "A line/cell magic"
153
158
154
159
155 @magics_class
160 @magics_class
156 class SimpleMagics(Magics):
161 class SimpleMagics(Magics):
157 @line_magic
162 @line_magic
158 def Clmagic(self, cline):
163 def Clmagic(self, cline):
159 "A class-based line magic"
164 "A class-based line magic"
160
165
161 @cell_magic
166 @cell_magic
162 def Ccmagic(self, cline, ccell):
167 def Ccmagic(self, cline, ccell):
163 "A class-based cell magic"
168 "A class-based cell magic"
164
169
165 @line_cell_magic
170 @line_cell_magic
166 def Clcmagic(self, cline, ccell=None):
171 def Clcmagic(self, cline, ccell=None):
167 "A class-based line/cell magic"
172 "A class-based line/cell magic"
168
173
169
174
170 class Awkward(object):
175 class Awkward(object):
171 def __getattr__(self, name):
176 def __getattr__(self, name):
172 raise Exception(name)
177 raise Exception(name)
173
178
174 class NoBoolCall:
179 class NoBoolCall:
175 """
180 """
176 callable with `__bool__` raising should still be inspect-able.
181 callable with `__bool__` raising should still be inspect-able.
177 """
182 """
178
183
179 def __call__(self):
184 def __call__(self):
180 """does nothing"""
185 """does nothing"""
181 pass
186 pass
182
187
183 def __bool__(self):
188 def __bool__(self):
184 """just raise NotImplemented"""
189 """just raise NotImplemented"""
185 raise NotImplementedError('Must be implemented')
190 raise NotImplementedError('Must be implemented')
186
191
187
192
188 class SerialLiar(object):
193 class SerialLiar(object):
189 """Attribute accesses always get another copy of the same class.
194 """Attribute accesses always get another copy of the same class.
190
195
191 unittest.mock.call does something similar, but it's not ideal for testing
196 unittest.mock.call does something similar, but it's not ideal for testing
192 as the failure mode is to eat all your RAM. This gives up after 10k levels.
197 as the failure mode is to eat all your RAM. This gives up after 10k levels.
193 """
198 """
194 def __init__(self, max_fibbing_twig, lies_told=0):
199 def __init__(self, max_fibbing_twig, lies_told=0):
195 if lies_told > 10000:
200 if lies_told > 10000:
196 raise RuntimeError('Nose too long, honesty is the best policy')
201 raise RuntimeError('Nose too long, honesty is the best policy')
197 self.max_fibbing_twig = max_fibbing_twig
202 self.max_fibbing_twig = max_fibbing_twig
198 self.lies_told = lies_told
203 self.lies_told = lies_told
199 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
204 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
200
205
201 def __getattr__(self, item):
206 def __getattr__(self, item):
202 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
207 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
203
208
204
209
205 def check_calltip(obj, name, call, docstring):
210 def check_calltip(obj, name, call, docstring):
206 """Generic check pattern all calltip tests will use"""
211 """Generic check pattern all calltip tests will use"""
207 info = inspector.info(obj, name)
212 info = inspector.info(obj, name)
208 call_line, ds = oinspect.call_tip(info)
213 call_line, ds = oinspect.call_tip(info)
209 nt.assert_equal(call_line, call)
214 nt.assert_equal(call_line, call)
210 nt.assert_equal(ds, docstring)
215 nt.assert_equal(ds, docstring)
211
216
212 #-----------------------------------------------------------------------------
217 #-----------------------------------------------------------------------------
213 # Tests
218 # Tests
214 #-----------------------------------------------------------------------------
219 #-----------------------------------------------------------------------------
215
220
216 def test_calltip_class():
221 def test_calltip_class():
217 check_calltip(Call, 'Call', 'Call(x, y=1)', Call.__init__.__doc__)
222 check_calltip(Call, 'Call', 'Call(x, y=1)', Call.__init__.__doc__)
218
223
219
224
220 def test_calltip_instance():
225 def test_calltip_instance():
221 c = Call(1)
226 c = Call(1)
222 check_calltip(c, 'c', 'c(*a, **kw)', c.__call__.__doc__)
227 check_calltip(c, 'c', 'c(*a, **kw)', c.__call__.__doc__)
223
228
224
229
225 def test_calltip_method():
230 def test_calltip_method():
226 c = Call(1)
231 c = Call(1)
227 check_calltip(c.method, 'c.method', 'c.method(x, z=2)', c.method.__doc__)
232 check_calltip(c.method, 'c.method', 'c.method(x, z=2)', c.method.__doc__)
228
233
229
234
230 def test_calltip_function():
235 def test_calltip_function():
231 check_calltip(f, 'f', 'f(x, y=2, *a, **kw)', f.__doc__)
236 check_calltip(f, 'f', 'f(x, y=2, *a, **kw)', f.__doc__)
232
237
233
238
234 def test_calltip_function2():
239 def test_calltip_function2():
235 check_calltip(g, 'g', 'g(y, z=3, *a, **kw)', '<no docstring>')
240 check_calltip(g, 'g', 'g(y, z=3, *a, **kw)', '<no docstring>')
236
241
237
242
238 @skipif(sys.version_info >= (3, 5))
243 @skipif(sys.version_info >= (3, 5))
239 def test_calltip_builtin():
244 def test_calltip_builtin():
240 check_calltip(sum, 'sum', None, sum.__doc__)
245 check_calltip(sum, 'sum', None, sum.__doc__)
241
246
242
247
243 def test_calltip_line_magic():
248 def test_calltip_line_magic():
244 check_calltip(lmagic, 'lmagic', 'lmagic(line)', "A line magic")
249 check_calltip(lmagic, 'lmagic', 'lmagic(line)', "A line magic")
245
250
246
251
247 def test_calltip_cell_magic():
252 def test_calltip_cell_magic():
248 check_calltip(cmagic, 'cmagic', 'cmagic(line, cell)', "A cell magic")
253 check_calltip(cmagic, 'cmagic', 'cmagic(line, cell)', "A cell magic")
249
254
250
255
251 def test_calltip_line_cell_magic():
256 def test_calltip_line_cell_magic():
252 check_calltip(lcmagic, 'lcmagic', 'lcmagic(line, cell=None)',
257 check_calltip(lcmagic, 'lcmagic', 'lcmagic(line, cell=None)',
253 "A line/cell magic")
258 "A line/cell magic")
254
259
255
260
256 def test_class_magics():
261 def test_class_magics():
257 cm = SimpleMagics(ip)
262 cm = SimpleMagics(ip)
258 ip.register_magics(cm)
263 ip.register_magics(cm)
259 check_calltip(cm.Clmagic, 'Clmagic', 'Clmagic(cline)',
264 check_calltip(cm.Clmagic, 'Clmagic', 'Clmagic(cline)',
260 "A class-based line magic")
265 "A class-based line magic")
261 check_calltip(cm.Ccmagic, 'Ccmagic', 'Ccmagic(cline, ccell)',
266 check_calltip(cm.Ccmagic, 'Ccmagic', 'Ccmagic(cline, ccell)',
262 "A class-based cell magic")
267 "A class-based cell magic")
263 check_calltip(cm.Clcmagic, 'Clcmagic', 'Clcmagic(cline, ccell=None)',
268 check_calltip(cm.Clcmagic, 'Clcmagic', 'Clcmagic(cline, ccell=None)',
264 "A class-based line/cell magic")
269 "A class-based line/cell magic")
265
270
266
271
267 def test_info():
272 def test_info():
268 "Check that Inspector.info fills out various fields as expected."
273 "Check that Inspector.info fills out various fields as expected."
269 i = inspector.info(Call, oname='Call')
274 i = inspector.info(Call, oname='Call')
270 nt.assert_equal(i['type_name'], 'type')
275 nt.assert_equal(i['type_name'], 'type')
271 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
276 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
272 nt.assert_equal(i['base_class'], expted_class)
277 nt.assert_equal(i['base_class'], expted_class)
273 if sys.version_info > (3,):
278 if sys.version_info > (3,):
274 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
279 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
275 fname = __file__
280 fname = __file__
276 if fname.endswith(".pyc"):
281 if fname.endswith(".pyc"):
277 fname = fname[:-1]
282 fname = fname[:-1]
278 # case-insensitive comparison needed on some filesystems
283 # case-insensitive comparison needed on some filesystems
279 # e.g. Windows:
284 # e.g. Windows:
280 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
285 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
281 nt.assert_equal(i['definition'], None)
286 nt.assert_equal(i['definition'], None)
282 nt.assert_equal(i['docstring'], Call.__doc__)
287 nt.assert_equal(i['docstring'], Call.__doc__)
283 nt.assert_equal(i['source'], None)
288 nt.assert_equal(i['source'], None)
284 nt.assert_true(i['isclass'])
289 nt.assert_true(i['isclass'])
285 _self_py2 = '' if py3compat.PY3 else 'self, '
290 _self_py2 = '' if py3compat.PY3 else 'self, '
286 nt.assert_equal(i['init_definition'], "Call(%sx, y=1)" % _self_py2)
291 nt.assert_equal(i['init_definition'], "Call(%sx, y=1)" % _self_py2)
287 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
292 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
288
293
289 i = inspector.info(Call, detail_level=1)
294 i = inspector.info(Call, detail_level=1)
290 nt.assert_not_equal(i['source'], None)
295 nt.assert_not_equal(i['source'], None)
291 nt.assert_equal(i['docstring'], None)
296 nt.assert_equal(i['docstring'], None)
292
297
293 c = Call(1)
298 c = Call(1)
294 c.__doc__ = "Modified instance docstring"
299 c.__doc__ = "Modified instance docstring"
295 i = inspector.info(c)
300 i = inspector.info(c)
296 nt.assert_equal(i['type_name'], 'Call')
301 nt.assert_equal(i['type_name'], 'Call')
297 nt.assert_equal(i['docstring'], "Modified instance docstring")
302 nt.assert_equal(i['docstring'], "Modified instance docstring")
298 nt.assert_equal(i['class_docstring'], Call.__doc__)
303 nt.assert_equal(i['class_docstring'], Call.__doc__)
299 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
304 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
300 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
305 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
301
306
302 # Test old-style classes, which for example may not have an __init__ method.
307 # Test old-style classes, which for example may not have an __init__ method.
303 if not py3compat.PY3:
308 if not py3compat.PY3:
304 i = inspector.info(OldStyle)
309 i = inspector.info(OldStyle)
305 nt.assert_equal(i['type_name'], 'classobj')
310 nt.assert_equal(i['type_name'], 'classobj')
306
311
307 i = inspector.info(OldStyle())
312 i = inspector.info(OldStyle())
308 nt.assert_equal(i['type_name'], 'instance')
313 nt.assert_equal(i['type_name'], 'instance')
309 nt.assert_equal(i['docstring'], OldStyle.__doc__)
314 nt.assert_equal(i['docstring'], OldStyle.__doc__)
310
315
311 def test_class_signature():
316 def test_class_signature():
312 info = inspector.info(HasSignature, 'HasSignature')
317 info = inspector.info(HasSignature, 'HasSignature')
313 nt.assert_equal(info['init_definition'], "HasSignature(test)")
318 nt.assert_equal(info['init_definition'], "HasSignature(test)")
314 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
319 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
315
320
316 def test_info_awkward():
321 def test_info_awkward():
317 # Just test that this doesn't throw an error.
322 # Just test that this doesn't throw an error.
318 inspector.info(Awkward())
323 inspector.info(Awkward())
319
324
320 def test_bool_raise():
325 def test_bool_raise():
321 inspector.info(NoBoolCall())
326 inspector.info(NoBoolCall())
322
327
323 def test_info_serialliar():
328 def test_info_serialliar():
324 fib_tracker = [0]
329 fib_tracker = [0]
325 i = inspector.info(SerialLiar(fib_tracker))
330 inspector.info(SerialLiar(fib_tracker))
326
331
327 # Nested attribute access should be cut off at 100 levels deep to avoid
332 # Nested attribute access should be cut off at 100 levels deep to avoid
328 # infinite loops: https://github.com/ipython/ipython/issues/9122
333 # infinite loops: https://github.com/ipython/ipython/issues/9122
329 nt.assert_less(fib_tracker[0], 9000)
334 nt.assert_less(fib_tracker[0], 9000)
330
335
331 def test_calldef_none():
336 def test_calldef_none():
332 # We should ignore __call__ for all of these.
337 # We should ignore __call__ for all of these.
333 for obj in [f, SimpleClass().method, any, str.upper]:
338 for obj in [f, SimpleClass().method, any, str.upper]:
334 print(obj)
339 print(obj)
335 i = inspector.info(obj)
340 i = inspector.info(obj)
336 nt.assert_is(i['call_def'], None)
341 nt.assert_is(i['call_def'], None)
337
342
338 if py3compat.PY3:
343 def f_kwarg(pos, *, kwonly):
339 exec("def f_kwarg(pos, *, kwonly): pass")
344 pass
340
345
341 @skipif(not py3compat.PY3)
342 def test_definition_kwonlyargs():
346 def test_definition_kwonlyargs():
343 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
347 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
344 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
348 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
345
349
346 def test_getdoc():
350 def test_getdoc():
347 class A(object):
351 class A(object):
348 """standard docstring"""
352 """standard docstring"""
349 pass
353 pass
350
354
351 class B(object):
355 class B(object):
352 """standard docstring"""
356 """standard docstring"""
353 def getdoc(self):
357 def getdoc(self):
354 return "custom docstring"
358 return "custom docstring"
355
359
356 class C(object):
360 class C(object):
357 """standard docstring"""
361 """standard docstring"""
358 def getdoc(self):
362 def getdoc(self):
359 return None
363 return None
360
364
361 a = A()
365 a = A()
362 b = B()
366 b = B()
363 c = C()
367 c = C()
364
368
365 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
369 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
366 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
370 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
367 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
371 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
368
372
369
373
370 def test_empty_property_has_no_source():
374 def test_empty_property_has_no_source():
371 i = inspector.info(property(), detail_level=1)
375 i = inspector.info(property(), detail_level=1)
372 nt.assert_is(i['source'], None)
376 nt.assert_is(i['source'], None)
373
377
374
378
375 def test_property_sources():
379 def test_property_sources():
376 import zlib
380 import zlib
377
381
378 class A(object):
382 class A(object):
379 @property
383 @property
380 def foo(self):
384 def foo(self):
381 return 'bar'
385 return 'bar'
382
386
383 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
387 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
384
388
385 id = property(id)
389 id = property(id)
386 compress = property(zlib.compress)
390 compress = property(zlib.compress)
387
391
388 i = inspector.info(A.foo, detail_level=1)
392 i = inspector.info(A.foo, detail_level=1)
389 nt.assert_in('def foo(self):', i['source'])
393 nt.assert_in('def foo(self):', i['source'])
390 nt.assert_in('lambda self, v:', i['source'])
394 nt.assert_in('lambda self, v:', i['source'])
391
395
392 i = inspector.info(A.id, detail_level=1)
396 i = inspector.info(A.id, detail_level=1)
393 nt.assert_in('fget = <function id>', i['source'])
397 nt.assert_in('fget = <function id>', i['source'])
394
398
395 i = inspector.info(A.compress, detail_level=1)
399 i = inspector.info(A.compress, detail_level=1)
396 nt.assert_in('fget = <function zlib.compress>', i['source'])
400 nt.assert_in('fget = <function zlib.compress>', i['source'])
397
401
398
402
399 def test_property_docstring_is_in_info_for_detail_level_0():
403 def test_property_docstring_is_in_info_for_detail_level_0():
400 class A(object):
404 class A(object):
401 @property
405 @property
402 def foobar(self):
406 def foobar(self):
403 """This is `foobar` property."""
407 """This is `foobar` property."""
404 pass
408 pass
405
409
406 ip.user_ns['a_obj'] = A()
410 ip.user_ns['a_obj'] = A()
407 nt.assert_equals(
411 nt.assert_equals(
408 'This is `foobar` property.',
412 'This is `foobar` property.',
409 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
413 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
410
414
411 ip.user_ns['a_cls'] = A
415 ip.user_ns['a_cls'] = A
412 nt.assert_equals(
416 nt.assert_equals(
413 'This is `foobar` property.',
417 'This is `foobar` property.',
414 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
418 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
415
419
416
420
417 def test_pdef():
421 def test_pdef():
418 # See gh-1914
422 # See gh-1914
419 def foo(): pass
423 def foo(): pass
420 inspector.pdef(foo, 'foo')
424 inspector.pdef(foo, 'foo')
421
425
422
426
423 def test_pinfo_nonascii():
427 def test_pinfo_nonascii():
424 # See gh-1177
428 # See gh-1177
425 from . import nonascii2
429 from . import nonascii2
426 ip.user_ns['nonascii2'] = nonascii2
430 ip.user_ns['nonascii2'] = nonascii2
427 ip._inspect('pinfo', 'nonascii2', detail_level=1)
431 ip._inspect('pinfo', 'nonascii2', detail_level=1)
428
432
429
433
430 def test_pinfo_magic():
434 def test_pinfo_magic():
431 with AssertPrints('Docstring:'):
435 with AssertPrints('Docstring:'):
432 ip._inspect('pinfo', 'lsmagic', detail_level=0)
436 ip._inspect('pinfo', 'lsmagic', detail_level=0)
433
437
434 with AssertPrints('Source:'):
438 with AssertPrints('Source:'):
435 ip._inspect('pinfo', 'lsmagic', detail_level=1)
439 ip._inspect('pinfo', 'lsmagic', detail_level=1)
436
440
437
441
438 def test_init_colors():
442 def test_init_colors():
439 # ensure colors are not present in signature info
443 # ensure colors are not present in signature info
440 info = inspector.info(HasSignature)
444 info = inspector.info(HasSignature)
441 init_def = info['init_definition']
445 init_def = info['init_definition']
442 nt.assert_not_in('[0m', init_def)
446 nt.assert_not_in('[0m', init_def)
443
447
444
448
445 def test_builtin_init():
449 def test_builtin_init():
446 info = inspector.info(list)
450 info = inspector.info(list)
447 init_def = info['init_definition']
451 init_def = info['init_definition']
448 # Python < 3.4 can't get init definition from builtins,
452 # Python < 3.4 can't get init definition from builtins,
449 # but still exercise the inspection in case of error-raising bugs.
453 # but still exercise the inspection in case of error-raising bugs.
450 if sys.version_info >= (3,4):
454 if sys.version_info >= (3,4):
451 nt.assert_is_not_none(init_def)
455 nt.assert_is_not_none(init_def)
452
456
@@ -1,1500 +1,1489 b''
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 .. note::
41 .. note::
42
42
43 The verbose mode print all variables in the stack, which means it can
43 The verbose mode print all variables in the stack, which means it can
44 potentially leak sensitive information like access keys, or unencryted
44 potentially leak sensitive information like access keys, or unencryted
45 password.
45 password.
46
46
47 Installation instructions for VerboseTB::
47 Installation instructions for VerboseTB::
48
48
49 import sys,ultratb
49 import sys,ultratb
50 sys.excepthook = ultratb.VerboseTB()
50 sys.excepthook = ultratb.VerboseTB()
51
51
52 Note: Much of the code in this module was lifted verbatim from the standard
52 Note: Much of the code in this module was lifted verbatim from the standard
53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
54
54
55 Color schemes
55 Color schemes
56 -------------
56 -------------
57
57
58 The colors are defined in the class TBTools through the use of the
58 The colors are defined in the class TBTools through the use of the
59 ColorSchemeTable class. Currently the following exist:
59 ColorSchemeTable class. Currently the following exist:
60
60
61 - NoColor: allows all of this module to be used in any terminal (the color
61 - NoColor: allows all of this module to be used in any terminal (the color
62 escapes are just dummy blank strings).
62 escapes are just dummy blank strings).
63
63
64 - Linux: is meant to look good in a terminal like the Linux console (black
64 - Linux: is meant to look good in a terminal like the Linux console (black
65 or very dark background).
65 or very dark background).
66
66
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
68 in light background terminals.
68 in light background terminals.
69
69
70 - Neutral: a neutral color scheme that should be readable on both light and
70 - Neutral: a neutral color scheme that should be readable on both light and
71 dark background
71 dark background
72
72
73 You can implement other color schemes easily, the syntax is fairly
73 You can implement other color schemes easily, the syntax is fairly
74 self-explanatory. Please send back new schemes you develop to the author for
74 self-explanatory. Please send back new schemes you develop to the author for
75 possible inclusion in future releases.
75 possible inclusion in future releases.
76
76
77 Inheritance diagram:
77 Inheritance diagram:
78
78
79 .. inheritance-diagram:: IPython.core.ultratb
79 .. inheritance-diagram:: IPython.core.ultratb
80 :parts: 3
80 :parts: 3
81 """
81 """
82
82
83 #*****************************************************************************
83 #*****************************************************************************
84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
86 #
86 #
87 # Distributed under the terms of the BSD License. The full license is in
87 # Distributed under the terms of the BSD License. The full license is in
88 # the file COPYING, distributed as part of this software.
88 # the file COPYING, distributed as part of this software.
89 #*****************************************************************************
89 #*****************************************************************************
90
90
91 from __future__ import absolute_import
91 from __future__ import absolute_import
92 from __future__ import unicode_literals
92 from __future__ import unicode_literals
93 from __future__ import print_function
93 from __future__ import print_function
94
94
95 import dis
95 import dis
96 import inspect
96 import inspect
97 import keyword
97 import keyword
98 import linecache
98 import linecache
99 import os
99 import os
100 import pydoc
100 import pydoc
101 import re
101 import re
102 import sys
102 import sys
103 import time
103 import time
104 import tokenize
104 import tokenize
105 import traceback
105 import traceback
106 import types
106 import types
107
107
108 try: # Python 2
108 try: # Python 2
109 generate_tokens = tokenize.generate_tokens
109 generate_tokens = tokenize.generate_tokens
110 except AttributeError: # Python 3
110 except AttributeError: # Python 3
111 generate_tokens = tokenize.tokenize
111 generate_tokens = tokenize.tokenize
112
112
113 # For purposes of monkeypatching inspect to fix a bug in it.
113 # For purposes of monkeypatching inspect to fix a bug in it.
114 from inspect import getsourcefile, getfile, getmodule, \
114 from inspect import getsourcefile, getfile, getmodule, \
115 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
115 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
116
116
117 # IPython's own modules
117 # IPython's own modules
118 from IPython import get_ipython
118 from IPython import get_ipython
119 from IPython.core import debugger
119 from IPython.core import debugger
120 from IPython.core.display_trap import DisplayTrap
120 from IPython.core.display_trap import DisplayTrap
121 from IPython.core.excolors import exception_colors
121 from IPython.core.excolors import exception_colors
122 from IPython.utils import PyColorize
122 from IPython.utils import PyColorize
123 from IPython.utils import openpy
123 from IPython.utils import openpy
124 from IPython.utils import path as util_path
124 from IPython.utils import path as util_path
125 from IPython.utils import py3compat
125 from IPython.utils import py3compat
126 from IPython.utils import ulinecache
126 from IPython.utils import ulinecache
127 from IPython.utils.data import uniq_stable
127 from IPython.utils.data import uniq_stable
128 from IPython.utils.terminal import get_terminal_size
128 from IPython.utils.terminal import get_terminal_size
129 from logging import info, error
129 from logging import info, error
130
130
131 import IPython.utils.colorable as colorable
131 import IPython.utils.colorable as colorable
132
132
133 # Globals
133 # Globals
134 # amount of space to put line numbers before verbose tracebacks
134 # amount of space to put line numbers before verbose tracebacks
135 INDENT_SIZE = 8
135 INDENT_SIZE = 8
136
136
137 # Default color scheme. This is used, for example, by the traceback
137 # Default color scheme. This is used, for example, by the traceback
138 # formatter. When running in an actual IPython instance, the user's rc.colors
138 # formatter. When running in an actual IPython instance, the user's rc.colors
139 # value is used, but having a module global makes this functionality available
139 # value is used, but having a module global makes this functionality available
140 # to users of ultratb who are NOT running inside ipython.
140 # to users of ultratb who are NOT running inside ipython.
141 DEFAULT_SCHEME = 'NoColor'
141 DEFAULT_SCHEME = 'NoColor'
142
142
143 # ---------------------------------------------------------------------------
143 # ---------------------------------------------------------------------------
144 # Code begins
144 # Code begins
145
145
146 # Utility functions
146 # Utility functions
147 def inspect_error():
147 def inspect_error():
148 """Print a message about internal inspect errors.
148 """Print a message about internal inspect errors.
149
149
150 These are unfortunately quite common."""
150 These are unfortunately quite common."""
151
151
152 error('Internal Python error in the inspect module.\n'
152 error('Internal Python error in the inspect module.\n'
153 'Below is the traceback from this internal error.\n')
153 'Below is the traceback from this internal error.\n')
154
154
155
155
156 # This function is a monkeypatch we apply to the Python inspect module. We have
156 # This function is a monkeypatch we apply to the Python inspect module. We have
157 # now found when it's needed (see discussion on issue gh-1456), and we have a
157 # now found when it's needed (see discussion on issue gh-1456), and we have a
158 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
158 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
159 # the monkeypatch is not applied. TK, Aug 2012.
159 # the monkeypatch is not applied. TK, Aug 2012.
160 def findsource(object):
160 def findsource(object):
161 """Return the entire source file and starting line number for an object.
161 """Return the entire source file and starting line number for an object.
162
162
163 The argument may be a module, class, method, function, traceback, frame,
163 The argument may be a module, class, method, function, traceback, frame,
164 or code object. The source code is returned as a list of all the lines
164 or code object. The source code is returned as a list of all the lines
165 in the file and the line number indexes a line in that list. An IOError
165 in the file and the line number indexes a line in that list. An IOError
166 is raised if the source code cannot be retrieved.
166 is raised if the source code cannot be retrieved.
167
167
168 FIXED version with which we monkeypatch the stdlib to work around a bug."""
168 FIXED version with which we monkeypatch the stdlib to work around a bug."""
169
169
170 file = getsourcefile(object) or getfile(object)
170 file = getsourcefile(object) or getfile(object)
171 # If the object is a frame, then trying to get the globals dict from its
171 # If the object is a frame, then trying to get the globals dict from its
172 # module won't work. Instead, the frame object itself has the globals
172 # module won't work. Instead, the frame object itself has the globals
173 # dictionary.
173 # dictionary.
174 globals_dict = None
174 globals_dict = None
175 if inspect.isframe(object):
175 if inspect.isframe(object):
176 # XXX: can this ever be false?
176 # XXX: can this ever be false?
177 globals_dict = object.f_globals
177 globals_dict = object.f_globals
178 else:
178 else:
179 module = getmodule(object, file)
179 module = getmodule(object, file)
180 if module:
180 if module:
181 globals_dict = module.__dict__
181 globals_dict = module.__dict__
182 lines = linecache.getlines(file, globals_dict)
182 lines = linecache.getlines(file, globals_dict)
183 if not lines:
183 if not lines:
184 raise IOError('could not get source code')
184 raise IOError('could not get source code')
185
185
186 if ismodule(object):
186 if ismodule(object):
187 return lines, 0
187 return lines, 0
188
188
189 if isclass(object):
189 if isclass(object):
190 name = object.__name__
190 name = object.__name__
191 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
191 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
192 # make some effort to find the best matching class definition:
192 # make some effort to find the best matching class definition:
193 # use the one with the least indentation, which is the one
193 # use the one with the least indentation, which is the one
194 # that's most probably not inside a function definition.
194 # that's most probably not inside a function definition.
195 candidates = []
195 candidates = []
196 for i in range(len(lines)):
196 for i in range(len(lines)):
197 match = pat.match(lines[i])
197 match = pat.match(lines[i])
198 if match:
198 if match:
199 # if it's at toplevel, it's already the best one
199 # if it's at toplevel, it's already the best one
200 if lines[i][0] == 'c':
200 if lines[i][0] == 'c':
201 return lines, i
201 return lines, i
202 # else add whitespace to candidate list
202 # else add whitespace to candidate list
203 candidates.append((match.group(1), i))
203 candidates.append((match.group(1), i))
204 if candidates:
204 if candidates:
205 # this will sort by whitespace, and by line number,
205 # this will sort by whitespace, and by line number,
206 # less whitespace first
206 # less whitespace first
207 candidates.sort()
207 candidates.sort()
208 return lines, candidates[0][1]
208 return lines, candidates[0][1]
209 else:
209 else:
210 raise IOError('could not find class definition')
210 raise IOError('could not find class definition')
211
211
212 if ismethod(object):
212 if ismethod(object):
213 object = object.__func__
213 object = object.__func__
214 if isfunction(object):
214 if isfunction(object):
215 object = object.__code__
215 object = object.__code__
216 if istraceback(object):
216 if istraceback(object):
217 object = object.tb_frame
217 object = object.tb_frame
218 if isframe(object):
218 if isframe(object):
219 object = object.f_code
219 object = object.f_code
220 if iscode(object):
220 if iscode(object):
221 if not hasattr(object, 'co_firstlineno'):
221 if not hasattr(object, 'co_firstlineno'):
222 raise IOError('could not find function definition')
222 raise IOError('could not find function definition')
223 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
223 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
224 pmatch = pat.match
224 pmatch = pat.match
225 # fperez - fix: sometimes, co_firstlineno can give a number larger than
225 # fperez - fix: sometimes, co_firstlineno can give a number larger than
226 # the length of lines, which causes an error. Safeguard against that.
226 # the length of lines, which causes an error. Safeguard against that.
227 lnum = min(object.co_firstlineno, len(lines)) - 1
227 lnum = min(object.co_firstlineno, len(lines)) - 1
228 while lnum > 0:
228 while lnum > 0:
229 if pmatch(lines[lnum]):
229 if pmatch(lines[lnum]):
230 break
230 break
231 lnum -= 1
231 lnum -= 1
232
232
233 return lines, lnum
233 return lines, lnum
234 raise IOError('could not find code object')
234 raise IOError('could not find code object')
235
235
236
236
237 # This is a patched version of inspect.getargs that applies the (unmerged)
237 # This is a patched version of inspect.getargs that applies the (unmerged)
238 # patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes
238 # patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes
239 # https://github.com/ipython/ipython/issues/8205 and
239 # https://github.com/ipython/ipython/issues/8205 and
240 # https://github.com/ipython/ipython/issues/8293
240 # https://github.com/ipython/ipython/issues/8293
241 def getargs(co):
241 def getargs(co):
242 """Get information about the arguments accepted by a code object.
242 """Get information about the arguments accepted by a code object.
243
243
244 Three things are returned: (args, varargs, varkw), where 'args' is
244 Three things are returned: (args, varargs, varkw), where 'args' is
245 a list of argument names (possibly containing nested lists), and
245 a list of argument names (possibly containing nested lists), and
246 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
246 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
247 if not iscode(co):
247 if not iscode(co):
248 raise TypeError('{!r} is not a code object'.format(co))
248 raise TypeError('{!r} is not a code object'.format(co))
249
249
250 nargs = co.co_argcount
250 nargs = co.co_argcount
251 names = co.co_varnames
251 names = co.co_varnames
252 args = list(names[:nargs])
252 args = list(names[:nargs])
253 step = 0
253 step = 0
254
254
255 # The following acrobatics are for anonymous (tuple) arguments.
255 # The following acrobatics are for anonymous (tuple) arguments.
256 for i in range(nargs):
256 for i in range(nargs):
257 if args[i][:1] in ('', '.'):
257 if args[i][:1] in ('', '.'):
258 stack, remain, count = [], [], []
258 stack, remain, count = [], [], []
259 while step < len(co.co_code):
259 while step < len(co.co_code):
260 op = ord(co.co_code[step])
260 op = ord(co.co_code[step])
261 step = step + 1
261 step = step + 1
262 if op >= dis.HAVE_ARGUMENT:
262 if op >= dis.HAVE_ARGUMENT:
263 opname = dis.opname[op]
263 opname = dis.opname[op]
264 value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256
264 value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256
265 step = step + 2
265 step = step + 2
266 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
266 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
267 remain.append(value)
267 remain.append(value)
268 count.append(value)
268 count.append(value)
269 elif opname in ('STORE_FAST', 'STORE_DEREF'):
269 elif opname in ('STORE_FAST', 'STORE_DEREF'):
270 if op in dis.haslocal:
270 if op in dis.haslocal:
271 stack.append(co.co_varnames[value])
271 stack.append(co.co_varnames[value])
272 elif op in dis.hasfree:
272 elif op in dis.hasfree:
273 stack.append((co.co_cellvars + co.co_freevars)[value])
273 stack.append((co.co_cellvars + co.co_freevars)[value])
274 # Special case for sublists of length 1: def foo((bar))
274 # Special case for sublists of length 1: def foo((bar))
275 # doesn't generate the UNPACK_TUPLE bytecode, so if
275 # doesn't generate the UNPACK_TUPLE bytecode, so if
276 # `remain` is empty here, we have such a sublist.
276 # `remain` is empty here, we have such a sublist.
277 if not remain:
277 if not remain:
278 stack[0] = [stack[0]]
278 stack[0] = [stack[0]]
279 break
279 break
280 else:
280 else:
281 remain[-1] = remain[-1] - 1
281 remain[-1] = remain[-1] - 1
282 while remain[-1] == 0:
282 while remain[-1] == 0:
283 remain.pop()
283 remain.pop()
284 size = count.pop()
284 size = count.pop()
285 stack[-size:] = [stack[-size:]]
285 stack[-size:] = [stack[-size:]]
286 if not remain:
286 if not remain:
287 break
287 break
288 remain[-1] = remain[-1] - 1
288 remain[-1] = remain[-1] - 1
289 if not remain:
289 if not remain:
290 break
290 break
291 args[i] = stack[0]
291 args[i] = stack[0]
292
292
293 varargs = None
293 varargs = None
294 if co.co_flags & inspect.CO_VARARGS:
294 if co.co_flags & inspect.CO_VARARGS:
295 varargs = co.co_varnames[nargs]
295 varargs = co.co_varnames[nargs]
296 nargs = nargs + 1
296 nargs = nargs + 1
297 varkw = None
297 varkw = None
298 if co.co_flags & inspect.CO_VARKEYWORDS:
298 if co.co_flags & inspect.CO_VARKEYWORDS:
299 varkw = co.co_varnames[nargs]
299 varkw = co.co_varnames[nargs]
300 return inspect.Arguments(args, varargs, varkw)
300 return inspect.Arguments(args, varargs, varkw)
301
301
302
302
303 # Monkeypatch inspect to apply our bugfix.
303 # Monkeypatch inspect to apply our bugfix.
304 def with_patch_inspect(f):
304 def with_patch_inspect(f):
305 """decorator for monkeypatching inspect.findsource"""
305 """decorator for monkeypatching inspect.findsource"""
306
306
307 def wrapped(*args, **kwargs):
307 def wrapped(*args, **kwargs):
308 save_findsource = inspect.findsource
308 save_findsource = inspect.findsource
309 save_getargs = inspect.getargs
309 save_getargs = inspect.getargs
310 inspect.findsource = findsource
310 inspect.findsource = findsource
311 inspect.getargs = getargs
311 inspect.getargs = getargs
312 try:
312 try:
313 return f(*args, **kwargs)
313 return f(*args, **kwargs)
314 finally:
314 finally:
315 inspect.findsource = save_findsource
315 inspect.findsource = save_findsource
316 inspect.getargs = save_getargs
316 inspect.getargs = save_getargs
317
317
318 return wrapped
318 return wrapped
319
319
320
320
321 if py3compat.PY3:
321 if py3compat.PY3:
322 fixed_getargvalues = inspect.getargvalues
322 fixed_getargvalues = inspect.getargvalues
323 else:
323 else:
324 # Fixes for https://github.com/ipython/ipython/issues/8293
324 # Fixes for https://github.com/ipython/ipython/issues/8293
325 # and https://github.com/ipython/ipython/issues/8205.
325 # and https://github.com/ipython/ipython/issues/8205.
326 # The relevant bug is caused by failure to correctly handle anonymous tuple
326 # The relevant bug is caused by failure to correctly handle anonymous tuple
327 # unpacking, which only exists in Python 2.
327 # unpacking, which only exists in Python 2.
328 fixed_getargvalues = with_patch_inspect(inspect.getargvalues)
328 fixed_getargvalues = with_patch_inspect(inspect.getargvalues)
329
329
330
330
331 def fix_frame_records_filenames(records):
331 def fix_frame_records_filenames(records):
332 """Try to fix the filenames in each record from inspect.getinnerframes().
332 """Try to fix the filenames in each record from inspect.getinnerframes().
333
333
334 Particularly, modules loaded from within zip files have useless filenames
334 Particularly, modules loaded from within zip files have useless filenames
335 attached to their code object, and inspect.getinnerframes() just uses it.
335 attached to their code object, and inspect.getinnerframes() just uses it.
336 """
336 """
337 fixed_records = []
337 fixed_records = []
338 for frame, filename, line_no, func_name, lines, index in records:
338 for frame, filename, line_no, func_name, lines, index in records:
339 # Look inside the frame's globals dictionary for __file__,
339 # Look inside the frame's globals dictionary for __file__,
340 # which should be better. However, keep Cython filenames since
340 # which should be better. However, keep Cython filenames since
341 # we prefer the source filenames over the compiled .so file.
341 # we prefer the source filenames over the compiled .so file.
342 filename = py3compat.cast_unicode_py2(filename, "utf-8")
342 filename = py3compat.cast_unicode_py2(filename, "utf-8")
343 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
343 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
344 better_fn = frame.f_globals.get('__file__', None)
344 better_fn = frame.f_globals.get('__file__', None)
345 if isinstance(better_fn, str):
345 if isinstance(better_fn, str):
346 # Check the type just in case someone did something weird with
346 # Check the type just in case someone did something weird with
347 # __file__. It might also be None if the error occurred during
347 # __file__. It might also be None if the error occurred during
348 # import.
348 # import.
349 filename = better_fn
349 filename = better_fn
350 fixed_records.append((frame, filename, line_no, func_name, lines, index))
350 fixed_records.append((frame, filename, line_no, func_name, lines, index))
351 return fixed_records
351 return fixed_records
352
352
353
353
354 @with_patch_inspect
354 @with_patch_inspect
355 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
355 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
356 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
356 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
357
357
358 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
358 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
359 # If the error is at the console, don't build any context, since it would
359 # If the error is at the console, don't build any context, since it would
360 # otherwise produce 5 blank lines printed out (there is no file at the
360 # otherwise produce 5 blank lines printed out (there is no file at the
361 # console)
361 # console)
362 rec_check = records[tb_offset:]
362 rec_check = records[tb_offset:]
363 try:
363 try:
364 rname = rec_check[0][1]
364 rname = rec_check[0][1]
365 if rname == '<ipython console>' or rname.endswith('<string>'):
365 if rname == '<ipython console>' or rname.endswith('<string>'):
366 return rec_check
366 return rec_check
367 except IndexError:
367 except IndexError:
368 pass
368 pass
369
369
370 aux = traceback.extract_tb(etb)
370 aux = traceback.extract_tb(etb)
371 assert len(records) == len(aux)
371 assert len(records) == len(aux)
372 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
372 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
373 maybeStart = lnum - 1 - context // 2
373 maybeStart = lnum - 1 - context // 2
374 start = max(maybeStart, 0)
374 start = max(maybeStart, 0)
375 end = start + context
375 end = start + context
376 lines = ulinecache.getlines(file)[start:end]
376 lines = ulinecache.getlines(file)[start:end]
377 buf = list(records[i])
377 buf = list(records[i])
378 buf[LNUM_POS] = lnum
378 buf[LNUM_POS] = lnum
379 buf[INDEX_POS] = lnum - 1 - start
379 buf[INDEX_POS] = lnum - 1 - start
380 buf[LINES_POS] = lines
380 buf[LINES_POS] = lines
381 records[i] = tuple(buf)
381 records[i] = tuple(buf)
382 return records[tb_offset:]
382 return records[tb_offset:]
383
383
384 # Helper function -- largely belongs to VerboseTB, but we need the same
384 # Helper function -- largely belongs to VerboseTB, but we need the same
385 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
385 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
386 # can be recognized properly by ipython.el's py-traceback-line-re
386 # can be recognized properly by ipython.el's py-traceback-line-re
387 # (SyntaxErrors have to be treated specially because they have no traceback)
387 # (SyntaxErrors have to be treated specially because they have no traceback)
388
388
389 _parser = PyColorize.Parser()
390
391
389
392 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None, scheme=None):
390 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None, scheme=None):
393 numbers_width = INDENT_SIZE - 1
391 numbers_width = INDENT_SIZE - 1
394 res = []
392 res = []
395 i = lnum - index
393 i = lnum - index
396
394
397 # This lets us get fully syntax-highlighted tracebacks.
395 _line_format = PyColorize.Parser(style=scheme).format2
398 if scheme is None:
399 ipinst = get_ipython()
400 if ipinst is not None:
401 scheme = ipinst.colors
402 else:
403 scheme = DEFAULT_SCHEME
404
405 _line_format = _parser.format2
406
396
407 for line in lines:
397 for line in lines:
408 line = py3compat.cast_unicode(line)
398 line = py3compat.cast_unicode(line)
409
399
410 new_line, err = _line_format(line, 'str', scheme)
400 new_line, err = _line_format(line, 'str')
411 if not err: line = new_line
401 if not err: line = new_line
412
402
413 if i == lnum:
403 if i == lnum:
414 # This is the line with the error
404 # This is the line with the error
415 pad = numbers_width - len(str(i))
405 pad = numbers_width - len(str(i))
416 num = '%s%s' % (debugger.make_arrow(pad), str(lnum))
406 num = '%s%s' % (debugger.make_arrow(pad), str(lnum))
417 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
407 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
418 Colors.line, line, Colors.Normal)
408 Colors.line, line, Colors.Normal)
419 else:
409 else:
420 num = '%*s' % (numbers_width, i)
410 num = '%*s' % (numbers_width, i)
421 line = '%s%s%s %s' % (Colors.lineno, num,
411 line = '%s%s%s %s' % (Colors.lineno, num,
422 Colors.Normal, line)
412 Colors.Normal, line)
423
413
424 res.append(line)
414 res.append(line)
425 if lvals and i == lnum:
415 if lvals and i == lnum:
426 res.append(lvals + '\n')
416 res.append(lvals + '\n')
427 i = i + 1
417 i = i + 1
428 return res
418 return res
429
419
430 def is_recursion_error(etype, value, records):
420 def is_recursion_error(etype, value, records):
431 try:
421 try:
432 # RecursionError is new in Python 3.5
422 # RecursionError is new in Python 3.5
433 recursion_error_type = RecursionError
423 recursion_error_type = RecursionError
434 except NameError:
424 except NameError:
435 recursion_error_type = RuntimeError
425 recursion_error_type = RuntimeError
436
426
437 # The default recursion limit is 1000, but some of that will be taken up
427 # The default recursion limit is 1000, but some of that will be taken up
438 # by stack frames in IPython itself. >500 frames probably indicates
428 # by stack frames in IPython itself. >500 frames probably indicates
439 # a recursion error.
429 # a recursion error.
440 return (etype is recursion_error_type) \
430 return (etype is recursion_error_type) \
441 and "recursion" in str(value).lower() \
431 and "recursion" in str(value).lower() \
442 and len(records) > 500
432 and len(records) > 500
443
433
444 def find_recursion(etype, value, records):
434 def find_recursion(etype, value, records):
445 """Identify the repeating stack frames from a RecursionError traceback
435 """Identify the repeating stack frames from a RecursionError traceback
446
436
447 'records' is a list as returned by VerboseTB.get_records()
437 'records' is a list as returned by VerboseTB.get_records()
448
438
449 Returns (last_unique, repeat_length)
439 Returns (last_unique, repeat_length)
450 """
440 """
451 # This involves a bit of guesswork - we want to show enough of the traceback
441 # This involves a bit of guesswork - we want to show enough of the traceback
452 # to indicate where the recursion is occurring. We guess that the innermost
442 # to indicate where the recursion is occurring. We guess that the innermost
453 # quarter of the traceback (250 frames by default) is repeats, and find the
443 # quarter of the traceback (250 frames by default) is repeats, and find the
454 # first frame (from in to out) that looks different.
444 # first frame (from in to out) that looks different.
455 if not is_recursion_error(etype, value, records):
445 if not is_recursion_error(etype, value, records):
456 return len(records), 0
446 return len(records), 0
457
447
458 # Select filename, lineno, func_name to track frames with
448 # Select filename, lineno, func_name to track frames with
459 records = [r[1:4] for r in records]
449 records = [r[1:4] for r in records]
460 inner_frames = records[-(len(records)//4):]
450 inner_frames = records[-(len(records)//4):]
461 frames_repeated = set(inner_frames)
451 frames_repeated = set(inner_frames)
462
452
463 last_seen_at = {}
453 last_seen_at = {}
464 longest_repeat = 0
454 longest_repeat = 0
465 i = len(records)
455 i = len(records)
466 for frame in reversed(records):
456 for frame in reversed(records):
467 i -= 1
457 i -= 1
468 if frame not in frames_repeated:
458 if frame not in frames_repeated:
469 last_unique = i
459 last_unique = i
470 break
460 break
471
461
472 if frame in last_seen_at:
462 if frame in last_seen_at:
473 distance = last_seen_at[frame] - i
463 distance = last_seen_at[frame] - i
474 longest_repeat = max(longest_repeat, distance)
464 longest_repeat = max(longest_repeat, distance)
475
465
476 last_seen_at[frame] = i
466 last_seen_at[frame] = i
477 else:
467 else:
478 last_unique = 0 # The whole traceback was recursion
468 last_unique = 0 # The whole traceback was recursion
479
469
480 return last_unique, longest_repeat
470 return last_unique, longest_repeat
481
471
482 #---------------------------------------------------------------------------
472 #---------------------------------------------------------------------------
483 # Module classes
473 # Module classes
484 class TBTools(colorable.Colorable):
474 class TBTools(colorable.Colorable):
485 """Basic tools used by all traceback printer classes."""
475 """Basic tools used by all traceback printer classes."""
486
476
487 # Number of frames to skip when reporting tracebacks
477 # Number of frames to skip when reporting tracebacks
488 tb_offset = 0
478 tb_offset = 0
489
479
490 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
480 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
491 # Whether to call the interactive pdb debugger after printing
481 # Whether to call the interactive pdb debugger after printing
492 # tracebacks or not
482 # tracebacks or not
493 super(TBTools, self).__init__(parent=parent, config=config)
483 super(TBTools, self).__init__(parent=parent, config=config)
494 self.call_pdb = call_pdb
484 self.call_pdb = call_pdb
495
485
496 # Output stream to write to. Note that we store the original value in
486 # Output stream to write to. Note that we store the original value in
497 # a private attribute and then make the public ostream a property, so
487 # a private attribute and then make the public ostream a property, so
498 # that we can delay accessing sys.stdout until runtime. The way
488 # that we can delay accessing sys.stdout until runtime. The way
499 # things are written now, the sys.stdout object is dynamically managed
489 # things are written now, the sys.stdout object is dynamically managed
500 # so a reference to it should NEVER be stored statically. This
490 # so a reference to it should NEVER be stored statically. This
501 # property approach confines this detail to a single location, and all
491 # property approach confines this detail to a single location, and all
502 # subclasses can simply access self.ostream for writing.
492 # subclasses can simply access self.ostream for writing.
503 self._ostream = ostream
493 self._ostream = ostream
504
494
505 # Create color table
495 # Create color table
506 self.color_scheme_table = exception_colors()
496 self.color_scheme_table = exception_colors()
507
497
508 self.set_colors(color_scheme)
498 self.set_colors(color_scheme)
509 self.old_scheme = color_scheme # save initial value for toggles
499 self.old_scheme = color_scheme # save initial value for toggles
510
500
511 if call_pdb:
501 if call_pdb:
512 self.pdb = debugger.Pdb()
502 self.pdb = debugger.Pdb()
513 else:
503 else:
514 self.pdb = None
504 self.pdb = None
515
505
516 def _get_ostream(self):
506 def _get_ostream(self):
517 """Output stream that exceptions are written to.
507 """Output stream that exceptions are written to.
518
508
519 Valid values are:
509 Valid values are:
520
510
521 - None: the default, which means that IPython will dynamically resolve
511 - None: the default, which means that IPython will dynamically resolve
522 to sys.stdout. This ensures compatibility with most tools, including
512 to sys.stdout. This ensures compatibility with most tools, including
523 Windows (where plain stdout doesn't recognize ANSI escapes).
513 Windows (where plain stdout doesn't recognize ANSI escapes).
524
514
525 - Any object with 'write' and 'flush' attributes.
515 - Any object with 'write' and 'flush' attributes.
526 """
516 """
527 return sys.stdout if self._ostream is None else self._ostream
517 return sys.stdout if self._ostream is None else self._ostream
528
518
529 def _set_ostream(self, val):
519 def _set_ostream(self, val):
530 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
520 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
531 self._ostream = val
521 self._ostream = val
532
522
533 ostream = property(_get_ostream, _set_ostream)
523 ostream = property(_get_ostream, _set_ostream)
534
524
535 def set_colors(self, *args, **kw):
525 def set_colors(self, *args, **kw):
536 """Shorthand access to the color table scheme selector method."""
526 """Shorthand access to the color table scheme selector method."""
537
527
538 # Set own color table
528 # Set own color table
539 self.color_scheme_table.set_active_scheme(*args, **kw)
529 self.color_scheme_table.set_active_scheme(*args, **kw)
540 # for convenience, set Colors to the active scheme
530 # for convenience, set Colors to the active scheme
541 self.Colors = self.color_scheme_table.active_colors
531 self.Colors = self.color_scheme_table.active_colors
542 # Also set colors of debugger
532 # Also set colors of debugger
543 if hasattr(self, 'pdb') and self.pdb is not None:
533 if hasattr(self, 'pdb') and self.pdb is not None:
544 self.pdb.set_colors(*args, **kw)
534 self.pdb.set_colors(*args, **kw)
545
535
546 def color_toggle(self):
536 def color_toggle(self):
547 """Toggle between the currently active color scheme and NoColor."""
537 """Toggle between the currently active color scheme and NoColor."""
548
538
549 if self.color_scheme_table.active_scheme_name == 'NoColor':
539 if self.color_scheme_table.active_scheme_name == 'NoColor':
550 self.color_scheme_table.set_active_scheme(self.old_scheme)
540 self.color_scheme_table.set_active_scheme(self.old_scheme)
551 self.Colors = self.color_scheme_table.active_colors
541 self.Colors = self.color_scheme_table.active_colors
552 else:
542 else:
553 self.old_scheme = self.color_scheme_table.active_scheme_name
543 self.old_scheme = self.color_scheme_table.active_scheme_name
554 self.color_scheme_table.set_active_scheme('NoColor')
544 self.color_scheme_table.set_active_scheme('NoColor')
555 self.Colors = self.color_scheme_table.active_colors
545 self.Colors = self.color_scheme_table.active_colors
556
546
557 def stb2text(self, stb):
547 def stb2text(self, stb):
558 """Convert a structured traceback (a list) to a string."""
548 """Convert a structured traceback (a list) to a string."""
559 return '\n'.join(stb)
549 return '\n'.join(stb)
560
550
561 def text(self, etype, value, tb, tb_offset=None, context=5):
551 def text(self, etype, value, tb, tb_offset=None, context=5):
562 """Return formatted traceback.
552 """Return formatted traceback.
563
553
564 Subclasses may override this if they add extra arguments.
554 Subclasses may override this if they add extra arguments.
565 """
555 """
566 tb_list = self.structured_traceback(etype, value, tb,
556 tb_list = self.structured_traceback(etype, value, tb,
567 tb_offset, context)
557 tb_offset, context)
568 return self.stb2text(tb_list)
558 return self.stb2text(tb_list)
569
559
570 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
560 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
571 context=5, mode=None):
561 context=5, mode=None):
572 """Return a list of traceback frames.
562 """Return a list of traceback frames.
573
563
574 Must be implemented by each class.
564 Must be implemented by each class.
575 """
565 """
576 raise NotImplementedError()
566 raise NotImplementedError()
577
567
578
568
579 #---------------------------------------------------------------------------
569 #---------------------------------------------------------------------------
580 class ListTB(TBTools):
570 class ListTB(TBTools):
581 """Print traceback information from a traceback list, with optional color.
571 """Print traceback information from a traceback list, with optional color.
582
572
583 Calling requires 3 arguments: (etype, evalue, elist)
573 Calling requires 3 arguments: (etype, evalue, elist)
584 as would be obtained by::
574 as would be obtained by::
585
575
586 etype, evalue, tb = sys.exc_info()
576 etype, evalue, tb = sys.exc_info()
587 if tb:
577 if tb:
588 elist = traceback.extract_tb(tb)
578 elist = traceback.extract_tb(tb)
589 else:
579 else:
590 elist = None
580 elist = None
591
581
592 It can thus be used by programs which need to process the traceback before
582 It can thus be used by programs which need to process the traceback before
593 printing (such as console replacements based on the code module from the
583 printing (such as console replacements based on the code module from the
594 standard library).
584 standard library).
595
585
596 Because they are meant to be called without a full traceback (only a
586 Because they are meant to be called without a full traceback (only a
597 list), instances of this class can't call the interactive pdb debugger."""
587 list), instances of this class can't call the interactive pdb debugger."""
598
588
599 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None):
589 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None):
600 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
590 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
601 ostream=ostream, parent=parent)
591 ostream=ostream, parent=parent)
602
592
603 def __call__(self, etype, value, elist):
593 def __call__(self, etype, value, elist):
604 self.ostream.flush()
594 self.ostream.flush()
605 self.ostream.write(self.text(etype, value, elist))
595 self.ostream.write(self.text(etype, value, elist))
606 self.ostream.write('\n')
596 self.ostream.write('\n')
607
597
608 def structured_traceback(self, etype, value, elist, tb_offset=None,
598 def structured_traceback(self, etype, value, elist, tb_offset=None,
609 context=5):
599 context=5):
610 """Return a color formatted string with the traceback info.
600 """Return a color formatted string with the traceback info.
611
601
612 Parameters
602 Parameters
613 ----------
603 ----------
614 etype : exception type
604 etype : exception type
615 Type of the exception raised.
605 Type of the exception raised.
616
606
617 value : object
607 value : object
618 Data stored in the exception
608 Data stored in the exception
619
609
620 elist : list
610 elist : list
621 List of frames, see class docstring for details.
611 List of frames, see class docstring for details.
622
612
623 tb_offset : int, optional
613 tb_offset : int, optional
624 Number of frames in the traceback to skip. If not given, the
614 Number of frames in the traceback to skip. If not given, the
625 instance value is used (set in constructor).
615 instance value is used (set in constructor).
626
616
627 context : int, optional
617 context : int, optional
628 Number of lines of context information to print.
618 Number of lines of context information to print.
629
619
630 Returns
620 Returns
631 -------
621 -------
632 String with formatted exception.
622 String with formatted exception.
633 """
623 """
634 tb_offset = self.tb_offset if tb_offset is None else tb_offset
624 tb_offset = self.tb_offset if tb_offset is None else tb_offset
635 Colors = self.Colors
625 Colors = self.Colors
636 out_list = []
626 out_list = []
637 if elist:
627 if elist:
638
628
639 if tb_offset and len(elist) > tb_offset:
629 if tb_offset and len(elist) > tb_offset:
640 elist = elist[tb_offset:]
630 elist = elist[tb_offset:]
641
631
642 out_list.append('Traceback %s(most recent call last)%s:' %
632 out_list.append('Traceback %s(most recent call last)%s:' %
643 (Colors.normalEm, Colors.Normal) + '\n')
633 (Colors.normalEm, Colors.Normal) + '\n')
644 out_list.extend(self._format_list(elist))
634 out_list.extend(self._format_list(elist))
645 # The exception info should be a single entry in the list.
635 # The exception info should be a single entry in the list.
646 lines = ''.join(self._format_exception_only(etype, value))
636 lines = ''.join(self._format_exception_only(etype, value))
647 out_list.append(lines)
637 out_list.append(lines)
648
638
649 # Note: this code originally read:
639 # Note: this code originally read:
650
640
651 ## for line in lines[:-1]:
641 ## for line in lines[:-1]:
652 ## out_list.append(" "+line)
642 ## out_list.append(" "+line)
653 ## out_list.append(lines[-1])
643 ## out_list.append(lines[-1])
654
644
655 # This means it was indenting everything but the last line by a little
645 # This means it was indenting everything but the last line by a little
656 # bit. I've disabled this for now, but if we see ugliness somewhere we
646 # bit. I've disabled this for now, but if we see ugliness somewhere we
657 # can restore it.
647 # can restore it.
658
648
659 return out_list
649 return out_list
660
650
661 def _format_list(self, extracted_list):
651 def _format_list(self, extracted_list):
662 """Format a list of traceback entry tuples for printing.
652 """Format a list of traceback entry tuples for printing.
663
653
664 Given a list of tuples as returned by extract_tb() or
654 Given a list of tuples as returned by extract_tb() or
665 extract_stack(), return a list of strings ready for printing.
655 extract_stack(), return a list of strings ready for printing.
666 Each string in the resulting list corresponds to the item with the
656 Each string in the resulting list corresponds to the item with the
667 same index in the argument list. Each string ends in a newline;
657 same index in the argument list. Each string ends in a newline;
668 the strings may contain internal newlines as well, for those items
658 the strings may contain internal newlines as well, for those items
669 whose source text line is not None.
659 whose source text line is not None.
670
660
671 Lifted almost verbatim from traceback.py
661 Lifted almost verbatim from traceback.py
672 """
662 """
673
663
674 Colors = self.Colors
664 Colors = self.Colors
675 list = []
665 list = []
676 for filename, lineno, name, line in extracted_list[:-1]:
666 for filename, lineno, name, line in extracted_list[:-1]:
677 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
667 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
678 (Colors.filename, py3compat.cast_unicode_py2(filename, "utf-8"), Colors.Normal,
668 (Colors.filename, py3compat.cast_unicode_py2(filename, "utf-8"), Colors.Normal,
679 Colors.lineno, lineno, Colors.Normal,
669 Colors.lineno, lineno, Colors.Normal,
680 Colors.name, py3compat.cast_unicode_py2(name, "utf-8"), Colors.Normal)
670 Colors.name, py3compat.cast_unicode_py2(name, "utf-8"), Colors.Normal)
681 if line:
671 if line:
682 item += ' %s\n' % line.strip()
672 item += ' %s\n' % line.strip()
683 list.append(item)
673 list.append(item)
684 # Emphasize the last entry
674 # Emphasize the last entry
685 filename, lineno, name, line = extracted_list[-1]
675 filename, lineno, name, line = extracted_list[-1]
686 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
676 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
687 (Colors.normalEm,
677 (Colors.normalEm,
688 Colors.filenameEm, py3compat.cast_unicode_py2(filename, "utf-8"), Colors.normalEm,
678 Colors.filenameEm, py3compat.cast_unicode_py2(filename, "utf-8"), Colors.normalEm,
689 Colors.linenoEm, lineno, Colors.normalEm,
679 Colors.linenoEm, lineno, Colors.normalEm,
690 Colors.nameEm, py3compat.cast_unicode_py2(name, "utf-8"), Colors.normalEm,
680 Colors.nameEm, py3compat.cast_unicode_py2(name, "utf-8"), Colors.normalEm,
691 Colors.Normal)
681 Colors.Normal)
692 if line:
682 if line:
693 item += '%s %s%s\n' % (Colors.line, line.strip(),
683 item += '%s %s%s\n' % (Colors.line, line.strip(),
694 Colors.Normal)
684 Colors.Normal)
695 list.append(item)
685 list.append(item)
696 return list
686 return list
697
687
698 def _format_exception_only(self, etype, value):
688 def _format_exception_only(self, etype, value):
699 """Format the exception part of a traceback.
689 """Format the exception part of a traceback.
700
690
701 The arguments are the exception type and value such as given by
691 The arguments are the exception type and value such as given by
702 sys.exc_info()[:2]. The return value is a list of strings, each ending
692 sys.exc_info()[:2]. The return value is a list of strings, each ending
703 in a newline. Normally, the list contains a single string; however,
693 in a newline. Normally, the list contains a single string; however,
704 for SyntaxError exceptions, it contains several lines that (when
694 for SyntaxError exceptions, it contains several lines that (when
705 printed) display detailed information about where the syntax error
695 printed) display detailed information about where the syntax error
706 occurred. The message indicating which exception occurred is the
696 occurred. The message indicating which exception occurred is the
707 always last string in the list.
697 always last string in the list.
708
698
709 Also lifted nearly verbatim from traceback.py
699 Also lifted nearly verbatim from traceback.py
710 """
700 """
711 have_filedata = False
701 have_filedata = False
712 Colors = self.Colors
702 Colors = self.Colors
713 list = []
703 list = []
714 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
704 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
715 if value is None:
705 if value is None:
716 # Not sure if this can still happen in Python 2.6 and above
706 # Not sure if this can still happen in Python 2.6 and above
717 list.append(stype + '\n')
707 list.append(stype + '\n')
718 else:
708 else:
719 if issubclass(etype, SyntaxError):
709 if issubclass(etype, SyntaxError):
720 have_filedata = True
710 have_filedata = True
721 if not value.filename: value.filename = "<string>"
711 if not value.filename: value.filename = "<string>"
722 if value.lineno:
712 if value.lineno:
723 lineno = value.lineno
713 lineno = value.lineno
724 textline = ulinecache.getline(value.filename, value.lineno)
714 textline = ulinecache.getline(value.filename, value.lineno)
725 else:
715 else:
726 lineno = 'unknown'
716 lineno = 'unknown'
727 textline = ''
717 textline = ''
728 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
718 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
729 (Colors.normalEm,
719 (Colors.normalEm,
730 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
720 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
731 Colors.linenoEm, lineno, Colors.Normal ))
721 Colors.linenoEm, lineno, Colors.Normal ))
732 if textline == '':
722 if textline == '':
733 textline = py3compat.cast_unicode(value.text, "utf-8")
723 textline = py3compat.cast_unicode(value.text, "utf-8")
734
724
735 if textline is not None:
725 if textline is not None:
736 i = 0
726 i = 0
737 while i < len(textline) and textline[i].isspace():
727 while i < len(textline) and textline[i].isspace():
738 i += 1
728 i += 1
739 list.append('%s %s%s\n' % (Colors.line,
729 list.append('%s %s%s\n' % (Colors.line,
740 textline.strip(),
730 textline.strip(),
741 Colors.Normal))
731 Colors.Normal))
742 if value.offset is not None:
732 if value.offset is not None:
743 s = ' '
733 s = ' '
744 for c in textline[i:value.offset - 1]:
734 for c in textline[i:value.offset - 1]:
745 if c.isspace():
735 if c.isspace():
746 s += c
736 s += c
747 else:
737 else:
748 s += ' '
738 s += ' '
749 list.append('%s%s^%s\n' % (Colors.caret, s,
739 list.append('%s%s^%s\n' % (Colors.caret, s,
750 Colors.Normal))
740 Colors.Normal))
751
741
752 try:
742 try:
753 s = value.msg
743 s = value.msg
754 except Exception:
744 except Exception:
755 s = self._some_str(value)
745 s = self._some_str(value)
756 if s:
746 if s:
757 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
747 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
758 Colors.Normal, s))
748 Colors.Normal, s))
759 else:
749 else:
760 list.append('%s\n' % stype)
750 list.append('%s\n' % stype)
761
751
762 # sync with user hooks
752 # sync with user hooks
763 if have_filedata:
753 if have_filedata:
764 ipinst = get_ipython()
754 ipinst = get_ipython()
765 if ipinst is not None:
755 if ipinst is not None:
766 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
756 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
767
757
768 return list
758 return list
769
759
770 def get_exception_only(self, etype, value):
760 def get_exception_only(self, etype, value):
771 """Only print the exception type and message, without a traceback.
761 """Only print the exception type and message, without a traceback.
772
762
773 Parameters
763 Parameters
774 ----------
764 ----------
775 etype : exception type
765 etype : exception type
776 value : exception value
766 value : exception value
777 """
767 """
778 return ListTB.structured_traceback(self, etype, value, [])
768 return ListTB.structured_traceback(self, etype, value, [])
779
769
780 def show_exception_only(self, etype, evalue):
770 def show_exception_only(self, etype, evalue):
781 """Only print the exception type and message, without a traceback.
771 """Only print the exception type and message, without a traceback.
782
772
783 Parameters
773 Parameters
784 ----------
774 ----------
785 etype : exception type
775 etype : exception type
786 value : exception value
776 value : exception value
787 """
777 """
788 # This method needs to use __call__ from *this* class, not the one from
778 # This method needs to use __call__ from *this* class, not the one from
789 # a subclass whose signature or behavior may be different
779 # a subclass whose signature or behavior may be different
790 ostream = self.ostream
780 ostream = self.ostream
791 ostream.flush()
781 ostream.flush()
792 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
782 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
793 ostream.flush()
783 ostream.flush()
794
784
795 def _some_str(self, value):
785 def _some_str(self, value):
796 # Lifted from traceback.py
786 # Lifted from traceback.py
797 try:
787 try:
798 return py3compat.cast_unicode(str(value))
788 return py3compat.cast_unicode(str(value))
799 except:
789 except:
800 return u'<unprintable %s object>' % type(value).__name__
790 return u'<unprintable %s object>' % type(value).__name__
801
791
802
792
803 #----------------------------------------------------------------------------
793 #----------------------------------------------------------------------------
804 class VerboseTB(TBTools):
794 class VerboseTB(TBTools):
805 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
795 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
806 of HTML. Requires inspect and pydoc. Crazy, man.
796 of HTML. Requires inspect and pydoc. Crazy, man.
807
797
808 Modified version which optionally strips the topmost entries from the
798 Modified version which optionally strips the topmost entries from the
809 traceback, to be used with alternate interpreters (because their own code
799 traceback, to be used with alternate interpreters (because their own code
810 would appear in the traceback)."""
800 would appear in the traceback)."""
811
801
812 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
802 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
813 tb_offset=0, long_header=False, include_vars=True,
803 tb_offset=0, long_header=False, include_vars=True,
814 check_cache=None, debugger_cls = None):
804 check_cache=None, debugger_cls = None):
815 """Specify traceback offset, headers and color scheme.
805 """Specify traceback offset, headers and color scheme.
816
806
817 Define how many frames to drop from the tracebacks. Calling it with
807 Define how many frames to drop from the tracebacks. Calling it with
818 tb_offset=1 allows use of this handler in interpreters which will have
808 tb_offset=1 allows use of this handler in interpreters which will have
819 their own code at the top of the traceback (VerboseTB will first
809 their own code at the top of the traceback (VerboseTB will first
820 remove that frame before printing the traceback info)."""
810 remove that frame before printing the traceback info)."""
821 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
811 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
822 ostream=ostream)
812 ostream=ostream)
823 self.tb_offset = tb_offset
813 self.tb_offset = tb_offset
824 self.long_header = long_header
814 self.long_header = long_header
825 self.include_vars = include_vars
815 self.include_vars = include_vars
826 # By default we use linecache.checkcache, but the user can provide a
816 # By default we use linecache.checkcache, but the user can provide a
827 # different check_cache implementation. This is used by the IPython
817 # different check_cache implementation. This is used by the IPython
828 # kernel to provide tracebacks for interactive code that is cached,
818 # kernel to provide tracebacks for interactive code that is cached,
829 # by a compiler instance that flushes the linecache but preserves its
819 # by a compiler instance that flushes the linecache but preserves its
830 # own code cache.
820 # own code cache.
831 if check_cache is None:
821 if check_cache is None:
832 check_cache = linecache.checkcache
822 check_cache = linecache.checkcache
833 self.check_cache = check_cache
823 self.check_cache = check_cache
834
824
835 self.debugger_cls = debugger_cls or debugger.Pdb
825 self.debugger_cls = debugger_cls or debugger.Pdb
836
826
837 def format_records(self, records, last_unique, recursion_repeat):
827 def format_records(self, records, last_unique, recursion_repeat):
838 """Format the stack frames of the traceback"""
828 """Format the stack frames of the traceback"""
839 frames = []
829 frames = []
840 for r in records[:last_unique+recursion_repeat+1]:
830 for r in records[:last_unique+recursion_repeat+1]:
841 #print '*** record:',file,lnum,func,lines,index # dbg
831 #print '*** record:',file,lnum,func,lines,index # dbg
842 frames.append(self.format_record(*r))
832 frames.append(self.format_record(*r))
843
833
844 if recursion_repeat:
834 if recursion_repeat:
845 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
835 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
846 frames.append(self.format_record(*records[last_unique+recursion_repeat+1]))
836 frames.append(self.format_record(*records[last_unique+recursion_repeat+1]))
847
837
848 return frames
838 return frames
849
839
850 def format_record(self, frame, file, lnum, func, lines, index):
840 def format_record(self, frame, file, lnum, func, lines, index):
851 """Format a single stack frame"""
841 """Format a single stack frame"""
852 Colors = self.Colors # just a shorthand + quicker name lookup
842 Colors = self.Colors # just a shorthand + quicker name lookup
853 ColorsNormal = Colors.Normal # used a lot
843 ColorsNormal = Colors.Normal # used a lot
854 col_scheme = self.color_scheme_table.active_scheme_name
844 col_scheme = self.color_scheme_table.active_scheme_name
855 indent = ' ' * INDENT_SIZE
845 indent = ' ' * INDENT_SIZE
856 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
846 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
857 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
847 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
858 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
848 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
859 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
849 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
860 ColorsNormal)
850 ColorsNormal)
861 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
851 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
862 (Colors.vName, Colors.valEm, ColorsNormal)
852 (Colors.vName, Colors.valEm, ColorsNormal)
863 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
853 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
864 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
854 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
865 Colors.vName, ColorsNormal)
855 Colors.vName, ColorsNormal)
866 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
856 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
867
857
868 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
858 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
869 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm, Colors.line,
859 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm, Colors.line,
870 ColorsNormal)
860 ColorsNormal)
871
861
872 abspath = os.path.abspath
862 abspath = os.path.abspath
873
863
874
864
875 if not file:
865 if not file:
876 file = '?'
866 file = '?'
877 elif file.startswith(str("<")) and file.endswith(str(">")):
867 elif file.startswith(str("<")) and file.endswith(str(">")):
878 # Not a real filename, no problem...
868 # Not a real filename, no problem...
879 pass
869 pass
880 elif not os.path.isabs(file):
870 elif not os.path.isabs(file):
881 # Try to make the filename absolute by trying all
871 # Try to make the filename absolute by trying all
882 # sys.path entries (which is also what linecache does)
872 # sys.path entries (which is also what linecache does)
883 for dirname in sys.path:
873 for dirname in sys.path:
884 try:
874 try:
885 fullname = os.path.join(dirname, file)
875 fullname = os.path.join(dirname, file)
886 if os.path.isfile(fullname):
876 if os.path.isfile(fullname):
887 file = os.path.abspath(fullname)
877 file = os.path.abspath(fullname)
888 break
878 break
889 except Exception:
879 except Exception:
890 # Just in case that sys.path contains very
880 # Just in case that sys.path contains very
891 # strange entries...
881 # strange entries...
892 pass
882 pass
893
883
894 file = py3compat.cast_unicode(file, util_path.fs_encoding)
884 file = py3compat.cast_unicode(file, util_path.fs_encoding)
895 link = tpl_link % file
885 link = tpl_link % file
896 args, varargs, varkw, locals = fixed_getargvalues(frame)
886 args, varargs, varkw, locals = fixed_getargvalues(frame)
897
887
898 if func == '?':
888 if func == '?':
899 call = ''
889 call = ''
900 else:
890 else:
901 # Decide whether to include variable details or not
891 # Decide whether to include variable details or not
902 var_repr = self.include_vars and eqrepr or nullrepr
892 var_repr = self.include_vars and eqrepr or nullrepr
903 try:
893 try:
904 call = tpl_call % (func, inspect.formatargvalues(args,
894 call = tpl_call % (func, inspect.formatargvalues(args,
905 varargs, varkw,
895 varargs, varkw,
906 locals, formatvalue=var_repr))
896 locals, formatvalue=var_repr))
907 except KeyError:
897 except KeyError:
908 # This happens in situations like errors inside generator
898 # This happens in situations like errors inside generator
909 # expressions, where local variables are listed in the
899 # expressions, where local variables are listed in the
910 # line, but can't be extracted from the frame. I'm not
900 # line, but can't be extracted from the frame. I'm not
911 # 100% sure this isn't actually a bug in inspect itself,
901 # 100% sure this isn't actually a bug in inspect itself,
912 # but since there's no info for us to compute with, the
902 # but since there's no info for us to compute with, the
913 # best we can do is report the failure and move on. Here
903 # best we can do is report the failure and move on. Here
914 # we must *not* call any traceback construction again,
904 # we must *not* call any traceback construction again,
915 # because that would mess up use of %debug later on. So we
905 # because that would mess up use of %debug later on. So we
916 # simply report the failure and move on. The only
906 # simply report the failure and move on. The only
917 # limitation will be that this frame won't have locals
907 # limitation will be that this frame won't have locals
918 # listed in the call signature. Quite subtle problem...
908 # listed in the call signature. Quite subtle problem...
919 # I can't think of a good way to validate this in a unit
909 # I can't think of a good way to validate this in a unit
920 # test, but running a script consisting of:
910 # test, but running a script consisting of:
921 # dict( (k,v.strip()) for (k,v) in range(10) )
911 # dict( (k,v.strip()) for (k,v) in range(10) )
922 # will illustrate the error, if this exception catch is
912 # will illustrate the error, if this exception catch is
923 # disabled.
913 # disabled.
924 call = tpl_call_fail % func
914 call = tpl_call_fail % func
925
915
926 # Don't attempt to tokenize binary files.
916 # Don't attempt to tokenize binary files.
927 if file.endswith(('.so', '.pyd', '.dll')):
917 if file.endswith(('.so', '.pyd', '.dll')):
928 return '%s %s\n' % (link, call)
918 return '%s %s\n' % (link, call)
929
919
930 elif file.endswith(('.pyc', '.pyo')):
920 elif file.endswith(('.pyc', '.pyo')):
931 # Look up the corresponding source file.
921 # Look up the corresponding source file.
932 try:
922 try:
933 file = openpy.source_from_cache(file)
923 file = openpy.source_from_cache(file)
934 except ValueError:
924 except ValueError:
935 # Failed to get the source file for some reason
925 # Failed to get the source file for some reason
936 # E.g. https://github.com/ipython/ipython/issues/9486
926 # E.g. https://github.com/ipython/ipython/issues/9486
937 return '%s %s\n' % (link, call)
927 return '%s %s\n' % (link, call)
938
928
939 def linereader(file=file, lnum=[lnum], getline=ulinecache.getline):
929 def linereader(file=file, lnum=[lnum], getline=ulinecache.getline):
940 line = getline(file, lnum[0])
930 line = getline(file, lnum[0])
941 lnum[0] += 1
931 lnum[0] += 1
942 return line
932 return line
943
933
944 # Build the list of names on this line of code where the exception
934 # Build the list of names on this line of code where the exception
945 # occurred.
935 # occurred.
946 try:
936 try:
947 names = []
937 names = []
948 name_cont = False
938 name_cont = False
949
939
950 for token_type, token, start, end, line in generate_tokens(linereader):
940 for token_type, token, start, end, line in generate_tokens(linereader):
951 # build composite names
941 # build composite names
952 if token_type == tokenize.NAME and token not in keyword.kwlist:
942 if token_type == tokenize.NAME and token not in keyword.kwlist:
953 if name_cont:
943 if name_cont:
954 # Continuation of a dotted name
944 # Continuation of a dotted name
955 try:
945 try:
956 names[-1].append(token)
946 names[-1].append(token)
957 except IndexError:
947 except IndexError:
958 names.append([token])
948 names.append([token])
959 name_cont = False
949 name_cont = False
960 else:
950 else:
961 # Regular new names. We append everything, the caller
951 # Regular new names. We append everything, the caller
962 # will be responsible for pruning the list later. It's
952 # will be responsible for pruning the list later. It's
963 # very tricky to try to prune as we go, b/c composite
953 # very tricky to try to prune as we go, b/c composite
964 # names can fool us. The pruning at the end is easy
954 # names can fool us. The pruning at the end is easy
965 # to do (or the caller can print a list with repeated
955 # to do (or the caller can print a list with repeated
966 # names if so desired.
956 # names if so desired.
967 names.append([token])
957 names.append([token])
968 elif token == '.':
958 elif token == '.':
969 name_cont = True
959 name_cont = True
970 elif token_type == tokenize.NEWLINE:
960 elif token_type == tokenize.NEWLINE:
971 break
961 break
972
962
973 except (IndexError, UnicodeDecodeError, SyntaxError):
963 except (IndexError, UnicodeDecodeError, SyntaxError):
974 # signals exit of tokenizer
964 # signals exit of tokenizer
975 # SyntaxError can occur if the file is not actually Python
965 # SyntaxError can occur if the file is not actually Python
976 # - see gh-6300
966 # - see gh-6300
977 pass
967 pass
978 except tokenize.TokenError as msg:
968 except tokenize.TokenError as msg:
979 _m = ("An unexpected error occurred while tokenizing input\n"
969 _m = ("An unexpected error occurred while tokenizing input\n"
980 "The following traceback may be corrupted or invalid\n"
970 "The following traceback may be corrupted or invalid\n"
981 "The error message is: %s\n" % msg)
971 "The error message is: %s\n" % msg)
982 error(_m)
972 error(_m)
983
973
984 # Join composite names (e.g. "dict.fromkeys")
974 # Join composite names (e.g. "dict.fromkeys")
985 names = ['.'.join(n) for n in names]
975 names = ['.'.join(n) for n in names]
986 # prune names list of duplicates, but keep the right order
976 # prune names list of duplicates, but keep the right order
987 unique_names = uniq_stable(names)
977 unique_names = uniq_stable(names)
988
978
989 # Start loop over vars
979 # Start loop over vars
990 lvals = []
980 lvals = []
991 if self.include_vars:
981 if self.include_vars:
992 for name_full in unique_names:
982 for name_full in unique_names:
993 name_base = name_full.split('.', 1)[0]
983 name_base = name_full.split('.', 1)[0]
994 if name_base in frame.f_code.co_varnames:
984 if name_base in frame.f_code.co_varnames:
995 if name_base in locals:
985 if name_base in locals:
996 try:
986 try:
997 value = repr(eval(name_full, locals))
987 value = repr(eval(name_full, locals))
998 except:
988 except:
999 value = undefined
989 value = undefined
1000 else:
990 else:
1001 value = undefined
991 value = undefined
1002 name = tpl_local_var % name_full
992 name = tpl_local_var % name_full
1003 else:
993 else:
1004 if name_base in frame.f_globals:
994 if name_base in frame.f_globals:
1005 try:
995 try:
1006 value = repr(eval(name_full, frame.f_globals))
996 value = repr(eval(name_full, frame.f_globals))
1007 except:
997 except:
1008 value = undefined
998 value = undefined
1009 else:
999 else:
1010 value = undefined
1000 value = undefined
1011 name = tpl_global_var % name_full
1001 name = tpl_global_var % name_full
1012 lvals.append(tpl_name_val % (name, value))
1002 lvals.append(tpl_name_val % (name, value))
1013 if lvals:
1003 if lvals:
1014 lvals = '%s%s' % (indent, em_normal.join(lvals))
1004 lvals = '%s%s' % (indent, em_normal.join(lvals))
1015 else:
1005 else:
1016 lvals = ''
1006 lvals = ''
1017
1007
1018 level = '%s %s\n' % (link, call)
1008 level = '%s %s\n' % (link, call)
1019
1009
1020 if index is None:
1010 if index is None:
1021 return level
1011 return level
1022 else:
1012 else:
1023 return '%s%s' % (level, ''.join(
1013 return '%s%s' % (level, ''.join(
1024 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1014 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1025 col_scheme)))
1015 col_scheme)))
1026
1016
1027 def prepare_chained_exception_message(self, cause):
1017 def prepare_chained_exception_message(self, cause):
1028 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
1018 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
1029 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
1019 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
1030
1020
1031 if cause:
1021 if cause:
1032 message = [[direct_cause]]
1022 message = [[direct_cause]]
1033 else:
1023 else:
1034 message = [[exception_during_handling]]
1024 message = [[exception_during_handling]]
1035 return message
1025 return message
1036
1026
1037 def prepare_header(self, etype, long_version=False):
1027 def prepare_header(self, etype, long_version=False):
1038 colors = self.Colors # just a shorthand + quicker name lookup
1028 colors = self.Colors # just a shorthand + quicker name lookup
1039 colorsnormal = colors.Normal # used a lot
1029 colorsnormal = colors.Normal # used a lot
1040 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1030 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1041 width = min(75, get_terminal_size()[0])
1031 width = min(75, get_terminal_size()[0])
1042 if long_version:
1032 if long_version:
1043 # Header with the exception type, python version, and date
1033 # Header with the exception type, python version, and date
1044 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1034 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1045 date = time.ctime(time.time())
1035 date = time.ctime(time.time())
1046
1036
1047 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
1037 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
1048 exc, ' ' * (width - len(str(etype)) - len(pyver)),
1038 exc, ' ' * (width - len(str(etype)) - len(pyver)),
1049 pyver, date.rjust(width) )
1039 pyver, date.rjust(width) )
1050 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
1040 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
1051 "\ncalls leading up to the error, with the most recent (innermost) call last."
1041 "\ncalls leading up to the error, with the most recent (innermost) call last."
1052 else:
1042 else:
1053 # Simplified header
1043 # Simplified header
1054 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
1044 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
1055 rjust(width - len(str(etype))) )
1045 rjust(width - len(str(etype))) )
1056
1046
1057 return head
1047 return head
1058
1048
1059 def format_exception(self, etype, evalue):
1049 def format_exception(self, etype, evalue):
1060 colors = self.Colors # just a shorthand + quicker name lookup
1050 colors = self.Colors # just a shorthand + quicker name lookup
1061 colorsnormal = colors.Normal # used a lot
1051 colorsnormal = colors.Normal # used a lot
1062 indent = ' ' * INDENT_SIZE
1052 indent = ' ' * INDENT_SIZE
1063 # Get (safely) a string form of the exception info
1053 # Get (safely) a string form of the exception info
1064 try:
1054 try:
1065 etype_str, evalue_str = map(str, (etype, evalue))
1055 etype_str, evalue_str = map(str, (etype, evalue))
1066 except:
1056 except:
1067 # User exception is improperly defined.
1057 # User exception is improperly defined.
1068 etype, evalue = str, sys.exc_info()[:2]
1058 etype, evalue = str, sys.exc_info()[:2]
1069 etype_str, evalue_str = map(str, (etype, evalue))
1059 etype_str, evalue_str = map(str, (etype, evalue))
1070 # ... and format it
1060 # ... and format it
1071 exception = ['%s%s%s: %s' % (colors.excName, etype_str,
1061 exception = ['%s%s%s: %s' % (colors.excName, etype_str,
1072 colorsnormal, py3compat.cast_unicode(evalue_str))]
1062 colorsnormal, py3compat.cast_unicode(evalue_str))]
1073
1063
1074 if (not py3compat.PY3) and type(evalue) is types.InstanceType:
1064 if (not py3compat.PY3) and type(evalue) is types.InstanceType:
1075 try:
1065 try:
1076 names = [w for w in dir(evalue) if isinstance(w, py3compat.string_types)]
1066 names = [w for w in dir(evalue) if isinstance(w, py3compat.string_types)]
1077 except:
1067 except:
1078 # Every now and then, an object with funny internals blows up
1068 # Every now and then, an object with funny internals blows up
1079 # when dir() is called on it. We do the best we can to report
1069 # when dir() is called on it. We do the best we can to report
1080 # the problem and continue
1070 # the problem and continue
1081 _m = '%sException reporting error (object with broken dir())%s:'
1071 _m = '%sException reporting error (object with broken dir())%s:'
1082 exception.append(_m % (colors.excName, colorsnormal))
1072 exception.append(_m % (colors.excName, colorsnormal))
1083 etype_str, evalue_str = map(str, sys.exc_info()[:2])
1073 etype_str, evalue_str = map(str, sys.exc_info()[:2])
1084 exception.append('%s%s%s: %s' % (colors.excName, etype_str,
1074 exception.append('%s%s%s: %s' % (colors.excName, etype_str,
1085 colorsnormal, py3compat.cast_unicode(evalue_str)))
1075 colorsnormal, py3compat.cast_unicode(evalue_str)))
1086 names = []
1076 names = []
1087 for name in names:
1077 for name in names:
1088 value = text_repr(getattr(evalue, name))
1078 value = text_repr(getattr(evalue, name))
1089 exception.append('\n%s%s = %s' % (indent, name, value))
1079 exception.append('\n%s%s = %s' % (indent, name, value))
1090
1080
1091 return exception
1081 return exception
1092
1082
1093 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
1083 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
1094 """Formats the header, traceback and exception message for a single exception.
1084 """Formats the header, traceback and exception message for a single exception.
1095
1085
1096 This may be called multiple times by Python 3 exception chaining
1086 This may be called multiple times by Python 3 exception chaining
1097 (PEP 3134).
1087 (PEP 3134).
1098 """
1088 """
1099 # some locals
1089 # some locals
1100 orig_etype = etype
1090 orig_etype = etype
1101 try:
1091 try:
1102 etype = etype.__name__
1092 etype = etype.__name__
1103 except AttributeError:
1093 except AttributeError:
1104 pass
1094 pass
1105
1095
1106 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1096 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1107 head = self.prepare_header(etype, self.long_header)
1097 head = self.prepare_header(etype, self.long_header)
1108 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1098 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1109
1099
1110 if records is None:
1100 if records is None:
1111 return ""
1101 return ""
1112
1102
1113 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1103 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1114
1104
1115 frames = self.format_records(records, last_unique, recursion_repeat)
1105 frames = self.format_records(records, last_unique, recursion_repeat)
1116
1106
1117 formatted_exception = self.format_exception(etype, evalue)
1107 formatted_exception = self.format_exception(etype, evalue)
1118 if records:
1108 if records:
1119 filepath, lnum = records[-1][1:3]
1109 filepath, lnum = records[-1][1:3]
1120 filepath = os.path.abspath(filepath)
1110 filepath = os.path.abspath(filepath)
1121 ipinst = get_ipython()
1111 ipinst = get_ipython()
1122 if ipinst is not None:
1112 if ipinst is not None:
1123 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
1113 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
1124
1114
1125 return [[head] + frames + [''.join(formatted_exception[0])]]
1115 return [[head] + frames + [''.join(formatted_exception[0])]]
1126
1116
1127 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1117 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1128 try:
1118 try:
1129 # Try the default getinnerframes and Alex's: Alex's fixes some
1119 # Try the default getinnerframes and Alex's: Alex's fixes some
1130 # problems, but it generates empty tracebacks for console errors
1120 # problems, but it generates empty tracebacks for console errors
1131 # (5 blanks lines) where none should be returned.
1121 # (5 blanks lines) where none should be returned.
1132 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
1122 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
1133 except UnicodeDecodeError:
1123 except UnicodeDecodeError:
1134 # This can occur if a file's encoding magic comment is wrong.
1124 # This can occur if a file's encoding magic comment is wrong.
1135 # I can't see a way to recover without duplicating a bunch of code
1125 # I can't see a way to recover without duplicating a bunch of code
1136 # from the stdlib traceback module. --TK
1126 # from the stdlib traceback module. --TK
1137 error('\nUnicodeDecodeError while processing traceback.\n')
1127 error('\nUnicodeDecodeError while processing traceback.\n')
1138 return None
1128 return None
1139 except:
1129 except:
1140 # FIXME: I've been getting many crash reports from python 2.3
1130 # FIXME: I've been getting many crash reports from python 2.3
1141 # users, traceable to inspect.py. If I can find a small test-case
1131 # users, traceable to inspect.py. If I can find a small test-case
1142 # to reproduce this, I should either write a better workaround or
1132 # to reproduce this, I should either write a better workaround or
1143 # file a bug report against inspect (if that's the real problem).
1133 # file a bug report against inspect (if that's the real problem).
1144 # So far, I haven't been able to find an isolated example to
1134 # So far, I haven't been able to find an isolated example to
1145 # reproduce the problem.
1135 # reproduce the problem.
1146 inspect_error()
1136 inspect_error()
1147 traceback.print_exc(file=self.ostream)
1137 traceback.print_exc(file=self.ostream)
1148 info('\nUnfortunately, your original traceback can not be constructed.\n')
1138 info('\nUnfortunately, your original traceback can not be constructed.\n')
1149 return None
1139 return None
1150
1140
1151 def get_parts_of_chained_exception(self, evalue):
1141 def get_parts_of_chained_exception(self, evalue):
1152 def get_chained_exception(exception_value):
1142 def get_chained_exception(exception_value):
1153 cause = getattr(exception_value, '__cause__', None)
1143 cause = getattr(exception_value, '__cause__', None)
1154 if cause:
1144 if cause:
1155 return cause
1145 return cause
1156 if getattr(exception_value, '__suppress_context__', False):
1146 if getattr(exception_value, '__suppress_context__', False):
1157 return None
1147 return None
1158 return getattr(exception_value, '__context__', None)
1148 return getattr(exception_value, '__context__', None)
1159
1149
1160 chained_evalue = get_chained_exception(evalue)
1150 chained_evalue = get_chained_exception(evalue)
1161
1151
1162 if chained_evalue:
1152 if chained_evalue:
1163 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
1153 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
1164
1154
1165 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1155 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1166 number_of_lines_of_context=5):
1156 number_of_lines_of_context=5):
1167 """Return a nice text document describing the traceback."""
1157 """Return a nice text document describing the traceback."""
1168
1158
1169 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1159 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1170 tb_offset)
1160 tb_offset)
1171
1161
1172 colors = self.Colors # just a shorthand + quicker name lookup
1162 colors = self.Colors # just a shorthand + quicker name lookup
1173 colorsnormal = colors.Normal # used a lot
1163 colorsnormal = colors.Normal # used a lot
1174 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1164 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1175 structured_traceback_parts = [head]
1165 structured_traceback_parts = [head]
1176 if py3compat.PY3:
1166 if py3compat.PY3:
1177 chained_exceptions_tb_offset = 0
1167 chained_exceptions_tb_offset = 0
1178 lines_of_context = 3
1168 lines_of_context = 3
1179 formatted_exceptions = formatted_exception
1169 formatted_exceptions = formatted_exception
1180 exception = self.get_parts_of_chained_exception(evalue)
1170 exception = self.get_parts_of_chained_exception(evalue)
1181 if exception:
1171 if exception:
1182 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1172 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1183 etype, evalue, etb = exception
1173 etype, evalue, etb = exception
1184 else:
1174 else:
1185 evalue = None
1175 evalue = None
1186 chained_exc_ids = set()
1176 chained_exc_ids = set()
1187 while evalue:
1177 while evalue:
1188 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1178 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1189 chained_exceptions_tb_offset)
1179 chained_exceptions_tb_offset)
1190 exception = self.get_parts_of_chained_exception(evalue)
1180 exception = self.get_parts_of_chained_exception(evalue)
1191
1181
1192 if exception and not id(exception[1]) in chained_exc_ids:
1182 if exception and not id(exception[1]) in chained_exc_ids:
1193 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1183 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1194 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1184 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1195 etype, evalue, etb = exception
1185 etype, evalue, etb = exception
1196 else:
1186 else:
1197 evalue = None
1187 evalue = None
1198
1188
1199 # we want to see exceptions in a reversed order:
1189 # we want to see exceptions in a reversed order:
1200 # the first exception should be on top
1190 # the first exception should be on top
1201 for formatted_exception in reversed(formatted_exceptions):
1191 for formatted_exception in reversed(formatted_exceptions):
1202 structured_traceback_parts += formatted_exception
1192 structured_traceback_parts += formatted_exception
1203 else:
1193 else:
1204 structured_traceback_parts += formatted_exception[0]
1194 structured_traceback_parts += formatted_exception[0]
1205
1195
1206 return structured_traceback_parts
1196 return structured_traceback_parts
1207
1197
1208 def debugger(self, force=False):
1198 def debugger(self, force=False):
1209 """Call up the pdb debugger if desired, always clean up the tb
1199 """Call up the pdb debugger if desired, always clean up the tb
1210 reference.
1200 reference.
1211
1201
1212 Keywords:
1202 Keywords:
1213
1203
1214 - force(False): by default, this routine checks the instance call_pdb
1204 - force(False): by default, this routine checks the instance call_pdb
1215 flag and does not actually invoke the debugger if the flag is false.
1205 flag and does not actually invoke the debugger if the flag is false.
1216 The 'force' option forces the debugger to activate even if the flag
1206 The 'force' option forces the debugger to activate even if the flag
1217 is false.
1207 is false.
1218
1208
1219 If the call_pdb flag is set, the pdb interactive debugger is
1209 If the call_pdb flag is set, the pdb interactive debugger is
1220 invoked. In all cases, the self.tb reference to the current traceback
1210 invoked. In all cases, the self.tb reference to the current traceback
1221 is deleted to prevent lingering references which hamper memory
1211 is deleted to prevent lingering references which hamper memory
1222 management.
1212 management.
1223
1213
1224 Note that each call to pdb() does an 'import readline', so if your app
1214 Note that each call to pdb() does an 'import readline', so if your app
1225 requires a special setup for the readline completers, you'll have to
1215 requires a special setup for the readline completers, you'll have to
1226 fix that by hand after invoking the exception handler."""
1216 fix that by hand after invoking the exception handler."""
1227
1217
1228 if force or self.call_pdb:
1218 if force or self.call_pdb:
1229 if self.pdb is None:
1219 if self.pdb is None:
1230 self.pdb = self.debugger_cls(
1220 self.pdb = self.debugger_cls()
1231 self.color_scheme_table.active_scheme_name)
1232 # the system displayhook may have changed, restore the original
1221 # the system displayhook may have changed, restore the original
1233 # for pdb
1222 # for pdb
1234 display_trap = DisplayTrap(hook=sys.__displayhook__)
1223 display_trap = DisplayTrap(hook=sys.__displayhook__)
1235 with display_trap:
1224 with display_trap:
1236 self.pdb.reset()
1225 self.pdb.reset()
1237 # Find the right frame so we don't pop up inside ipython itself
1226 # Find the right frame so we don't pop up inside ipython itself
1238 if hasattr(self, 'tb') and self.tb is not None:
1227 if hasattr(self, 'tb') and self.tb is not None:
1239 etb = self.tb
1228 etb = self.tb
1240 else:
1229 else:
1241 etb = self.tb = sys.last_traceback
1230 etb = self.tb = sys.last_traceback
1242 while self.tb is not None and self.tb.tb_next is not None:
1231 while self.tb is not None and self.tb.tb_next is not None:
1243 self.tb = self.tb.tb_next
1232 self.tb = self.tb.tb_next
1244 if etb and etb.tb_next:
1233 if etb and etb.tb_next:
1245 etb = etb.tb_next
1234 etb = etb.tb_next
1246 self.pdb.botframe = etb.tb_frame
1235 self.pdb.botframe = etb.tb_frame
1247 self.pdb.interaction(self.tb.tb_frame, self.tb)
1236 self.pdb.interaction(self.tb.tb_frame, self.tb)
1248
1237
1249 if hasattr(self, 'tb'):
1238 if hasattr(self, 'tb'):
1250 del self.tb
1239 del self.tb
1251
1240
1252 def handler(self, info=None):
1241 def handler(self, info=None):
1253 (etype, evalue, etb) = info or sys.exc_info()
1242 (etype, evalue, etb) = info or sys.exc_info()
1254 self.tb = etb
1243 self.tb = etb
1255 ostream = self.ostream
1244 ostream = self.ostream
1256 ostream.flush()
1245 ostream.flush()
1257 ostream.write(self.text(etype, evalue, etb))
1246 ostream.write(self.text(etype, evalue, etb))
1258 ostream.write('\n')
1247 ostream.write('\n')
1259 ostream.flush()
1248 ostream.flush()
1260
1249
1261 # Changed so an instance can just be called as VerboseTB_inst() and print
1250 # Changed so an instance can just be called as VerboseTB_inst() and print
1262 # out the right info on its own.
1251 # out the right info on its own.
1263 def __call__(self, etype=None, evalue=None, etb=None):
1252 def __call__(self, etype=None, evalue=None, etb=None):
1264 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1253 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1265 if etb is None:
1254 if etb is None:
1266 self.handler()
1255 self.handler()
1267 else:
1256 else:
1268 self.handler((etype, evalue, etb))
1257 self.handler((etype, evalue, etb))
1269 try:
1258 try:
1270 self.debugger()
1259 self.debugger()
1271 except KeyboardInterrupt:
1260 except KeyboardInterrupt:
1272 print("\nKeyboardInterrupt")
1261 print("\nKeyboardInterrupt")
1273
1262
1274
1263
1275 #----------------------------------------------------------------------------
1264 #----------------------------------------------------------------------------
1276 class FormattedTB(VerboseTB, ListTB):
1265 class FormattedTB(VerboseTB, ListTB):
1277 """Subclass ListTB but allow calling with a traceback.
1266 """Subclass ListTB but allow calling with a traceback.
1278
1267
1279 It can thus be used as a sys.excepthook for Python > 2.1.
1268 It can thus be used as a sys.excepthook for Python > 2.1.
1280
1269
1281 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1270 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1282
1271
1283 Allows a tb_offset to be specified. This is useful for situations where
1272 Allows a tb_offset to be specified. This is useful for situations where
1284 one needs to remove a number of topmost frames from the traceback (such as
1273 one needs to remove a number of topmost frames from the traceback (such as
1285 occurs with python programs that themselves execute other python code,
1274 occurs with python programs that themselves execute other python code,
1286 like Python shells). """
1275 like Python shells). """
1287
1276
1288 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1277 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1289 ostream=None,
1278 ostream=None,
1290 tb_offset=0, long_header=False, include_vars=False,
1279 tb_offset=0, long_header=False, include_vars=False,
1291 check_cache=None, debugger_cls=None):
1280 check_cache=None, debugger_cls=None):
1292
1281
1293 # NEVER change the order of this list. Put new modes at the end:
1282 # NEVER change the order of this list. Put new modes at the end:
1294 self.valid_modes = ['Plain', 'Context', 'Verbose']
1283 self.valid_modes = ['Plain', 'Context', 'Verbose']
1295 self.verbose_modes = self.valid_modes[1:3]
1284 self.verbose_modes = self.valid_modes[1:3]
1296
1285
1297 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1286 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1298 ostream=ostream, tb_offset=tb_offset,
1287 ostream=ostream, tb_offset=tb_offset,
1299 long_header=long_header, include_vars=include_vars,
1288 long_header=long_header, include_vars=include_vars,
1300 check_cache=check_cache, debugger_cls=debugger_cls)
1289 check_cache=check_cache, debugger_cls=debugger_cls)
1301
1290
1302 # Different types of tracebacks are joined with different separators to
1291 # Different types of tracebacks are joined with different separators to
1303 # form a single string. They are taken from this dict
1292 # form a single string. They are taken from this dict
1304 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1293 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1305 # set_mode also sets the tb_join_char attribute
1294 # set_mode also sets the tb_join_char attribute
1306 self.set_mode(mode)
1295 self.set_mode(mode)
1307
1296
1308 def _extract_tb(self, tb):
1297 def _extract_tb(self, tb):
1309 if tb:
1298 if tb:
1310 return traceback.extract_tb(tb)
1299 return traceback.extract_tb(tb)
1311 else:
1300 else:
1312 return None
1301 return None
1313
1302
1314 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1303 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1315 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1304 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1316 mode = self.mode
1305 mode = self.mode
1317 if mode in self.verbose_modes:
1306 if mode in self.verbose_modes:
1318 # Verbose modes need a full traceback
1307 # Verbose modes need a full traceback
1319 return VerboseTB.structured_traceback(
1308 return VerboseTB.structured_traceback(
1320 self, etype, value, tb, tb_offset, number_of_lines_of_context
1309 self, etype, value, tb, tb_offset, number_of_lines_of_context
1321 )
1310 )
1322 else:
1311 else:
1323 # We must check the source cache because otherwise we can print
1312 # We must check the source cache because otherwise we can print
1324 # out-of-date source code.
1313 # out-of-date source code.
1325 self.check_cache()
1314 self.check_cache()
1326 # Now we can extract and format the exception
1315 # Now we can extract and format the exception
1327 elist = self._extract_tb(tb)
1316 elist = self._extract_tb(tb)
1328 return ListTB.structured_traceback(
1317 return ListTB.structured_traceback(
1329 self, etype, value, elist, tb_offset, number_of_lines_of_context
1318 self, etype, value, elist, tb_offset, number_of_lines_of_context
1330 )
1319 )
1331
1320
1332 def stb2text(self, stb):
1321 def stb2text(self, stb):
1333 """Convert a structured traceback (a list) to a string."""
1322 """Convert a structured traceback (a list) to a string."""
1334 return self.tb_join_char.join(stb)
1323 return self.tb_join_char.join(stb)
1335
1324
1336
1325
1337 def set_mode(self, mode=None):
1326 def set_mode(self, mode=None):
1338 """Switch to the desired mode.
1327 """Switch to the desired mode.
1339
1328
1340 If mode is not specified, cycles through the available modes."""
1329 If mode is not specified, cycles through the available modes."""
1341
1330
1342 if not mode:
1331 if not mode:
1343 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1332 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1344 len(self.valid_modes)
1333 len(self.valid_modes)
1345 self.mode = self.valid_modes[new_idx]
1334 self.mode = self.valid_modes[new_idx]
1346 elif mode not in self.valid_modes:
1335 elif mode not in self.valid_modes:
1347 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
1336 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
1348 'Valid modes: ' + str(self.valid_modes))
1337 'Valid modes: ' + str(self.valid_modes))
1349 else:
1338 else:
1350 self.mode = mode
1339 self.mode = mode
1351 # include variable details only in 'Verbose' mode
1340 # include variable details only in 'Verbose' mode
1352 self.include_vars = (self.mode == self.valid_modes[2])
1341 self.include_vars = (self.mode == self.valid_modes[2])
1353 # Set the join character for generating text tracebacks
1342 # Set the join character for generating text tracebacks
1354 self.tb_join_char = self._join_chars[self.mode]
1343 self.tb_join_char = self._join_chars[self.mode]
1355
1344
1356 # some convenient shortcuts
1345 # some convenient shortcuts
1357 def plain(self):
1346 def plain(self):
1358 self.set_mode(self.valid_modes[0])
1347 self.set_mode(self.valid_modes[0])
1359
1348
1360 def context(self):
1349 def context(self):
1361 self.set_mode(self.valid_modes[1])
1350 self.set_mode(self.valid_modes[1])
1362
1351
1363 def verbose(self):
1352 def verbose(self):
1364 self.set_mode(self.valid_modes[2])
1353 self.set_mode(self.valid_modes[2])
1365
1354
1366
1355
1367 #----------------------------------------------------------------------------
1356 #----------------------------------------------------------------------------
1368 class AutoFormattedTB(FormattedTB):
1357 class AutoFormattedTB(FormattedTB):
1369 """A traceback printer which can be called on the fly.
1358 """A traceback printer which can be called on the fly.
1370
1359
1371 It will find out about exceptions by itself.
1360 It will find out about exceptions by itself.
1372
1361
1373 A brief example::
1362 A brief example::
1374
1363
1375 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1364 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1376 try:
1365 try:
1377 ...
1366 ...
1378 except:
1367 except:
1379 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1368 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1380 """
1369 """
1381
1370
1382 def __call__(self, etype=None, evalue=None, etb=None,
1371 def __call__(self, etype=None, evalue=None, etb=None,
1383 out=None, tb_offset=None):
1372 out=None, tb_offset=None):
1384 """Print out a formatted exception traceback.
1373 """Print out a formatted exception traceback.
1385
1374
1386 Optional arguments:
1375 Optional arguments:
1387 - out: an open file-like object to direct output to.
1376 - out: an open file-like object to direct output to.
1388
1377
1389 - tb_offset: the number of frames to skip over in the stack, on a
1378 - tb_offset: the number of frames to skip over in the stack, on a
1390 per-call basis (this overrides temporarily the instance's tb_offset
1379 per-call basis (this overrides temporarily the instance's tb_offset
1391 given at initialization time. """
1380 given at initialization time. """
1392
1381
1393 if out is None:
1382 if out is None:
1394 out = self.ostream
1383 out = self.ostream
1395 out.flush()
1384 out.flush()
1396 out.write(self.text(etype, evalue, etb, tb_offset))
1385 out.write(self.text(etype, evalue, etb, tb_offset))
1397 out.write('\n')
1386 out.write('\n')
1398 out.flush()
1387 out.flush()
1399 # FIXME: we should remove the auto pdb behavior from here and leave
1388 # FIXME: we should remove the auto pdb behavior from here and leave
1400 # that to the clients.
1389 # that to the clients.
1401 try:
1390 try:
1402 self.debugger()
1391 self.debugger()
1403 except KeyboardInterrupt:
1392 except KeyboardInterrupt:
1404 print("\nKeyboardInterrupt")
1393 print("\nKeyboardInterrupt")
1405
1394
1406 def structured_traceback(self, etype=None, value=None, tb=None,
1395 def structured_traceback(self, etype=None, value=None, tb=None,
1407 tb_offset=None, number_of_lines_of_context=5):
1396 tb_offset=None, number_of_lines_of_context=5):
1408 if etype is None:
1397 if etype is None:
1409 etype, value, tb = sys.exc_info()
1398 etype, value, tb = sys.exc_info()
1410 self.tb = tb
1399 self.tb = tb
1411 return FormattedTB.structured_traceback(
1400 return FormattedTB.structured_traceback(
1412 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1401 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1413
1402
1414
1403
1415 #---------------------------------------------------------------------------
1404 #---------------------------------------------------------------------------
1416
1405
1417 # A simple class to preserve Nathan's original functionality.
1406 # A simple class to preserve Nathan's original functionality.
1418 class ColorTB(FormattedTB):
1407 class ColorTB(FormattedTB):
1419 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1408 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1420
1409
1421 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1410 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1422 FormattedTB.__init__(self, color_scheme=color_scheme,
1411 FormattedTB.__init__(self, color_scheme=color_scheme,
1423 call_pdb=call_pdb, **kwargs)
1412 call_pdb=call_pdb, **kwargs)
1424
1413
1425
1414
1426 class SyntaxTB(ListTB):
1415 class SyntaxTB(ListTB):
1427 """Extension which holds some state: the last exception value"""
1416 """Extension which holds some state: the last exception value"""
1428
1417
1429 def __init__(self, color_scheme='NoColor'):
1418 def __init__(self, color_scheme='NoColor'):
1430 ListTB.__init__(self, color_scheme)
1419 ListTB.__init__(self, color_scheme)
1431 self.last_syntax_error = None
1420 self.last_syntax_error = None
1432
1421
1433 def __call__(self, etype, value, elist):
1422 def __call__(self, etype, value, elist):
1434 self.last_syntax_error = value
1423 self.last_syntax_error = value
1435
1424
1436 ListTB.__call__(self, etype, value, elist)
1425 ListTB.__call__(self, etype, value, elist)
1437
1426
1438 def structured_traceback(self, etype, value, elist, tb_offset=None,
1427 def structured_traceback(self, etype, value, elist, tb_offset=None,
1439 context=5):
1428 context=5):
1440 # If the source file has been edited, the line in the syntax error can
1429 # If the source file has been edited, the line in the syntax error can
1441 # be wrong (retrieved from an outdated cache). This replaces it with
1430 # be wrong (retrieved from an outdated cache). This replaces it with
1442 # the current value.
1431 # the current value.
1443 if isinstance(value, SyntaxError) \
1432 if isinstance(value, SyntaxError) \
1444 and isinstance(value.filename, py3compat.string_types) \
1433 and isinstance(value.filename, py3compat.string_types) \
1445 and isinstance(value.lineno, int):
1434 and isinstance(value.lineno, int):
1446 linecache.checkcache(value.filename)
1435 linecache.checkcache(value.filename)
1447 newtext = ulinecache.getline(value.filename, value.lineno)
1436 newtext = ulinecache.getline(value.filename, value.lineno)
1448 if newtext:
1437 if newtext:
1449 value.text = newtext
1438 value.text = newtext
1450 self.last_syntax_error = value
1439 self.last_syntax_error = value
1451 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1440 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1452 tb_offset=tb_offset, context=context)
1441 tb_offset=tb_offset, context=context)
1453
1442
1454 def clear_err_state(self):
1443 def clear_err_state(self):
1455 """Return the current error state and clear it"""
1444 """Return the current error state and clear it"""
1456 e = self.last_syntax_error
1445 e = self.last_syntax_error
1457 self.last_syntax_error = None
1446 self.last_syntax_error = None
1458 return e
1447 return e
1459
1448
1460 def stb2text(self, stb):
1449 def stb2text(self, stb):
1461 """Convert a structured traceback (a list) to a string."""
1450 """Convert a structured traceback (a list) to a string."""
1462 return ''.join(stb)
1451 return ''.join(stb)
1463
1452
1464
1453
1465 # some internal-use functions
1454 # some internal-use functions
1466 def text_repr(value):
1455 def text_repr(value):
1467 """Hopefully pretty robust repr equivalent."""
1456 """Hopefully pretty robust repr equivalent."""
1468 # this is pretty horrible but should always return *something*
1457 # this is pretty horrible but should always return *something*
1469 try:
1458 try:
1470 return pydoc.text.repr(value)
1459 return pydoc.text.repr(value)
1471 except KeyboardInterrupt:
1460 except KeyboardInterrupt:
1472 raise
1461 raise
1473 except:
1462 except:
1474 try:
1463 try:
1475 return repr(value)
1464 return repr(value)
1476 except KeyboardInterrupt:
1465 except KeyboardInterrupt:
1477 raise
1466 raise
1478 except:
1467 except:
1479 try:
1468 try:
1480 # all still in an except block so we catch
1469 # all still in an except block so we catch
1481 # getattr raising
1470 # getattr raising
1482 name = getattr(value, '__name__', None)
1471 name = getattr(value, '__name__', None)
1483 if name:
1472 if name:
1484 # ick, recursion
1473 # ick, recursion
1485 return text_repr(name)
1474 return text_repr(name)
1486 klass = getattr(value, '__class__', None)
1475 klass = getattr(value, '__class__', None)
1487 if klass:
1476 if klass:
1488 return '%s instance' % text_repr(klass)
1477 return '%s instance' % text_repr(klass)
1489 except KeyboardInterrupt:
1478 except KeyboardInterrupt:
1490 raise
1479 raise
1491 except:
1480 except:
1492 return 'UNRECOVERABLE REPR FAILURE'
1481 return 'UNRECOVERABLE REPR FAILURE'
1493
1482
1494
1483
1495 def eqrepr(value, repr=text_repr):
1484 def eqrepr(value, repr=text_repr):
1496 return '=%s' % repr(value)
1485 return '=%s' % repr(value)
1497
1486
1498
1487
1499 def nullrepr(value, repr=text_repr):
1488 def nullrepr(value, repr=text_repr):
1500 return ''
1489 return ''
@@ -1,382 +1,332 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Class and program to colorize python source code for ANSI terminals.
3 Class and program to colorize python source code for ANSI terminals.
4
4
5 Based on an HTML code highlighter by Jurgen Hermann found at:
5 Based on an HTML code highlighter by Jurgen Hermann found at:
6 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
6 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
7
7
8 Modifications by Fernando Perez (fperez@colorado.edu).
8 Modifications by Fernando Perez (fperez@colorado.edu).
9
9
10 Information on the original HTML highlighter follows:
10 Information on the original HTML highlighter follows:
11
11
12 MoinMoin - Python Source Parser
12 MoinMoin - Python Source Parser
13
13
14 Title: Colorize Python source using the built-in tokenizer
14 Title: Colorize Python source using the built-in tokenizer
15
15
16 Submitter: Jurgen Hermann
16 Submitter: Jurgen Hermann
17 Last Updated:2001/04/06
17 Last Updated:2001/04/06
18
18
19 Version no:1.2
19 Version no:1.2
20
20
21 Description:
21 Description:
22
22
23 This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
23 This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
24 Python source code to HTML markup, rendering comments, keywords,
24 Python source code to HTML markup, rendering comments, keywords,
25 operators, numeric and string literals in different colors.
25 operators, numeric and string literals in different colors.
26
26
27 It shows how to use the built-in keyword, token and tokenize modules to
27 It shows how to use the built-in keyword, token and tokenize modules to
28 scan Python source code and re-emit it with no changes to its original
28 scan Python source code and re-emit it with no changes to its original
29 formatting (which is the hard part).
29 formatting (which is the hard part).
30 """
30 """
31 from __future__ import print_function
31 from __future__ import print_function
32 from __future__ import absolute_import
32 from __future__ import absolute_import
33 from __future__ import unicode_literals
33 from __future__ import unicode_literals
34
34
35 __all__ = ['ANSICodeColors','Parser']
35 __all__ = ['ANSICodeColors','Parser']
36
36
37 _scheme_default = 'Linux'
37 _scheme_default = 'Linux'
38
38
39
39
40 # Imports
40 # Imports
41 import keyword
41 import keyword
42 import os
42 import os
43 import sys
43 import sys
44 import token
44 import token
45 import tokenize
45 import tokenize
46
46
47 try:
47 try:
48 generate_tokens = tokenize.generate_tokens
48 generate_tokens = tokenize.generate_tokens
49 except AttributeError:
49 except AttributeError:
50 # Python 3. Note that we use the undocumented _tokenize because it expects
50 # Python 3. Note that we use the undocumented _tokenize because it expects
51 # strings, not bytes. See also Python issue #9969.
51 # strings, not bytes. See also Python issue #9969.
52 generate_tokens = tokenize._tokenize
52 generate_tokens = tokenize._tokenize
53
53
54 from IPython.utils.coloransi import TermColors, InputTermColors ,ColorScheme, ColorSchemeTable
54 from IPython.utils.coloransi import TermColors, InputTermColors ,ColorScheme, ColorSchemeTable
55 from IPython.utils.py3compat import PY3
55 from IPython.utils.py3compat import PY3
56
56
57 from .colorable import Colorable
57 from .colorable import Colorable
58
58
59 if PY3:
59 if PY3:
60 from io import StringIO
60 from io import StringIO
61 else:
61 else:
62 from StringIO import StringIO
62 from StringIO import StringIO
63
63
64 #############################################################################
64 #############################################################################
65 ### Python Source Parser (does Hilighting)
65 ### Python Source Parser (does Hilighting)
66 #############################################################################
66 #############################################################################
67
67
68 _KEYWORD = token.NT_OFFSET + 1
68 _KEYWORD = token.NT_OFFSET + 1
69 _TEXT = token.NT_OFFSET + 2
69 _TEXT = token.NT_OFFSET + 2
70
70
71 #****************************************************************************
71 #****************************************************************************
72 # Builtin color schemes
72 # Builtin color schemes
73
73
74 Colors = TermColors # just a shorthand
74 Colors = TermColors # just a shorthand
75
75
76 # Build a few color schemes
76 # Build a few color schemes
77 NoColor = ColorScheme(
77 NoColor = ColorScheme(
78 'NoColor',{
78 'NoColor',{
79 'header' : Colors.NoColor,
79 'header' : Colors.NoColor,
80 token.NUMBER : Colors.NoColor,
80 token.NUMBER : Colors.NoColor,
81 token.OP : Colors.NoColor,
81 token.OP : Colors.NoColor,
82 token.STRING : Colors.NoColor,
82 token.STRING : Colors.NoColor,
83 tokenize.COMMENT : Colors.NoColor,
83 tokenize.COMMENT : Colors.NoColor,
84 token.NAME : Colors.NoColor,
84 token.NAME : Colors.NoColor,
85 token.ERRORTOKEN : Colors.NoColor,
85 token.ERRORTOKEN : Colors.NoColor,
86
86
87 _KEYWORD : Colors.NoColor,
87 _KEYWORD : Colors.NoColor,
88 _TEXT : Colors.NoColor,
88 _TEXT : Colors.NoColor,
89
89
90 'in_prompt' : InputTermColors.NoColor, # Input prompt
90 'in_prompt' : InputTermColors.NoColor, # Input prompt
91 'in_number' : InputTermColors.NoColor, # Input prompt number
91 'in_number' : InputTermColors.NoColor, # Input prompt number
92 'in_prompt2' : InputTermColors.NoColor, # Continuation prompt
92 'in_prompt2' : InputTermColors.NoColor, # Continuation prompt
93 'in_normal' : InputTermColors.NoColor, # color off (usu. Colors.Normal)
93 'in_normal' : InputTermColors.NoColor, # color off (usu. Colors.Normal)
94
94
95 'out_prompt' : Colors.NoColor, # Output prompt
95 'out_prompt' : Colors.NoColor, # Output prompt
96 'out_number' : Colors.NoColor, # Output prompt number
96 'out_number' : Colors.NoColor, # Output prompt number
97
97
98 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
98 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
99 } )
99 } )
100
100
101 LinuxColors = ColorScheme(
101 LinuxColors = ColorScheme(
102 'Linux',{
102 'Linux',{
103 'header' : Colors.LightRed,
103 'header' : Colors.LightRed,
104 token.NUMBER : Colors.LightCyan,
104 token.NUMBER : Colors.LightCyan,
105 token.OP : Colors.Yellow,
105 token.OP : Colors.Yellow,
106 token.STRING : Colors.LightBlue,
106 token.STRING : Colors.LightBlue,
107 tokenize.COMMENT : Colors.LightRed,
107 tokenize.COMMENT : Colors.LightRed,
108 token.NAME : Colors.Normal,
108 token.NAME : Colors.Normal,
109 token.ERRORTOKEN : Colors.Red,
109 token.ERRORTOKEN : Colors.Red,
110
110
111 _KEYWORD : Colors.LightGreen,
111 _KEYWORD : Colors.LightGreen,
112 _TEXT : Colors.Yellow,
112 _TEXT : Colors.Yellow,
113
113
114 'in_prompt' : InputTermColors.Green,
114 'in_prompt' : InputTermColors.Green,
115 'in_number' : InputTermColors.LightGreen,
115 'in_number' : InputTermColors.LightGreen,
116 'in_prompt2' : InputTermColors.Green,
116 'in_prompt2' : InputTermColors.Green,
117 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
117 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
118
118
119 'out_prompt' : Colors.Red,
119 'out_prompt' : Colors.Red,
120 'out_number' : Colors.LightRed,
120 'out_number' : Colors.LightRed,
121
121
122 'normal' : Colors.Normal # color off (usu. Colors.Normal)
122 'normal' : Colors.Normal # color off (usu. Colors.Normal)
123 } )
123 } )
124
124
125 NeutralColors = ColorScheme(
125 NeutralColors = ColorScheme(
126 'Neutral',{
126 'Neutral',{
127 'header' : Colors.Red,
127 'header' : Colors.Red,
128 token.NUMBER : Colors.Cyan,
128 token.NUMBER : Colors.Cyan,
129 token.OP : Colors.Blue,
129 token.OP : Colors.Blue,
130 token.STRING : Colors.Blue,
130 token.STRING : Colors.Blue,
131 tokenize.COMMENT : Colors.Red,
131 tokenize.COMMENT : Colors.Red,
132 token.NAME : Colors.Normal,
132 token.NAME : Colors.Normal,
133 token.ERRORTOKEN : Colors.Red,
133 token.ERRORTOKEN : Colors.Red,
134
134
135 _KEYWORD : Colors.Green,
135 _KEYWORD : Colors.Green,
136 _TEXT : Colors.Blue,
136 _TEXT : Colors.Blue,
137
137
138 'in_prompt' : InputTermColors.Blue,
138 'in_prompt' : InputTermColors.Blue,
139 'in_number' : InputTermColors.LightBlue,
139 'in_number' : InputTermColors.LightBlue,
140 'in_prompt2' : InputTermColors.Blue,
140 'in_prompt2' : InputTermColors.Blue,
141 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
141 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
142
142
143 'out_prompt' : Colors.Red,
143 'out_prompt' : Colors.Red,
144 'out_number' : Colors.LightRed,
144 'out_number' : Colors.LightRed,
145
145
146 'normal' : Colors.Normal # color off (usu. Colors.Normal)
146 'normal' : Colors.Normal # color off (usu. Colors.Normal)
147 } )
147 } )
148
148
149 # Hack: the 'neutral' colours are not very visible on a dark background on
149 # Hack: the 'neutral' colours are not very visible on a dark background on
150 # Windows. Since Windows command prompts have a dark background by default, and
150 # Windows. Since Windows command prompts have a dark background by default, and
151 # relatively few users are likely to alter that, we will use the 'Linux' colours,
151 # relatively few users are likely to alter that, we will use the 'Linux' colours,
152 # designed for a dark background, as the default on Windows. Changing it here
152 # designed for a dark background, as the default on Windows. Changing it here
153 # avoids affecting the prompt colours rendered by prompt_toolkit, where the
153 # avoids affecting the prompt colours rendered by prompt_toolkit, where the
154 # neutral defaults do work OK.
154 # neutral defaults do work OK.
155
155
156 if os.name == 'nt':
156 if os.name == 'nt':
157 NeutralColors = LinuxColors.copy(name='Neutral')
157 NeutralColors = LinuxColors.copy(name='Neutral')
158
158
159 LightBGColors = ColorScheme(
159 LightBGColors = ColorScheme(
160 'LightBG',{
160 'LightBG',{
161 'header' : Colors.Red,
161 'header' : Colors.Red,
162 token.NUMBER : Colors.Cyan,
162 token.NUMBER : Colors.Cyan,
163 token.OP : Colors.Blue,
163 token.OP : Colors.Blue,
164 token.STRING : Colors.Blue,
164 token.STRING : Colors.Blue,
165 tokenize.COMMENT : Colors.Red,
165 tokenize.COMMENT : Colors.Red,
166 token.NAME : Colors.Normal,
166 token.NAME : Colors.Normal,
167 token.ERRORTOKEN : Colors.Red,
167 token.ERRORTOKEN : Colors.Red,
168
168
169
169
170 _KEYWORD : Colors.Green,
170 _KEYWORD : Colors.Green,
171 _TEXT : Colors.Blue,
171 _TEXT : Colors.Blue,
172
172
173 'in_prompt' : InputTermColors.Blue,
173 'in_prompt' : InputTermColors.Blue,
174 'in_number' : InputTermColors.LightBlue,
174 'in_number' : InputTermColors.LightBlue,
175 'in_prompt2' : InputTermColors.Blue,
175 'in_prompt2' : InputTermColors.Blue,
176 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
176 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
177
177
178 'out_prompt' : Colors.Red,
178 'out_prompt' : Colors.Red,
179 'out_number' : Colors.LightRed,
179 'out_number' : Colors.LightRed,
180
180
181 'normal' : Colors.Normal # color off (usu. Colors.Normal)
181 'normal' : Colors.Normal # color off (usu. Colors.Normal)
182 } )
182 } )
183
183
184 # Build table of color schemes (needed by the parser)
184 # Build table of color schemes (needed by the parser)
185 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors, NeutralColors],
185 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors, NeutralColors],
186 _scheme_default)
186 _scheme_default)
187
187
188 Undefined = object()
189
188 class Parser(Colorable):
190 class Parser(Colorable):
189 """ Format colored Python source.
191 """ Format colored Python source.
190 """
192 """
191
193
192 def __init__(self, color_table=None, out = sys.stdout, parent=None, style=None):
194 def __init__(self, color_table=None, out = sys.stdout, parent=None, style=None):
193 """ Create a parser with a specified color table and output channel.
195 """ Create a parser with a specified color table and output channel.
194
196
195 Call format() to process code.
197 Call format() to process code.
196 """
198 """
197
199
198 super(Parser, self).__init__(parent=parent)
200 super(Parser, self).__init__(parent=parent)
199
201
200 self.color_table = color_table and color_table or ANSICodeColors
202 self.color_table = color_table and color_table or ANSICodeColors
201 self.out = out
203 self.out = out
204 if not style:
205 self.style = self.default_style
206 else:
207 self.style = style
208
202
209
203 def format(self, raw, out = None, scheme = ''):
210 def format(self, raw, out=None, scheme=Undefined):
204 return self.format2(raw, out, scheme)[0]
211 import warnings
212 if scheme is not Undefined:
213 warnings.warn('The `scheme` argument of IPython.utils.PyColorize:Parser.format is deprecated since IPython 6.0.'
214 'It will have no effect. Set the parser `style` directly.',
215 stacklevel=2)
216 return self.format2(raw, out)[0]
205
217
206 def format2(self, raw, out = None, scheme = ''):
218 def format2(self, raw, out = None):
207 """ Parse and send the colored source.
219 """ Parse and send the colored source.
208
220
209 If out and scheme are not specified, the defaults (given to
221 If out and scheme are not specified, the defaults (given to
210 constructor) are used.
222 constructor) are used.
211
223
212 out should be a file-type object. Optionally, out can be given as the
224 out should be a file-type object. Optionally, out can be given as the
213 string 'str' and the parser will automatically return the output in a
225 string 'str' and the parser will automatically return the output in a
214 string."""
226 string."""
215
227
216 string_output = 0
228 string_output = 0
217 if out == 'str' or self.out == 'str' or \
229 if out == 'str' or self.out == 'str' or \
218 isinstance(self.out,StringIO):
230 isinstance(self.out,StringIO):
219 # XXX - I don't really like this state handling logic, but at this
231 # XXX - I don't really like this state handling logic, but at this
220 # point I don't want to make major changes, so adding the
232 # point I don't want to make major changes, so adding the
221 # isinstance() check is the simplest I can do to ensure correct
233 # isinstance() check is the simplest I can do to ensure correct
222 # behavior.
234 # behavior.
223 out_old = self.out
235 out_old = self.out
224 self.out = StringIO()
236 self.out = StringIO()
225 string_output = 1
237 string_output = 1
226 elif out is not None:
238 elif out is not None:
227 self.out = out
239 self.out = out
228
240
229 # Fast return of the unmodified input for NoColor scheme
241 # Fast return of the unmodified input for NoColor scheme
230 if scheme == 'NoColor':
242 if self.style == 'NoColor':
231 error = False
243 error = False
232 self.out.write(raw)
244 self.out.write(raw)
233 if string_output:
245 if string_output:
234 return raw,error
246 return raw,error
235 else:
247 else:
236 return None,error
248 return None,error
237
249
238 # local shorthands
250 # local shorthands
239 colors = self.color_table[scheme].colors
251 colors = self.color_table[self.style].colors
240 self.colors = colors # put in object so __call__ sees it
252 self.colors = colors # put in object so __call__ sees it
241
253
242 # Remove trailing whitespace and normalize tabs
254 # Remove trailing whitespace and normalize tabs
243 self.raw = raw.expandtabs().rstrip()
255 self.raw = raw.expandtabs().rstrip()
244
256
245 # store line offsets in self.lines
257 # store line offsets in self.lines
246 self.lines = [0, 0]
258 self.lines = [0, 0]
247 pos = 0
259 pos = 0
248 raw_find = self.raw.find
260 raw_find = self.raw.find
249 lines_append = self.lines.append
261 lines_append = self.lines.append
250 while 1:
262 while 1:
251 pos = raw_find('\n', pos) + 1
263 pos = raw_find('\n', pos) + 1
252 if not pos: break
264 if not pos: break
253 lines_append(pos)
265 lines_append(pos)
254 lines_append(len(self.raw))
266 lines_append(len(self.raw))
255
267
256 # parse the source and write it
268 # parse the source and write it
257 self.pos = 0
269 self.pos = 0
258 text = StringIO(self.raw)
270 text = StringIO(self.raw)
259
271
260 error = False
272 error = False
261 try:
273 try:
262 for atoken in generate_tokens(text.readline):
274 for atoken in generate_tokens(text.readline):
263 self(*atoken)
275 self(*atoken)
264 except tokenize.TokenError as ex:
276 except tokenize.TokenError as ex:
265 msg = ex.args[0]
277 msg = ex.args[0]
266 line = ex.args[1][0]
278 line = ex.args[1][0]
267 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
279 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
268 (colors[token.ERRORTOKEN],
280 (colors[token.ERRORTOKEN],
269 msg, self.raw[self.lines[line]:],
281 msg, self.raw[self.lines[line]:],
270 colors.normal)
282 colors.normal)
271 )
283 )
272 error = True
284 error = True
273 self.out.write(colors.normal+'\n')
285 self.out.write(colors.normal+'\n')
274 if string_output:
286 if string_output:
275 output = self.out.getvalue()
287 output = self.out.getvalue()
276 self.out = out_old
288 self.out = out_old
277 return (output, error)
289 return (output, error)
278 return (None, error)
290 return (None, error)
279
291
280 def __call__(self, toktype, toktext, start_pos, end_pos, line):
292 def __call__(self, toktype, toktext, start_pos, end_pos, line):
281 """ Token handler, with syntax highlighting."""
293 """ Token handler, with syntax highlighting."""
282 (srow,scol) = start_pos
294 (srow,scol) = start_pos
283 (erow,ecol) = end_pos
295 (erow,ecol) = end_pos
284 colors = self.colors
296 colors = self.colors
285 owrite = self.out.write
297 owrite = self.out.write
286
298
287 # line separator, so this works across platforms
299 # line separator, so this works across platforms
288 linesep = os.linesep
300 linesep = os.linesep
289
301
290 # calculate new positions
302 # calculate new positions
291 oldpos = self.pos
303 oldpos = self.pos
292 newpos = self.lines[srow] + scol
304 newpos = self.lines[srow] + scol
293 self.pos = newpos + len(toktext)
305 self.pos = newpos + len(toktext)
294
306
295 # send the original whitespace, if needed
307 # send the original whitespace, if needed
296 if newpos > oldpos:
308 if newpos > oldpos:
297 owrite(self.raw[oldpos:newpos])
309 owrite(self.raw[oldpos:newpos])
298
310
299 # skip indenting tokens
311 # skip indenting tokens
300 if toktype in [token.INDENT, token.DEDENT]:
312 if toktype in [token.INDENT, token.DEDENT]:
301 self.pos = newpos
313 self.pos = newpos
302 return
314 return
303
315
304 # map token type to a color group
316 # map token type to a color group
305 if token.LPAR <= toktype <= token.OP:
317 if token.LPAR <= toktype <= token.OP:
306 toktype = token.OP
318 toktype = token.OP
307 elif toktype == token.NAME and keyword.iskeyword(toktext):
319 elif toktype == token.NAME and keyword.iskeyword(toktext):
308 toktype = _KEYWORD
320 toktype = _KEYWORD
309 color = colors.get(toktype, colors[_TEXT])
321 color = colors.get(toktype, colors[_TEXT])
310
322
311 #print '<%s>' % toktext, # dbg
323 #print '<%s>' % toktext, # dbg
312
324
313 # Triple quoted strings must be handled carefully so that backtracking
325 # Triple quoted strings must be handled carefully so that backtracking
314 # in pagers works correctly. We need color terminators on _each_ line.
326 # in pagers works correctly. We need color terminators on _each_ line.
315 if linesep in toktext:
327 if linesep in toktext:
316 toktext = toktext.replace(linesep, '%s%s%s' %
328 toktext = toktext.replace(linesep, '%s%s%s' %
317 (colors.normal,linesep,color))
329 (colors.normal,linesep,color))
318
330
319 # send text
331 # send text
320 owrite('%s%s%s' % (color,toktext,colors.normal))
332 owrite('%s%s%s' % (color,toktext,colors.normal))
321
322 def main(argv=None):
323 """Run as a command-line script: colorize a python file or stdin using ANSI
324 color escapes and print to stdout.
325
326 Inputs:
327
328 - argv(None): a list of strings like sys.argv[1:] giving the command-line
329 arguments. If None, use sys.argv[1:].
330 """
331
332 usage_msg = """%prog [options] [filename]
333
334 Colorize a python file or stdin using ANSI color escapes and print to stdout.
335 If no filename is given, or if filename is -, read standard input."""
336
337 import optparse
338 parser = optparse.OptionParser(usage=usage_msg)
339 newopt = parser.add_option
340 newopt('-s','--scheme',metavar='NAME',dest='scheme_name',action='store',
341 choices=['Linux','LightBG','NoColor'],default=_scheme_default,
342 help="give the color scheme to use. Currently only 'Linux'\
343 (default) and 'LightBG' and 'NoColor' are implemented (give without\
344 quotes)")
345
346 opts,args = parser.parse_args(argv)
347
348 if len(args) > 1:
349 parser.error("you must give at most one filename.")
350
351 if len(args) == 0:
352 fname = '-' # no filename given; setup to read from stdin
353 else:
354 fname = args[0]
355
356 if fname == '-':
357 stream = sys.stdin
358 else:
359 try:
360 stream = open(fname)
361 except IOError as msg:
362 print(msg, file=sys.stderr)
363 sys.exit(1)
364
365 parser = Parser()
366
367 # we need nested try blocks because pre-2.5 python doesn't support unified
368 # try-except-finally
369 try:
370 try:
371 # write colorized version to stdout
372 parser.format(stream.read(),scheme=opts.scheme_name)
373 except IOError as msg:
374 # if user reads through a pager and quits, don't print traceback
375 if msg.args != (32,'Broken pipe'):
376 raise
377 finally:
378 if stream is not sys.stdin:
379 stream.close() # in case a non-handled exception happened above
380
381 if __name__ == "__main__":
382 main()
@@ -1,26 +1,26 b''
1 #*****************************************************************************
1 #*****************************************************************************
2 # Copyright (C) 2016 The IPython Team <ipython-dev@scipy.org>
2 # Copyright (C) 2016 The IPython Team <ipython-dev@scipy.org>
3 #
3 #
4 # Distributed under the terms of the BSD License. The full license is in
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING, distributed as part of this software.
5 # the file COPYING, distributed as part of this software.
6 #*****************************************************************************
6 #*****************************************************************************
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 """
9 """
10 Color managing related utilities
10 Color managing related utilities
11 """
11 """
12
12
13 import pygments
13 import pygments
14
14
15 from traitlets.config import Configurable
15 from traitlets.config import Configurable
16 from traitlets import Unicode
16 from traitlets import Unicode
17
17
18
18
19 available_themes = lambda : [s for s in pygments.styles.get_all_styles()]+['NoColor','LightBG','Linux', 'Neutral']
19 available_themes = lambda : [s for s in pygments.styles.get_all_styles()]+['NoColor','LightBG','Linux', 'Neutral']
20
20
21 class Colorable(Configurable):
21 class Colorable(Configurable):
22 """
22 """
23 A subclass of configurable for all the classes that have a `default_scheme`
23 A subclass of configurable for all the classes that have a `default_scheme`
24 """
24 """
25 default_style=Unicode('lightbg').tag(config=True)
25 default_style=Unicode('LightBG').tag(config=True)
26
26
@@ -1,78 +1,78 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Test suite for our color utilities.
2 """Test suite for our color utilities.
3
3
4 Authors
4 Authors
5 -------
5 -------
6
6
7 * Min RK
7 * Min RK
8 """
8 """
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2011 The IPython Development Team
10 # Copyright (C) 2011 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING.txt, distributed as part of this software.
13 # the file COPYING.txt, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 # third party
20 # third party
21 import nose.tools as nt
21 import nose.tools as nt
22
22
23 # our own
23 # our own
24 from IPython.utils.PyColorize import Parser
24 from IPython.utils.PyColorize import Parser
25 import io
25 import io
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Test functions
28 # Test functions
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 sample = u"""
31 sample = u"""
32 def function(arg, *args, kwarg=True, **kwargs):
32 def function(arg, *args, kwarg=True, **kwargs):
33 '''
33 '''
34 this is docs
34 this is docs
35 '''
35 '''
36 pass is True
36 pass is True
37 False == None
37 False == None
38
38
39 with io.open(ru'unicode'):
39 with io.open(ru'unicode'):
40 raise ValueError("\n escape \r sequence")
40 raise ValueError("\n escape \r sequence")
41
41
42 print("wΔ›ird ΓΌnicoΓ°e")
42 print("wΔ›ird ΓΌnicoΓ°e")
43
43
44 class Bar(Super):
44 class Bar(Super):
45
45
46 def __init__(self):
46 def __init__(self):
47 super(Bar, self).__init__(1**2, 3^4, 5 or 6)
47 super(Bar, self).__init__(1**2, 3^4, 5 or 6)
48 """
48 """
49
49
50 def test_loop_colors():
50 def test_loop_colors():
51
51
52 for scheme in ('Linux', 'NoColor','LightBG', 'Neutral'):
52 for style in ('Linux', 'NoColor','LightBG', 'Neutral'):
53
53
54 def test_unicode_colorize():
54 def test_unicode_colorize():
55 p = Parser()
55 p = Parser(style=style)
56 f1 = p.format('1/0', 'str', scheme=scheme)
56 f1 = p.format('1/0', 'str')
57 f2 = p.format(u'1/0', 'str', scheme=scheme)
57 f2 = p.format(u'1/0', 'str')
58 nt.assert_equal(f1, f2)
58 nt.assert_equal(f1, f2)
59
59
60 def test_parse_sample():
60 def test_parse_sample():
61 """and test writing to a buffer"""
61 """and test writing to a buffer"""
62 buf = io.StringIO()
62 buf = io.StringIO()
63 p = Parser()
63 p = Parser(style=style)
64 p.format(sample, buf, scheme=scheme)
64 p.format(sample, buf)
65 buf.seek(0)
65 buf.seek(0)
66 f1 = buf.read()
66 f1 = buf.read()
67
67
68 nt.assert_not_in('ERROR', f1)
68 nt.assert_not_in('ERROR', f1)
69
69
70 def test_parse_error():
70 def test_parse_error():
71 p = Parser()
71 p = Parser(style=style)
72 f1 = p.format(')', 'str', scheme=scheme)
72 f1 = p.format(')', 'str')
73 if scheme != 'NoColor':
73 if style != 'NoColor':
74 nt.assert_in('ERROR', f1)
74 nt.assert_in('ERROR', f1)
75
75
76 yield test_unicode_colorize
76 yield test_unicode_colorize
77 yield test_parse_sample
77 yield test_parse_sample
78 yield test_parse_error
78 yield test_parse_error
General Comments 0
You need to be logged in to leave comments. Login now