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