1
# -*- coding: utf-8 -*-
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
9
In the future, this class will be expanded with improvements over the standard
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
14
details on the PSF (Python Software Foundation) standard license, see:
16
http://www.python.org/2.2.3/license.html
18
$Id: Debugger.py 2913 2007-12-31 12:42:14Z vivainio $"""
20
#*****************************************************************************
22
# This file is licensed under the PSF license.
24
# Copyright (C) 2001 Python Software Foundation, www.python.org
25
# Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
28
#*****************************************************************************
30
from IPython import Release
31
__author__ = '%s <%s>' % Release.authors['Fernando']
32
__license__ = 'Python'
40
from IPython import PyColorize, ColorANSI, ipapi
41
from IPython.genutils import Term
42
from IPython.excolors import ExceptionColors
44
# See if we can use pydb.
47
#We have to check this directly from sys.argv, config struct not yet available
48
if '-pydb' in sys.argv:
51
if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
52
# Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
53
# better protect against it.
56
print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available"
59
from pydb import Pdb as OldPdb
60
#print "Using pydb for %run -d and post-mortem" #dbg
63
from pdb import Pdb as OldPdb
65
# Allow the set_trace code to operate outside of an ipython instance, even if
66
# it does so with some limitations. The rest of this support is implemented in
67
# the Tracer constructor.
68
def BdbQuit_excepthook(et,ev,tb):
70
print 'Exiting Debugger.'
72
BdbQuit_excepthook.excepthook_ori(et,ev,tb)
74
def BdbQuit_IPython_excepthook(self,et,ev,tb):
75
print 'Exiting Debugger.'
78
"""Class for local debugging, similar to pdb.set_trace.
80
Instances of this class, when called, behave like pdb.set_trace, but
81
providing IPython's enhanced capabilities.
83
This is implemented as a class which must be initialized in your own code
84
and not as a standalone function because we need to detect at runtime
85
whether IPython is already active or not. That detection is done in the
86
constructor, ensuring that this code plays nicely with a running IPython,
87
while functioning acceptably (though with limitations) if outside of it.
90
def __init__(self,colors=None):
91
"""Create a local debugger instance.
95
- `colors` (None): a string containing the name of the color scheme to
96
use, it must be one of IPython's valid color schemes. If not given, the
97
function will default to the current IPython scheme when running inside
98
IPython, and to 'NoColor' otherwise.
102
from IPython.Debugger import Tracer; debug_here = Tracer()
104
... later in your code
105
debug_here() # -> will open up the debugger at that point.
107
Once the debugger activates, you can use all of its regular commands to
108
step through code, set breakpoints, etc. See the pdb documentation
109
from the Python standard library for usage details.
116
# Outside of ipython, we set our own exception hook manually
117
__IPYTHON__ = ipapi.get(True,False)
118
BdbQuit_excepthook.excepthook_ori = sys.excepthook
119
sys.excepthook = BdbQuit_excepthook
120
def_colors = 'NoColor'
122
# Limited tab completion support
123
import rlcompleter,readline
124
readline.parse_and_bind('tab: complete')
128
# In ipython, we use its custom exception handler mechanism
130
def_colors = ip.options.colors
131
ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook)
135
self.debugger = Pdb(colors)
138
"""Starts an interactive debugger at the point where called.
140
This is similar to the pdb.set_trace() function from the std lib, but
141
using IPython's enhanced debugger."""
143
self.debugger.set_trace(sys._getframe().f_back)
145
def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
146
"""Make new_fn have old_fn's doc string. This is particularly useful
147
for the do_... commands that hook into the help system.
148
Adapted from from a comp.lang.python posting
150
def wrapper(*args, **kw):
151
return new_fn(*args, **kw)
153
wrapper.__doc__ = old_fn.__doc__ + additional_text
156
def _file_lines(fname):
157
"""Return the contents of a named file as a list of lines.
159
This function never raises an IOError exception: if the file can't be
160
read, it simply returns an empty list."""
163
outfile = open(fname)
167
out = outfile.readlines()
172
"""Modified Pdb class, does not load readline."""
174
if sys.version[:3] >= '2.5' or has_pydb:
175
def __init__(self,color_scheme='NoColor',completekey=None,
176
stdin=None, stdout=None):
178
# Parent constructor:
179
if has_pydb and completekey is None:
180
OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
182
OldPdb.__init__(self,completekey,stdin,stdout)
184
self.prompt = prompt # The default prompt is '(Pdb)'
187
self.is_pydb = has_pydb
191
# iplib.py's ipalias seems to want pdb's checkline
192
# which located in pydb.fn
194
self.checkline = lambda filename, lineno: \
195
pydb.fns.checkline(self, filename, lineno)
198
self.do_restart = self.new_do_restart
200
self.old_all_completions = __IPYTHON__.Completer.all_completions
201
__IPYTHON__.Completer.all_completions=self.all_completions
203
self.do_list = decorate_fn_with_doc(self.list_command_pydb,
205
self.do_l = self.do_list
206
self.do_frame = decorate_fn_with_doc(self.new_do_frame,
211
# Create color table: we copy the default one from the traceback
212
# module and add a few attributes needed for debugging
213
self.color_scheme_table = ExceptionColors.copy()
216
C = ColorANSI.TermColors
217
cst = self.color_scheme_table
219
cst['NoColor'].colors.breakpoint_enabled = C.NoColor
220
cst['NoColor'].colors.breakpoint_disabled = C.NoColor
222
cst['Linux'].colors.breakpoint_enabled = C.LightRed
223
cst['Linux'].colors.breakpoint_disabled = C.Red
225
cst['LightBG'].colors.breakpoint_enabled = C.LightRed
226
cst['LightBG'].colors.breakpoint_disabled = C.Red
228
self.set_colors(color_scheme)
230
# Add a python parser so we can syntax highlight source while
232
self.parser = PyColorize.Parser()
236
# Ugly hack: for Python 2.3-2.4, we can't call the parent constructor,
237
# because it binds readline and breaks tab-completion. This means we
238
# have to COPY the constructor here.
239
def __init__(self,color_scheme='NoColor'):
240
bdb.Bdb.__init__(self)
241
cmd.Cmd.__init__(self,completekey=None) # don't load readline
242
self.prompt = 'ipdb> ' # The default prompt is '(Pdb)'
245
# These two lines are part of the py2.4 constructor, let's put them
246
# unconditionally here as they won't cause any problems in 2.3.
248
self._wait_for_mainpyfile = 0
250
# Read $HOME/.pdbrc and ./.pdbrc
252
self.rcLines = _file_lines(os.path.join(os.environ['HOME'],
256
self.rcLines.extend(_file_lines(".pdbrc"))
258
# Create color table: we copy the default one from the traceback
259
# module and add a few attributes needed for debugging
260
ExceptionColors.set_active_scheme(color_scheme)
261
self.color_scheme_table = ExceptionColors.copy()
264
C = ColorANSI.TermColors
265
cst = self.color_scheme_table
267
cst['NoColor'].colors.breakpoint_enabled = C.NoColor
268
cst['NoColor'].colors.breakpoint_disabled = C.NoColor
270
cst['Linux'].colors.breakpoint_enabled = C.LightRed
271
cst['Linux'].colors.breakpoint_disabled = C.Red
273
cst['LightBG'].colors.breakpoint_enabled = C.LightRed
274
cst['LightBG'].colors.breakpoint_disabled = C.Red
276
self.set_colors(color_scheme)
278
# Add a python parser so we can syntax highlight source while
280
self.parser = PyColorize.Parser()
282
def set_colors(self, scheme):
283
"""Shorthand access to the color table scheme selector method."""
284
self.color_scheme_table.set_active_scheme(scheme)
286
def interaction(self, frame, traceback):
287
__IPYTHON__.set_completer_frame(frame)
288
OldPdb.interaction(self, frame, traceback)
290
def new_do_up(self, arg):
291
OldPdb.do_up(self, arg)
292
__IPYTHON__.set_completer_frame(self.curframe)
293
do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
295
def new_do_down(self, arg):
296
OldPdb.do_down(self, arg)
297
__IPYTHON__.set_completer_frame(self.curframe)
299
do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
301
def new_do_frame(self, arg):
302
OldPdb.do_frame(self, arg)
303
__IPYTHON__.set_completer_frame(self.curframe)
305
def new_do_quit(self, arg):
307
if hasattr(self, 'old_all_completions'):
308
__IPYTHON__.Completer.all_completions=self.old_all_completions
311
return OldPdb.do_quit(self, arg)
313
do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
315
def new_do_restart(self, arg):
316
"""Restart command. In the context of ipython this is exactly the same
318
self.msg("Restart doesn't make sense here. Using 'quit' instead.")
319
return self.do_quit(arg)
322
__IPYTHON__.set_completer_frame(None)
324
def print_stack_trace(self):
326
for frame_lineno in self.stack:
327
self.print_stack_entry(frame_lineno, context = 5)
328
except KeyboardInterrupt:
331
def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
333
#frame, lineno = frame_lineno
334
print >>Term.cout, self.format_stack_entry(frame_lineno, '', context)
336
def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
337
import linecache, repr
341
Colors = self.color_scheme_table.active_colors
342
ColorsNormal = Colors.Normal
343
tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
344
tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
345
tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
346
tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
349
frame, lineno = frame_lineno
352
if '__return__' in frame.f_locals:
353
rv = frame.f_locals['__return__']
354
#return_value += '->'
355
return_value += repr.repr(rv) + '\n'
356
ret.append(return_value)
358
#s = filename + '(' + `lineno` + ')'
359
filename = self.canonic(frame.f_code.co_filename)
360
link = tpl_link % filename
362
if frame.f_code.co_name:
363
func = frame.f_code.co_name
369
if '__args__' in frame.f_locals:
370
args = repr.repr(frame.f_locals['__args__'])
373
call = tpl_call % (func, args)
375
# The level info should be generated in the same format pdb uses, to
376
# avoid breaking the pdbtrack functionality of python-mode in *emacs.
377
if frame is self.curframe:
381
ret.append('%s(%s)%s\n' % (link,lineno,call))
383
start = lineno - 1 - context//2
384
lines = linecache.getlines(filename)
385
start = max(start, 0)
386
start = min(start, len(lines) - context)
387
lines = lines[start : start + context]
389
for i,line in enumerate(lines):
390
show_arrow = (start + 1 + i == lineno)
391
linetpl = (frame is self.curframe or show_arrow) \
394
ret.append(self.__format_line(linetpl, filename,
396
arrow = show_arrow) )
400
def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
404
scheme = self.color_scheme_table.active_scheme_name
405
new_line, err = self.parser.format2(line, 'str', scheme)
406
if not err: line = new_line
409
if lineno in self.get_file_breaks(filename):
410
bps = self.get_breaks(filename, lineno)
414
Colors = self.color_scheme_table.active_colors
415
bp_mark = str(bp.number)
416
bp_mark_color = Colors.breakpoint_enabled
418
bp_mark_color = Colors.breakpoint_disabled
422
# This is the line with the error
423
pad = numbers_width - len(str(lineno)) - len(bp_mark)
425
marker = '-'*(pad-3) + '-> '
432
num = '%s%s' % (marker, str(lineno))
433
line = tpl_line % (bp_mark_color + bp_mark, num, line)
435
num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
436
line = tpl_line % (bp_mark_color + bp_mark, num, line)
440
def list_command_pydb(self, arg):
441
"""List command to use if we have a newer pydb installed"""
442
filename, first, last = OldPdb.parse_list_cmd(self, arg)
443
if filename is not None:
444
self.print_list_lines(filename, first, last)
446
def print_list_lines(self, filename, first, last):
447
"""The printing (as opposed to the parsing part of a 'list'
450
Colors = self.color_scheme_table.active_colors
451
ColorsNormal = Colors.Normal
452
tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
453
tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
455
for lineno in range(first, last+1):
456
line = linecache.getline(filename, lineno)
460
if lineno == self.curframe.f_lineno:
461
line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
463
line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
468
print >>Term.cout, ''.join(src)
470
except KeyboardInterrupt:
473
def do_list(self, arg):
474
self.lastcmd = 'list'
478
x = eval(arg, {}, {})
479
if type(x) == type(()):
484
# Assume it's a count
487
first = max(1, int(x) - 5)
489
print '*** Error in argument:', `arg`
491
elif self.lineno is None:
492
first = max(1, self.curframe.f_lineno - 5)
494
first = self.lineno + 1
497
self.print_list_lines(self.curframe.f_code.co_filename, first, last)
501
def do_pdef(self, arg):
502
"""The debugger interface to magic_pdef"""
503
namespaces = [('Locals', self.curframe.f_locals),
504
('Globals', self.curframe.f_globals)]
505
__IPYTHON__.magic_pdef(arg, namespaces=namespaces)
507
def do_pdoc(self, arg):
508
"""The debugger interface to magic_pdoc"""
509
namespaces = [('Locals', self.curframe.f_locals),
510
('Globals', self.curframe.f_globals)]
511
__IPYTHON__.magic_pdoc(arg, namespaces=namespaces)
513
def do_pinfo(self, arg):
514
"""The debugger equivalant of ?obj"""
515
namespaces = [('Locals', self.curframe.f_locals),
516
('Globals', self.curframe.f_globals)]
517
__IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)