##// END OF EJS Templates
Fix quitting debugger when readline not in use...
Thomas Kluyver -
Show More
@@ -1,560 +1,561 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Pdb debugger class.
4 4
5 5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 6 the command line completion of other programs which include this isn't
7 7 damaged.
8 8
9 9 In the future, this class will be expanded with improvements over the standard
10 10 pdb.
11 11
12 12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 13 changes. Licensing should therefore be under the standard Python terms. For
14 14 details on the PSF (Python Software Foundation) standard license, see:
15 15
16 16 http://www.python.org/2.2.3/license.html"""
17 17
18 18 #*****************************************************************************
19 19 #
20 20 # This file is licensed under the PSF license.
21 21 #
22 22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 24 #
25 25 #
26 26 #*****************************************************************************
27 27 from __future__ import print_function
28 28
29 29 import bdb
30 30 import functools
31 31 import linecache
32 32 import sys
33 33
34 34 from IPython import get_ipython
35 35 from IPython.utils import PyColorize, ulinecache
36 36 from IPython.utils import coloransi, io, py3compat
37 37 from IPython.core.excolors import exception_colors
38 38 from IPython.testing.skipdoctest import skip_doctest
39 39
40 40 # See if we can use pydb.
41 41 has_pydb = False
42 42 prompt = 'ipdb> '
43 43 #We have to check this directly from sys.argv, config struct not yet available
44 44 if '--pydb' in sys.argv:
45 45 try:
46 46 import pydb
47 47 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
48 48 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
49 49 # better protect against it.
50 50 has_pydb = True
51 51 except ImportError:
52 52 print("Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available")
53 53
54 54 if has_pydb:
55 55 from pydb import Pdb as OldPdb
56 56 #print "Using pydb for %run -d and post-mortem" #dbg
57 57 prompt = 'ipydb> '
58 58 else:
59 59 from pdb import Pdb as OldPdb
60 60
61 61 # Allow the set_trace code to operate outside of an ipython instance, even if
62 62 # it does so with some limitations. The rest of this support is implemented in
63 63 # the Tracer constructor.
64 64 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
65 65 """Exception hook which handles `BdbQuit` exceptions.
66 66
67 67 All other exceptions are processed using the `excepthook`
68 68 parameter.
69 69 """
70 70 if et==bdb.BdbQuit:
71 71 print('Exiting Debugger.')
72 72 elif excepthook is not None:
73 73 excepthook(et, ev, tb)
74 74 else:
75 75 # Backwards compatibility. Raise deprecation warning?
76 76 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
77 77
78 78 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
79 79 print('Exiting Debugger.')
80 80
81 81
82 82 class Tracer(object):
83 83 """Class for local debugging, similar to pdb.set_trace.
84 84
85 85 Instances of this class, when called, behave like pdb.set_trace, but
86 86 providing IPython's enhanced capabilities.
87 87
88 88 This is implemented as a class which must be initialized in your own code
89 89 and not as a standalone function because we need to detect at runtime
90 90 whether IPython is already active or not. That detection is done in the
91 91 constructor, ensuring that this code plays nicely with a running IPython,
92 92 while functioning acceptably (though with limitations) if outside of it.
93 93 """
94 94
95 95 @skip_doctest
96 96 def __init__(self,colors=None):
97 97 """Create a local debugger instance.
98 98
99 99 Parameters
100 100 ----------
101 101
102 102 colors : str, optional
103 103 The name of the color scheme to use, it must be one of IPython's
104 104 valid color schemes. If not given, the function will default to
105 105 the current IPython scheme when running inside IPython, and to
106 106 'NoColor' otherwise.
107 107
108 108 Examples
109 109 --------
110 110 ::
111 111
112 112 from IPython.core.debugger import Tracer; debug_here = Tracer()
113 113
114 114 Later in your code::
115 115
116 116 debug_here() # -> will open up the debugger at that point.
117 117
118 118 Once the debugger activates, you can use all of its regular commands to
119 119 step through code, set breakpoints, etc. See the pdb documentation
120 120 from the Python standard library for usage details.
121 121 """
122 122
123 123 ip = get_ipython()
124 124 if ip is None:
125 125 # Outside of ipython, we set our own exception hook manually
126 126 sys.excepthook = functools.partial(BdbQuit_excepthook,
127 127 excepthook=sys.excepthook)
128 128 def_colors = 'NoColor'
129 129 try:
130 130 # Limited tab completion support
131 131 import readline
132 132 readline.parse_and_bind('tab: complete')
133 133 except ImportError:
134 134 pass
135 135 else:
136 136 # In ipython, we use its custom exception handler mechanism
137 137 def_colors = ip.colors
138 138 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
139 139
140 140 if colors is None:
141 141 colors = def_colors
142 142
143 143 # The stdlib debugger internally uses a modified repr from the `repr`
144 144 # module, that limits the length of printed strings to a hardcoded
145 145 # limit of 30 characters. That much trimming is too aggressive, let's
146 146 # at least raise that limit to 80 chars, which should be enough for
147 147 # most interactive uses.
148 148 try:
149 149 try:
150 150 from reprlib import aRepr # Py 3
151 151 except ImportError:
152 152 from repr import aRepr # Py 2
153 153 aRepr.maxstring = 80
154 154 except:
155 155 # This is only a user-facing convenience, so any error we encounter
156 156 # here can be warned about but can be otherwise ignored. These
157 157 # printouts will tell us about problems if this API changes
158 158 import traceback
159 159 traceback.print_exc()
160 160
161 161 self.debugger = Pdb(colors)
162 162
163 163 def __call__(self):
164 164 """Starts an interactive debugger at the point where called.
165 165
166 166 This is similar to the pdb.set_trace() function from the std lib, but
167 167 using IPython's enhanced debugger."""
168 168
169 169 self.debugger.set_trace(sys._getframe().f_back)
170 170
171 171
172 172 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
173 173 """Make new_fn have old_fn's doc string. This is particularly useful
174 174 for the ``do_...`` commands that hook into the help system.
175 175 Adapted from from a comp.lang.python posting
176 176 by Duncan Booth."""
177 177 def wrapper(*args, **kw):
178 178 return new_fn(*args, **kw)
179 179 if old_fn.__doc__:
180 180 wrapper.__doc__ = old_fn.__doc__ + additional_text
181 181 return wrapper
182 182
183 183
184 184 def _file_lines(fname):
185 185 """Return the contents of a named file as a list of lines.
186 186
187 187 This function never raises an IOError exception: if the file can't be
188 188 read, it simply returns an empty list."""
189 189
190 190 try:
191 191 outfile = open(fname)
192 192 except IOError:
193 193 return []
194 194 else:
195 195 out = outfile.readlines()
196 196 outfile.close()
197 197 return out
198 198
199 199
200 200 class Pdb(OldPdb):
201 201 """Modified Pdb class, does not load readline."""
202 202
203 203 def __init__(self,color_scheme='NoColor',completekey=None,
204 204 stdin=None, stdout=None):
205 205
206 206 # Parent constructor:
207 207 if has_pydb and completekey is None:
208 208 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
209 209 else:
210 210 OldPdb.__init__(self,completekey,stdin,stdout)
211 211
212 212 self.prompt = prompt # The default prompt is '(Pdb)'
213 213
214 214 # IPython changes...
215 215 self.is_pydb = has_pydb
216 216
217 217 self.shell = get_ipython()
218 218
219 219 if self.shell is None:
220 220 # No IPython instance running, we must create one
221 221 from IPython.terminal.interactiveshell import \
222 222 TerminalInteractiveShell
223 223 self.shell = TerminalInteractiveShell.instance()
224 224
225 225 if self.is_pydb:
226 226
227 227 # interactiveshell.py's ipalias seems to want pdb's checkline
228 228 # which located in pydb.fn
229 229 import pydb.fns
230 230 self.checkline = lambda filename, lineno: \
231 231 pydb.fns.checkline(self, filename, lineno)
232 232
233 233 self.curframe = None
234 234 self.do_restart = self.new_do_restart
235 235
236 236 self.old_all_completions = self.shell.Completer.all_completions
237 237 self.shell.Completer.all_completions=self.all_completions
238 238
239 239 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
240 240 OldPdb.do_list)
241 241 self.do_l = self.do_list
242 242 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
243 243 OldPdb.do_frame)
244 244
245 245 self.aliases = {}
246 246
247 247 # Create color table: we copy the default one from the traceback
248 248 # module and add a few attributes needed for debugging
249 249 self.color_scheme_table = exception_colors()
250 250
251 251 # shorthands
252 252 C = coloransi.TermColors
253 253 cst = self.color_scheme_table
254 254
255 255 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
256 256 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
257 257
258 258 cst['Linux'].colors.breakpoint_enabled = C.LightRed
259 259 cst['Linux'].colors.breakpoint_disabled = C.Red
260 260
261 261 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
262 262 cst['LightBG'].colors.breakpoint_disabled = C.Red
263 263
264 264 self.set_colors(color_scheme)
265 265
266 266 # Add a python parser so we can syntax highlight source while
267 267 # debugging.
268 268 self.parser = PyColorize.Parser()
269 269
270 270 def set_colors(self, scheme):
271 271 """Shorthand access to the color table scheme selector method."""
272 272 self.color_scheme_table.set_active_scheme(scheme)
273 273
274 274 def interaction(self, frame, traceback):
275 275 self.shell.set_completer_frame(frame)
276 276 while True:
277 277 try:
278 278 OldPdb.interaction(self, frame, traceback)
279 279 except KeyboardInterrupt:
280 280 self.shell.write('\n' + self.shell.get_exception_only())
281 281 break
282 282 else:
283 283 break
284 284
285 285 def new_do_up(self, arg):
286 286 OldPdb.do_up(self, arg)
287 287 self.shell.set_completer_frame(self.curframe)
288 288 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
289 289
290 290 def new_do_down(self, arg):
291 291 OldPdb.do_down(self, arg)
292 292 self.shell.set_completer_frame(self.curframe)
293 293
294 294 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
295 295
296 296 def new_do_frame(self, arg):
297 297 OldPdb.do_frame(self, arg)
298 298 self.shell.set_completer_frame(self.curframe)
299 299
300 300 def new_do_quit(self, arg):
301 301
302 302 if hasattr(self, 'old_all_completions'):
303 303 self.shell.Completer.all_completions=self.old_all_completions
304 304
305 305 # Pdb sets readline delimiters, so set them back to our own
306 self.shell.readline.set_completer_delims(self.shell.readline_delims)
306 if self.shell.readline is not None:
307 self.shell.readline.set_completer_delims(self.shell.readline_delims)
307 308
308 309 return OldPdb.do_quit(self, arg)
309 310
310 311 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
311 312
312 313 def new_do_restart(self, arg):
313 314 """Restart command. In the context of ipython this is exactly the same
314 315 thing as 'quit'."""
315 316 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
316 317 return self.do_quit(arg)
317 318
318 319 def postloop(self):
319 320 self.shell.set_completer_frame(None)
320 321
321 322 def print_stack_trace(self):
322 323 try:
323 324 for frame_lineno in self.stack:
324 325 self.print_stack_entry(frame_lineno, context = 5)
325 326 except KeyboardInterrupt:
326 327 pass
327 328
328 329 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
329 330 context = 3):
330 331 #frame, lineno = frame_lineno
331 332 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
332 333
333 334 # vds: >>
334 335 frame, lineno = frame_lineno
335 336 filename = frame.f_code.co_filename
336 337 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
337 338 # vds: <<
338 339
339 340 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
340 341 try:
341 342 import reprlib # Py 3
342 343 except ImportError:
343 344 import repr as reprlib # Py 2
344 345
345 346 ret = []
346 347
347 348 Colors = self.color_scheme_table.active_colors
348 349 ColorsNormal = Colors.Normal
349 350 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
350 351 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
351 352 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
352 353 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
353 354 ColorsNormal)
354 355
355 356 frame, lineno = frame_lineno
356 357
357 358 return_value = ''
358 359 if '__return__' in frame.f_locals:
359 360 rv = frame.f_locals['__return__']
360 361 #return_value += '->'
361 362 return_value += reprlib.repr(rv) + '\n'
362 363 ret.append(return_value)
363 364
364 365 #s = filename + '(' + `lineno` + ')'
365 366 filename = self.canonic(frame.f_code.co_filename)
366 367 link = tpl_link % py3compat.cast_unicode(filename)
367 368
368 369 if frame.f_code.co_name:
369 370 func = frame.f_code.co_name
370 371 else:
371 372 func = "<lambda>"
372 373
373 374 call = ''
374 375 if func != '?':
375 376 if '__args__' in frame.f_locals:
376 377 args = reprlib.repr(frame.f_locals['__args__'])
377 378 else:
378 379 args = '()'
379 380 call = tpl_call % (func, args)
380 381
381 382 # The level info should be generated in the same format pdb uses, to
382 383 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
383 384 if frame is self.curframe:
384 385 ret.append('> ')
385 386 else:
386 387 ret.append(' ')
387 388 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
388 389
389 390 start = lineno - 1 - context//2
390 391 lines = ulinecache.getlines(filename)
391 392 start = min(start, len(lines) - context)
392 393 start = max(start, 0)
393 394 lines = lines[start : start + context]
394 395
395 396 for i,line in enumerate(lines):
396 397 show_arrow = (start + 1 + i == lineno)
397 398 linetpl = (frame is self.curframe or show_arrow) \
398 399 and tpl_line_em \
399 400 or tpl_line
400 401 ret.append(self.__format_line(linetpl, filename,
401 402 start + 1 + i, line,
402 403 arrow = show_arrow) )
403 404 return ''.join(ret)
404 405
405 406 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
406 407 bp_mark = ""
407 408 bp_mark_color = ""
408 409
409 410 scheme = self.color_scheme_table.active_scheme_name
410 411 new_line, err = self.parser.format2(line, 'str', scheme)
411 412 if not err: line = new_line
412 413
413 414 bp = None
414 415 if lineno in self.get_file_breaks(filename):
415 416 bps = self.get_breaks(filename, lineno)
416 417 bp = bps[-1]
417 418
418 419 if bp:
419 420 Colors = self.color_scheme_table.active_colors
420 421 bp_mark = str(bp.number)
421 422 bp_mark_color = Colors.breakpoint_enabled
422 423 if not bp.enabled:
423 424 bp_mark_color = Colors.breakpoint_disabled
424 425
425 426 numbers_width = 7
426 427 if arrow:
427 428 # This is the line with the error
428 429 pad = numbers_width - len(str(lineno)) - len(bp_mark)
429 430 if pad >= 3:
430 431 marker = '-'*(pad-3) + '-> '
431 432 elif pad == 2:
432 433 marker = '> '
433 434 elif pad == 1:
434 435 marker = '>'
435 436 else:
436 437 marker = ''
437 438 num = '%s%s' % (marker, str(lineno))
438 439 line = tpl_line % (bp_mark_color + bp_mark, num, line)
439 440 else:
440 441 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
441 442 line = tpl_line % (bp_mark_color + bp_mark, num, line)
442 443
443 444 return line
444 445
445 446 def list_command_pydb(self, arg):
446 447 """List command to use if we have a newer pydb installed"""
447 448 filename, first, last = OldPdb.parse_list_cmd(self, arg)
448 449 if filename is not None:
449 450 self.print_list_lines(filename, first, last)
450 451
451 452 def print_list_lines(self, filename, first, last):
452 453 """The printing (as opposed to the parsing part of a 'list'
453 454 command."""
454 455 try:
455 456 Colors = self.color_scheme_table.active_colors
456 457 ColorsNormal = Colors.Normal
457 458 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
458 459 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
459 460 src = []
460 461 if filename == "<string>" and hasattr(self, "_exec_filename"):
461 462 filename = self._exec_filename
462 463
463 464 for lineno in range(first, last+1):
464 465 line = ulinecache.getline(filename, lineno)
465 466 if not line:
466 467 break
467 468
468 469 if lineno == self.curframe.f_lineno:
469 470 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
470 471 else:
471 472 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
472 473
473 474 src.append(line)
474 475 self.lineno = lineno
475 476
476 477 print(''.join(src), file=io.stdout)
477 478
478 479 except KeyboardInterrupt:
479 480 pass
480 481
481 482 def do_list(self, arg):
482 483 self.lastcmd = 'list'
483 484 last = None
484 485 if arg:
485 486 try:
486 487 x = eval(arg, {}, {})
487 488 if type(x) == type(()):
488 489 first, last = x
489 490 first = int(first)
490 491 last = int(last)
491 492 if last < first:
492 493 # Assume it's a count
493 494 last = first + last
494 495 else:
495 496 first = max(1, int(x) - 5)
496 497 except:
497 498 print('*** Error in argument:', repr(arg))
498 499 return
499 500 elif self.lineno is None:
500 501 first = max(1, self.curframe.f_lineno - 5)
501 502 else:
502 503 first = self.lineno + 1
503 504 if last is None:
504 505 last = first + 10
505 506 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
506 507
507 508 # vds: >>
508 509 lineno = first
509 510 filename = self.curframe.f_code.co_filename
510 511 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
511 512 # vds: <<
512 513
513 514 do_l = do_list
514 515
515 516 def do_pdef(self, arg):
516 517 """Print the call signature for any callable object.
517 518
518 519 The debugger interface to %pdef"""
519 520 namespaces = [('Locals', self.curframe.f_locals),
520 521 ('Globals', self.curframe.f_globals)]
521 522 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
522 523
523 524 def do_pdoc(self, arg):
524 525 """Print the docstring for an object.
525 526
526 527 The debugger interface to %pdoc."""
527 528 namespaces = [('Locals', self.curframe.f_locals),
528 529 ('Globals', self.curframe.f_globals)]
529 530 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
530 531
531 532 def do_pfile(self, arg):
532 533 """Print (or run through pager) the file where an object is defined.
533 534
534 535 The debugger interface to %pfile.
535 536 """
536 537 namespaces = [('Locals', self.curframe.f_locals),
537 538 ('Globals', self.curframe.f_globals)]
538 539 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
539 540
540 541 def do_pinfo(self, arg):
541 542 """Provide detailed information about an object.
542 543
543 544 The debugger interface to %pinfo, i.e., obj?."""
544 545 namespaces = [('Locals', self.curframe.f_locals),
545 546 ('Globals', self.curframe.f_globals)]
546 547 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
547 548
548 549 def do_pinfo2(self, arg):
549 550 """Provide extra detailed information about an object.
550 551
551 552 The debugger interface to %pinfo2, i.e., obj??."""
552 553 namespaces = [('Locals', self.curframe.f_locals),
553 554 ('Globals', self.curframe.f_globals)]
554 555 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
555 556
556 557 def do_psource(self, arg):
557 558 """Print (or run through pager) the source code for an object."""
558 559 namespaces = [('Locals', self.curframe.f_locals),
559 560 ('Globals', self.curframe.f_globals)]
560 561 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
General Comments 0
You need to be logged in to leave comments. Login now