##// END OF EJS Templates
Remove readline code from debugger machinery
Thomas Kluyver -
Show More
@@ -1,644 +1,630 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
33
34 from IPython import get_ipython
34 from IPython import get_ipython
35 from IPython.utils import PyColorize, ulinecache
35 from IPython.utils import PyColorize, ulinecache
36 from IPython.utils import coloransi, py3compat
36 from IPython.utils import coloransi, py3compat
37 from IPython.core.excolors import exception_colors
37 from IPython.core.excolors import exception_colors
38 from IPython.testing.skipdoctest import skip_doctest
38 from IPython.testing.skipdoctest import skip_doctest
39
39
40 from prompt_toolkit import prompt as ptk_prompt
40 from prompt_toolkit import prompt as ptk_prompt
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 if et==bdb.BdbQuit:
66 if et==bdb.BdbQuit:
67 print('Exiting Debugger.')
67 print('Exiting Debugger.')
68 elif excepthook is not None:
68 elif excepthook is not None:
69 excepthook(et, ev, tb)
69 excepthook(et, ev, tb)
70 else:
70 else:
71 # Backwards compatibility. Raise deprecation warning?
71 # Backwards compatibility. Raise deprecation warning?
72 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
72 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
73
73
74 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
74 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
75 print('Exiting Debugger.')
75 print('Exiting Debugger.')
76
76
77
77
78 class Tracer(object):
78 class Tracer(object):
79 """Class for local debugging, similar to pdb.set_trace.
79 """Class for local debugging, similar to pdb.set_trace.
80
80
81 Instances of this class, when called, behave like pdb.set_trace, but
81 Instances of this class, when called, behave like pdb.set_trace, but
82 providing IPython's enhanced capabilities.
82 providing IPython's enhanced capabilities.
83
83
84 This is implemented as a class which must be initialized in your own code
84 This is implemented as a class which must be initialized in your own code
85 and not as a standalone function because we need to detect at runtime
85 and not as a standalone function because we need to detect at runtime
86 whether IPython is already active or not. That detection is done in the
86 whether IPython is already active or not. That detection is done in the
87 constructor, ensuring that this code plays nicely with a running IPython,
87 constructor, ensuring that this code plays nicely with a running IPython,
88 while functioning acceptably (though with limitations) if outside of it.
88 while functioning acceptably (though with limitations) if outside of it.
89 """
89 """
90
90
91 @skip_doctest
91 @skip_doctest
92 def __init__(self, colors=None):
92 def __init__(self, colors=None):
93 """Create a local debugger instance.
93 """Create a local debugger instance.
94
94
95 Parameters
95 Parameters
96 ----------
96 ----------
97
97
98 colors : str, optional
98 colors : str, optional
99 The name of the color scheme to use, it must be one of IPython's
99 The name of the color scheme to use, it must be one of IPython's
100 valid color schemes. If not given, the function will default to
100 valid color schemes. If not given, the function will default to
101 the current IPython scheme when running inside IPython, and to
101 the current IPython scheme when running inside IPython, and to
102 'NoColor' otherwise.
102 'NoColor' otherwise.
103
103
104 Examples
104 Examples
105 --------
105 --------
106 ::
106 ::
107
107
108 from IPython.core.debugger import Tracer; debug_here = Tracer()
108 from IPython.core.debugger import Tracer; debug_here = Tracer()
109
109
110 Later in your code::
110 Later in your code::
111
111
112 debug_here() # -> will open up the debugger at that point.
112 debug_here() # -> will open up the debugger at that point.
113
113
114 Once the debugger activates, you can use all of its regular commands to
114 Once the debugger activates, you can use all of its regular commands to
115 step through code, set breakpoints, etc. See the pdb documentation
115 step through code, set breakpoints, etc. See the pdb documentation
116 from the Python standard library for usage details.
116 from the Python standard library for usage details.
117 """
117 """
118
118
119 ip = get_ipython()
119 ip = get_ipython()
120 if ip is None:
120 if ip is None:
121 # Outside of ipython, we set our own exception hook manually
121 # Outside of ipython, we set our own exception hook manually
122 sys.excepthook = functools.partial(BdbQuit_excepthook,
122 sys.excepthook = functools.partial(BdbQuit_excepthook,
123 excepthook=sys.excepthook)
123 excepthook=sys.excepthook)
124 def_colors = 'NoColor'
124 def_colors = 'NoColor'
125 try:
126 # Limited tab completion support
127 import readline
128 readline.parse_and_bind('tab: complete')
129 except ImportError:
130 pass
131 else:
125 else:
132 # In ipython, we use its custom exception handler mechanism
126 # In ipython, we use its custom exception handler mechanism
133 def_colors = ip.colors
127 def_colors = ip.colors
134 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
128 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
135
129
136 if colors is None:
130 if colors is None:
137 colors = def_colors
131 colors = def_colors
138
132
139 # The stdlib debugger internally uses a modified repr from the `repr`
133 # The stdlib debugger internally uses a modified repr from the `repr`
140 # module, that limits the length of printed strings to a hardcoded
134 # module, that limits the length of printed strings to a hardcoded
141 # limit of 30 characters. That much trimming is too aggressive, let's
135 # limit of 30 characters. That much trimming is too aggressive, let's
142 # at least raise that limit to 80 chars, which should be enough for
136 # at least raise that limit to 80 chars, which should be enough for
143 # most interactive uses.
137 # most interactive uses.
144 try:
138 try:
145 try:
139 try:
146 from reprlib import aRepr # Py 3
140 from reprlib import aRepr # Py 3
147 except ImportError:
141 except ImportError:
148 from repr import aRepr # Py 2
142 from repr import aRepr # Py 2
149 aRepr.maxstring = 80
143 aRepr.maxstring = 80
150 except:
144 except:
151 # This is only a user-facing convenience, so any error we encounter
145 # This is only a user-facing convenience, so any error we encounter
152 # here can be warned about but can be otherwise ignored. These
146 # here can be warned about but can be otherwise ignored. These
153 # printouts will tell us about problems if this API changes
147 # printouts will tell us about problems if this API changes
154 import traceback
148 import traceback
155 traceback.print_exc()
149 traceback.print_exc()
156
150
157 self.debugger = Pdb(colors)
151 self.debugger = Pdb(colors)
158
152
159 def __call__(self):
153 def __call__(self):
160 """Starts an interactive debugger at the point where called.
154 """Starts an interactive debugger at the point where called.
161
155
162 This is similar to the pdb.set_trace() function from the std lib, but
156 This is similar to the pdb.set_trace() function from the std lib, but
163 using IPython's enhanced debugger."""
157 using IPython's enhanced debugger."""
164
158
165 self.debugger.set_trace(sys._getframe().f_back)
159 self.debugger.set_trace(sys._getframe().f_back)
166
160
167
161
168 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
162 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
169 """Make new_fn have old_fn's doc string. This is particularly useful
163 """Make new_fn have old_fn's doc string. This is particularly useful
170 for the ``do_...`` commands that hook into the help system.
164 for the ``do_...`` commands that hook into the help system.
171 Adapted from from a comp.lang.python posting
165 Adapted from from a comp.lang.python posting
172 by Duncan Booth."""
166 by Duncan Booth."""
173 def wrapper(*args, **kw):
167 def wrapper(*args, **kw):
174 return new_fn(*args, **kw)
168 return new_fn(*args, **kw)
175 if old_fn.__doc__:
169 if old_fn.__doc__:
176 wrapper.__doc__ = old_fn.__doc__ + additional_text
170 wrapper.__doc__ = old_fn.__doc__ + additional_text
177 return wrapper
171 return wrapper
178
172
179
173
180 def _file_lines(fname):
174 def _file_lines(fname):
181 """Return the contents of a named file as a list of lines.
175 """Return the contents of a named file as a list of lines.
182
176
183 This function never raises an IOError exception: if the file can't be
177 This function never raises an IOError exception: if the file can't be
184 read, it simply returns an empty list."""
178 read, it simply returns an empty list."""
185
179
186 try:
180 try:
187 outfile = open(fname)
181 outfile = open(fname)
188 except IOError:
182 except IOError:
189 return []
183 return []
190 else:
184 else:
191 out = outfile.readlines()
185 out = outfile.readlines()
192 outfile.close()
186 outfile.close()
193 return out
187 return out
194
188
195
189
196 class Pdb(OldPdb, object):
190 class Pdb(OldPdb, object):
197 """Modified Pdb class, does not load readline."""
191 """Modified Pdb class, does not load readline."""
198
192
199 def __init__(self,color_scheme='NoColor',completekey=None,
193 def __init__(self,color_scheme='NoColor',completekey=None,
200 stdin=None, stdout=None, context=5):
194 stdin=None, stdout=None, context=5):
201
195
202 # Parent constructor:
196 # Parent constructor:
203 try:
197 try:
204 self.context=int(context)
198 self.context=int(context)
205 if self.context <= 0:
199 if self.context <= 0:
206 raise ValueError("Context must be a positive integer")
200 raise ValueError("Context must be a positive integer")
207 except (TypeError, ValueError):
201 except (TypeError, ValueError):
208 raise ValueError("Context must be a positive integer")
202 raise ValueError("Context must be a positive integer")
209
203
210 OldPdb.__init__(self,completekey,stdin,stdout)
204 OldPdb.__init__(self,completekey,stdin,stdout)
211
205
212 # IPython changes...
206 # IPython changes...
213 self.shell = get_ipython()
207 self.shell = get_ipython()
214
208
215 if self.shell is None:
209 if self.shell is None:
216 # No IPython instance running, we must create one
210 # No IPython instance running, we must create one
217 from IPython.terminal.interactiveshell import \
211 from IPython.terminal.interactiveshell import \
218 TerminalInteractiveShell
212 TerminalInteractiveShell
219 self.shell = TerminalInteractiveShell.instance()
213 self.shell = TerminalInteractiveShell.instance()
220
214
221 # This is icky, but I'm not currently sure how best to test if we're
215 # This is icky, but I'm not currently sure how best to test if we're
222 # in a terminal shell using prompt_toolkit
216 # in a terminal shell using prompt_toolkit
223 self.use_prompt_toolkit = hasattr(self.shell, 'pt_cli')
217 self.use_prompt_toolkit = hasattr(self.shell, 'pt_cli')
224
218
225 self.aliases = {}
219 self.aliases = {}
226
220
227 # Create color table: we copy the default one from the traceback
221 # Create color table: we copy the default one from the traceback
228 # module and add a few attributes needed for debugging
222 # module and add a few attributes needed for debugging
229 self.color_scheme_table = exception_colors()
223 self.color_scheme_table = exception_colors()
230
224
231 # shorthands
225 # shorthands
232 C = coloransi.TermColors
226 C = coloransi.TermColors
233 cst = self.color_scheme_table
227 cst = self.color_scheme_table
234
228
235 cst['NoColor'].colors.prompt = C.NoColor
229 cst['NoColor'].colors.prompt = C.NoColor
236 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
230 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
237 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
231 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
238
232
239 cst['Linux'].colors.prompt = C.Green
233 cst['Linux'].colors.prompt = C.Green
240 cst['Linux'].colors.breakpoint_enabled = C.LightRed
234 cst['Linux'].colors.breakpoint_enabled = C.LightRed
241 cst['Linux'].colors.breakpoint_disabled = C.Red
235 cst['Linux'].colors.breakpoint_disabled = C.Red
242
236
243 cst['LightBG'].colors.prompt = C.Blue
237 cst['LightBG'].colors.prompt = C.Blue
244 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
238 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
245 cst['LightBG'].colors.breakpoint_disabled = C.Red
239 cst['LightBG'].colors.breakpoint_disabled = C.Red
246
240
247 self.set_colors(color_scheme)
241 self.set_colors(color_scheme)
248
242
249 # Add a python parser so we can syntax highlight source while
243 # Add a python parser so we can syntax highlight source while
250 # debugging.
244 # debugging.
251 self.parser = PyColorize.Parser()
245 self.parser = PyColorize.Parser()
252
246
253 # Set the prompt - the default prompt is '(Pdb)'
247 # Set the prompt - the default prompt is '(Pdb)'
254 self.prompt = prompt
248 self.prompt = prompt
255
249
256
250
257 def cmdloop(self, intro=None):
251 def cmdloop(self, intro=None):
258 """Repeatedly issue a prompt, accept input, parse an initial prefix
252 """Repeatedly issue a prompt, accept input, parse an initial prefix
259 off the received input, and dispatch to action methods, passing them
253 off the received input, and dispatch to action methods, passing them
260 the remainder of the line as argument.
254 the remainder of the line as argument.
261
255
262 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
256 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
263 """
257 """
264 if not self.use_prompt_toolkit:
258 if not self.use_prompt_toolkit:
265 super(Pdb, self).cmdloop(intro)
259 super(Pdb, self).cmdloop(intro)
266
260
267 if not self.use_rawinput:
261 if not self.use_rawinput:
268 raise ValueError('Sorry ipdb does not support raw_input=False')
262 raise ValueError('Sorry ipdb does not support raw_input=False')
269
263
270 def get_prompt_tokens(cli):
264 def get_prompt_tokens(cli):
271 from pygments.token import Token
265 from pygments.token import Token
272 return [(Token.Prompt, self.prompt)]
266 return [(Token.Prompt, self.prompt)]
273
267
274 self.preloop()
268 self.preloop()
275 try:
269 try:
276 if intro is not None:
270 if intro is not None:
277 self.intro = intro
271 self.intro = intro
278 if self.intro:
272 if self.intro:
279 self.stdout.write(str(self.intro)+"\n")
273 self.stdout.write(str(self.intro)+"\n")
280 stop = None
274 stop = None
281 while not stop:
275 while not stop:
282 if self.cmdqueue:
276 if self.cmdqueue:
283 line = self.cmdqueue.pop(0)
277 line = self.cmdqueue.pop(0)
284 else:
278 else:
285 try:
279 try:
286 line = ptk_prompt(get_prompt_tokens=get_prompt_tokens)
280 line = ptk_prompt(get_prompt_tokens=get_prompt_tokens)
287 except EOFError:
281 except EOFError:
288 line = 'EOF'
282 line = 'EOF'
289 line = self.precmd(line)
283 line = self.precmd(line)
290 stop = self.onecmd(line)
284 stop = self.onecmd(line)
291 stop = self.postcmd(stop, line)
285 stop = self.postcmd(stop, line)
292 self.postloop()
286 self.postloop()
293 except Exception:
287 except Exception:
294 pass
288 pass
295
289
296
290
297
291
298 def set_colors(self, scheme):
292 def set_colors(self, scheme):
299 """Shorthand access to the color table scheme selector method."""
293 """Shorthand access to the color table scheme selector method."""
300 self.color_scheme_table.set_active_scheme(scheme)
294 self.color_scheme_table.set_active_scheme(scheme)
301
295
302 def interaction(self, frame, traceback):
296 def interaction(self, frame, traceback):
303 self.shell.set_completer_frame(frame)
297 try:
304 while True:
298 OldPdb.interaction(self, frame, traceback)
305 try:
299 except KeyboardInterrupt:
306 OldPdb.interaction(self, frame, traceback)
300 sys.stdout.write('\n' + self.shell.get_exception_only())
307 break
308 except KeyboardInterrupt:
309 self.shell.write('\n' + self.shell.get_exception_only())
310 break
311 finally:
312 # Pdb sets readline delimiters, so set them back to our own
313 if self.shell.readline is not None:
314 self.shell.readline.set_completer_delims(self.shell.readline_delims)
315
301
316 def parseline(self, line):
302 def parseline(self, line):
317 if line.startswith("!!"):
303 if line.startswith("!!"):
318 # Force standard behavior.
304 # Force standard behavior.
319 return super(Pdb, self).parseline(line[2:])
305 return super(Pdb, self).parseline(line[2:])
320 # "Smart command mode" from pdb++: don't execute commands if a variable
306 # "Smart command mode" from pdb++: don't execute commands if a variable
321 # with the same name exists.
307 # with the same name exists.
322 cmd, arg, newline = super(Pdb, self).parseline(line)
308 cmd, arg, newline = super(Pdb, self).parseline(line)
323 if cmd in self.curframe.f_globals or cmd in self.curframe.f_locals:
309 if cmd in self.curframe.f_globals or cmd in self.curframe.f_locals:
324 return super(Pdb, self).parseline("!" + line)
310 return super(Pdb, self).parseline("!" + line)
325 return super(Pdb, self).parseline(line)
311 return super(Pdb, self).parseline(line)
326
312
327 def new_do_up(self, arg):
313 def new_do_up(self, arg):
328 OldPdb.do_up(self, arg)
314 OldPdb.do_up(self, arg)
329 self.shell.set_completer_frame(self.curframe)
315 self.shell.set_completer_frame(self.curframe)
330 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
316 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
331
317
332 def new_do_down(self, arg):
318 def new_do_down(self, arg):
333 OldPdb.do_down(self, arg)
319 OldPdb.do_down(self, arg)
334 self.shell.set_completer_frame(self.curframe)
320 self.shell.set_completer_frame(self.curframe)
335
321
336 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
322 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
337
323
338 def new_do_frame(self, arg):
324 def new_do_frame(self, arg):
339 OldPdb.do_frame(self, arg)
325 OldPdb.do_frame(self, arg)
340 self.shell.set_completer_frame(self.curframe)
326 self.shell.set_completer_frame(self.curframe)
341
327
342 def new_do_quit(self, arg):
328 def new_do_quit(self, arg):
343
329
344 if hasattr(self, 'old_all_completions'):
330 if hasattr(self, 'old_all_completions'):
345 self.shell.Completer.all_completions=self.old_all_completions
331 self.shell.Completer.all_completions=self.old_all_completions
346
332
347 return OldPdb.do_quit(self, arg)
333 return OldPdb.do_quit(self, arg)
348
334
349 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
335 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
350
336
351 def new_do_restart(self, arg):
337 def new_do_restart(self, arg):
352 """Restart command. In the context of ipython this is exactly the same
338 """Restart command. In the context of ipython this is exactly the same
353 thing as 'quit'."""
339 thing as 'quit'."""
354 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
340 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
355 return self.do_quit(arg)
341 return self.do_quit(arg)
356
342
357 def postloop(self):
343 def postloop(self):
358 self.shell.set_completer_frame(None)
344 self.shell.set_completer_frame(None)
359
345
360 def print_stack_trace(self, context=None):
346 def print_stack_trace(self, context=None):
361 if context is None:
347 if context is None:
362 context = self.context
348 context = self.context
363 try:
349 try:
364 context=int(context)
350 context=int(context)
365 if context <= 0:
351 if context <= 0:
366 raise ValueError("Context must be a positive integer")
352 raise ValueError("Context must be a positive integer")
367 except (TypeError, ValueError):
353 except (TypeError, ValueError):
368 raise ValueError("Context must be a positive integer")
354 raise ValueError("Context must be a positive integer")
369 try:
355 try:
370 for frame_lineno in self.stack:
356 for frame_lineno in self.stack:
371 self.print_stack_entry(frame_lineno, context=context)
357 self.print_stack_entry(frame_lineno, context=context)
372 except KeyboardInterrupt:
358 except KeyboardInterrupt:
373 pass
359 pass
374
360
375 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
361 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
376 context=None):
362 context=None):
377 if context is None:
363 if context is None:
378 context = self.context
364 context = self.context
379 try:
365 try:
380 context=int(context)
366 context=int(context)
381 if context <= 0:
367 if context <= 0:
382 raise ValueError("Context must be a positive integer")
368 raise ValueError("Context must be a positive integer")
383 except (TypeError, ValueError):
369 except (TypeError, ValueError):
384 raise ValueError("Context must be a positive integer")
370 raise ValueError("Context must be a positive integer")
385 print(self.format_stack_entry(frame_lineno, '', context))
371 print(self.format_stack_entry(frame_lineno, '', context))
386
372
387 # vds: >>
373 # vds: >>
388 frame, lineno = frame_lineno
374 frame, lineno = frame_lineno
389 filename = frame.f_code.co_filename
375 filename = frame.f_code.co_filename
390 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
376 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
391 # vds: <<
377 # vds: <<
392
378
393 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
379 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
394 if context is None:
380 if context is None:
395 context = self.context
381 context = self.context
396 try:
382 try:
397 context=int(context)
383 context=int(context)
398 if context <= 0:
384 if context <= 0:
399 print("Context must be a positive integer")
385 print("Context must be a positive integer")
400 except (TypeError, ValueError):
386 except (TypeError, ValueError):
401 print("Context must be a positive integer")
387 print("Context must be a positive integer")
402 try:
388 try:
403 import reprlib # Py 3
389 import reprlib # Py 3
404 except ImportError:
390 except ImportError:
405 import repr as reprlib # Py 2
391 import repr as reprlib # Py 2
406
392
407 ret = []
393 ret = []
408
394
409 Colors = self.color_scheme_table.active_colors
395 Colors = self.color_scheme_table.active_colors
410 ColorsNormal = Colors.Normal
396 ColorsNormal = Colors.Normal
411 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
397 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
412 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
398 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
413 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
399 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
414 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
400 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
415 ColorsNormal)
401 ColorsNormal)
416
402
417 frame, lineno = frame_lineno
403 frame, lineno = frame_lineno
418
404
419 return_value = ''
405 return_value = ''
420 if '__return__' in frame.f_locals:
406 if '__return__' in frame.f_locals:
421 rv = frame.f_locals['__return__']
407 rv = frame.f_locals['__return__']
422 #return_value += '->'
408 #return_value += '->'
423 return_value += reprlib.repr(rv) + '\n'
409 return_value += reprlib.repr(rv) + '\n'
424 ret.append(return_value)
410 ret.append(return_value)
425
411
426 #s = filename + '(' + `lineno` + ')'
412 #s = filename + '(' + `lineno` + ')'
427 filename = self.canonic(frame.f_code.co_filename)
413 filename = self.canonic(frame.f_code.co_filename)
428 link = tpl_link % py3compat.cast_unicode(filename)
414 link = tpl_link % py3compat.cast_unicode(filename)
429
415
430 if frame.f_code.co_name:
416 if frame.f_code.co_name:
431 func = frame.f_code.co_name
417 func = frame.f_code.co_name
432 else:
418 else:
433 func = "<lambda>"
419 func = "<lambda>"
434
420
435 call = ''
421 call = ''
436 if func != '?':
422 if func != '?':
437 if '__args__' in frame.f_locals:
423 if '__args__' in frame.f_locals:
438 args = reprlib.repr(frame.f_locals['__args__'])
424 args = reprlib.repr(frame.f_locals['__args__'])
439 else:
425 else:
440 args = '()'
426 args = '()'
441 call = tpl_call % (func, args)
427 call = tpl_call % (func, args)
442
428
443 # The level info should be generated in the same format pdb uses, to
429 # The level info should be generated in the same format pdb uses, to
444 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
430 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
445 if frame is self.curframe:
431 if frame is self.curframe:
446 ret.append('> ')
432 ret.append('> ')
447 else:
433 else:
448 ret.append(' ')
434 ret.append(' ')
449 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
435 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
450
436
451 start = lineno - 1 - context//2
437 start = lineno - 1 - context//2
452 lines = ulinecache.getlines(filename)
438 lines = ulinecache.getlines(filename)
453 start = min(start, len(lines) - context)
439 start = min(start, len(lines) - context)
454 start = max(start, 0)
440 start = max(start, 0)
455 lines = lines[start : start + context]
441 lines = lines[start : start + context]
456
442
457 for i,line in enumerate(lines):
443 for i,line in enumerate(lines):
458 show_arrow = (start + 1 + i == lineno)
444 show_arrow = (start + 1 + i == lineno)
459 linetpl = (frame is self.curframe or show_arrow) \
445 linetpl = (frame is self.curframe or show_arrow) \
460 and tpl_line_em \
446 and tpl_line_em \
461 or tpl_line
447 or tpl_line
462 ret.append(self.__format_line(linetpl, filename,
448 ret.append(self.__format_line(linetpl, filename,
463 start + 1 + i, line,
449 start + 1 + i, line,
464 arrow = show_arrow) )
450 arrow = show_arrow) )
465 return ''.join(ret)
451 return ''.join(ret)
466
452
467 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
453 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
468 bp_mark = ""
454 bp_mark = ""
469 bp_mark_color = ""
455 bp_mark_color = ""
470
456
471 scheme = self.color_scheme_table.active_scheme_name
457 scheme = self.color_scheme_table.active_scheme_name
472 new_line, err = self.parser.format2(line, 'str', scheme)
458 new_line, err = self.parser.format2(line, 'str', scheme)
473 if not err: line = new_line
459 if not err: line = new_line
474
460
475 bp = None
461 bp = None
476 if lineno in self.get_file_breaks(filename):
462 if lineno in self.get_file_breaks(filename):
477 bps = self.get_breaks(filename, lineno)
463 bps = self.get_breaks(filename, lineno)
478 bp = bps[-1]
464 bp = bps[-1]
479
465
480 if bp:
466 if bp:
481 Colors = self.color_scheme_table.active_colors
467 Colors = self.color_scheme_table.active_colors
482 bp_mark = str(bp.number)
468 bp_mark = str(bp.number)
483 bp_mark_color = Colors.breakpoint_enabled
469 bp_mark_color = Colors.breakpoint_enabled
484 if not bp.enabled:
470 if not bp.enabled:
485 bp_mark_color = Colors.breakpoint_disabled
471 bp_mark_color = Colors.breakpoint_disabled
486
472
487 numbers_width = 7
473 numbers_width = 7
488 if arrow:
474 if arrow:
489 # This is the line with the error
475 # This is the line with the error
490 pad = numbers_width - len(str(lineno)) - len(bp_mark)
476 pad = numbers_width - len(str(lineno)) - len(bp_mark)
491 num = '%s%s' % (make_arrow(pad), str(lineno))
477 num = '%s%s' % (make_arrow(pad), str(lineno))
492 else:
478 else:
493 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
479 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
494
480
495 return tpl_line % (bp_mark_color + bp_mark, num, line)
481 return tpl_line % (bp_mark_color + bp_mark, num, line)
496
482
497
483
498 def print_list_lines(self, filename, first, last):
484 def print_list_lines(self, filename, first, last):
499 """The printing (as opposed to the parsing part of a 'list'
485 """The printing (as opposed to the parsing part of a 'list'
500 command."""
486 command."""
501 try:
487 try:
502 Colors = self.color_scheme_table.active_colors
488 Colors = self.color_scheme_table.active_colors
503 ColorsNormal = Colors.Normal
489 ColorsNormal = Colors.Normal
504 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
490 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
505 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
491 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
506 src = []
492 src = []
507 if filename == "<string>" and hasattr(self, "_exec_filename"):
493 if filename == "<string>" and hasattr(self, "_exec_filename"):
508 filename = self._exec_filename
494 filename = self._exec_filename
509
495
510 for lineno in range(first, last+1):
496 for lineno in range(first, last+1):
511 line = ulinecache.getline(filename, lineno)
497 line = ulinecache.getline(filename, lineno)
512 if not line:
498 if not line:
513 break
499 break
514
500
515 if lineno == self.curframe.f_lineno:
501 if lineno == self.curframe.f_lineno:
516 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
502 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
517 else:
503 else:
518 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
504 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
519
505
520 src.append(line)
506 src.append(line)
521 self.lineno = lineno
507 self.lineno = lineno
522
508
523 print(''.join(src))
509 print(''.join(src))
524
510
525 except KeyboardInterrupt:
511 except KeyboardInterrupt:
526 pass
512 pass
527
513
528 def do_list(self, arg):
514 def do_list(self, arg):
529 self.lastcmd = 'list'
515 self.lastcmd = 'list'
530 last = None
516 last = None
531 if arg:
517 if arg:
532 try:
518 try:
533 x = eval(arg, {}, {})
519 x = eval(arg, {}, {})
534 if type(x) == type(()):
520 if type(x) == type(()):
535 first, last = x
521 first, last = x
536 first = int(first)
522 first = int(first)
537 last = int(last)
523 last = int(last)
538 if last < first:
524 if last < first:
539 # Assume it's a count
525 # Assume it's a count
540 last = first + last
526 last = first + last
541 else:
527 else:
542 first = max(1, int(x) - 5)
528 first = max(1, int(x) - 5)
543 except:
529 except:
544 print('*** Error in argument:', repr(arg))
530 print('*** Error in argument:', repr(arg))
545 return
531 return
546 elif self.lineno is None:
532 elif self.lineno is None:
547 first = max(1, self.curframe.f_lineno - 5)
533 first = max(1, self.curframe.f_lineno - 5)
548 else:
534 else:
549 first = self.lineno + 1
535 first = self.lineno + 1
550 if last is None:
536 if last is None:
551 last = first + 10
537 last = first + 10
552 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
538 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
553
539
554 # vds: >>
540 # vds: >>
555 lineno = first
541 lineno = first
556 filename = self.curframe.f_code.co_filename
542 filename = self.curframe.f_code.co_filename
557 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
543 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
558 # vds: <<
544 # vds: <<
559
545
560 do_l = do_list
546 do_l = do_list
561
547
562 def getsourcelines(self, obj):
548 def getsourcelines(self, obj):
563 lines, lineno = inspect.findsource(obj)
549 lines, lineno = inspect.findsource(obj)
564 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
550 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
565 # must be a module frame: do not try to cut a block out of it
551 # must be a module frame: do not try to cut a block out of it
566 return lines, 1
552 return lines, 1
567 elif inspect.ismodule(obj):
553 elif inspect.ismodule(obj):
568 return lines, 1
554 return lines, 1
569 return inspect.getblock(lines[lineno:]), lineno+1
555 return inspect.getblock(lines[lineno:]), lineno+1
570
556
571 def do_longlist(self, arg):
557 def do_longlist(self, arg):
572 self.lastcmd = 'longlist'
558 self.lastcmd = 'longlist'
573 try:
559 try:
574 lines, lineno = self.getsourcelines(self.curframe)
560 lines, lineno = self.getsourcelines(self.curframe)
575 except OSError as err:
561 except OSError as err:
576 self.error(err)
562 self.error(err)
577 return
563 return
578 last = lineno + len(lines)
564 last = lineno + len(lines)
579 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
565 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
580 do_ll = do_longlist
566 do_ll = do_longlist
581
567
582 def do_pdef(self, arg):
568 def do_pdef(self, arg):
583 """Print the call signature for any callable object.
569 """Print the call signature for any callable object.
584
570
585 The debugger interface to %pdef"""
571 The debugger interface to %pdef"""
586 namespaces = [('Locals', self.curframe.f_locals),
572 namespaces = [('Locals', self.curframe.f_locals),
587 ('Globals', self.curframe.f_globals)]
573 ('Globals', self.curframe.f_globals)]
588 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
574 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
589
575
590 def do_pdoc(self, arg):
576 def do_pdoc(self, arg):
591 """Print the docstring for an object.
577 """Print the docstring for an object.
592
578
593 The debugger interface to %pdoc."""
579 The debugger interface to %pdoc."""
594 namespaces = [('Locals', self.curframe.f_locals),
580 namespaces = [('Locals', self.curframe.f_locals),
595 ('Globals', self.curframe.f_globals)]
581 ('Globals', self.curframe.f_globals)]
596 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
582 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
597
583
598 def do_pfile(self, arg):
584 def do_pfile(self, arg):
599 """Print (or run through pager) the file where an object is defined.
585 """Print (or run through pager) the file where an object is defined.
600
586
601 The debugger interface to %pfile.
587 The debugger interface to %pfile.
602 """
588 """
603 namespaces = [('Locals', self.curframe.f_locals),
589 namespaces = [('Locals', self.curframe.f_locals),
604 ('Globals', self.curframe.f_globals)]
590 ('Globals', self.curframe.f_globals)]
605 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
591 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
606
592
607 def do_pinfo(self, arg):
593 def do_pinfo(self, arg):
608 """Provide detailed information about an object.
594 """Provide detailed information about an object.
609
595
610 The debugger interface to %pinfo, i.e., obj?."""
596 The debugger interface to %pinfo, i.e., obj?."""
611 namespaces = [('Locals', self.curframe.f_locals),
597 namespaces = [('Locals', self.curframe.f_locals),
612 ('Globals', self.curframe.f_globals)]
598 ('Globals', self.curframe.f_globals)]
613 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
599 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
614
600
615 def do_pinfo2(self, arg):
601 def do_pinfo2(self, arg):
616 """Provide extra detailed information about an object.
602 """Provide extra detailed information about an object.
617
603
618 The debugger interface to %pinfo2, i.e., obj??."""
604 The debugger interface to %pinfo2, i.e., obj??."""
619 namespaces = [('Locals', self.curframe.f_locals),
605 namespaces = [('Locals', self.curframe.f_locals),
620 ('Globals', self.curframe.f_globals)]
606 ('Globals', self.curframe.f_globals)]
621 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
607 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
622
608
623 def do_psource(self, arg):
609 def do_psource(self, arg):
624 """Print (or run through pager) the source code for an object."""
610 """Print (or run through pager) the source code for an object."""
625 namespaces = [('Locals', self.curframe.f_locals),
611 namespaces = [('Locals', self.curframe.f_locals),
626 ('Globals', self.curframe.f_globals)]
612 ('Globals', self.curframe.f_globals)]
627 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
613 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
628
614
629 if sys.version_info > (3, ):
615 if sys.version_info > (3, ):
630 def do_where(self, arg):
616 def do_where(self, arg):
631 """w(here)
617 """w(here)
632 Print a stack trace, with the most recent frame at the bottom.
618 Print a stack trace, with the most recent frame at the bottom.
633 An arrow indicates the "current frame", which determines the
619 An arrow indicates the "current frame", which determines the
634 context of most commands. 'bt' is an alias for this command.
620 context of most commands. 'bt' is an alias for this command.
635
621
636 Take a number as argument as an (optional) number of context line to
622 Take a number as argument as an (optional) number of context line to
637 print"""
623 print"""
638 if arg:
624 if arg:
639 context = int(arg)
625 context = int(arg)
640 self.print_stack_trace(context)
626 self.print_stack_trace(context)
641 else:
627 else:
642 self.print_stack_trace()
628 self.print_stack_trace()
643
629
644 do_w = do_where
630 do_w = do_where
General Comments 0
You need to be logged in to leave comments. Login now