##// END OF EJS Templates
Small Debugger fix seen only when using the code outside a running ipython instance
jdh2358 -
Show More

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

@@ -1,520 +1,520 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 $Id: Debugger.py 2203 2007-04-04 05:08:36Z fperez $"""
18 $Id: Debugger.py 2261 2007-04-20 17:50:00Z jdh2358 $"""
19 19
20 20 #*****************************************************************************
21 21 #
22 22 # Since this file is essentially a modified copy of the pdb module which is
23 23 # part of the standard Python distribution, I assume that the proper procedure
24 24 # is to maintain its copyright as belonging to the Python Software Foundation
25 25 # (in addition to my own, for all new code).
26 26 #
27 27 # Copyright (C) 2001 Python Software Foundation, www.python.org
28 28 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
29 29 #
30 30 # Distributed under the terms of the BSD License. The full license is in
31 31 # the file COPYING, distributed as part of this software.
32 32 #
33 33 #*****************************************************************************
34 34
35 35 from IPython import Release
36 36 __author__ = '%s <%s>' % Release.authors['Fernando']
37 37 __license__ = 'Python'
38 38
39 39 import bdb
40 40 import cmd
41 41 import linecache
42 42 import os
43 43 import sys
44 44
45 45 from IPython import PyColorize, ColorANSI, ipapi
46 46 from IPython.genutils import Term
47 47 from IPython.excolors import ExceptionColors
48 48
49 49 # See if we can use pydb.
50 50 has_pydb = False
51 51 prompt = 'ipdb> '
52 52 try:
53 53 import pydb
54 54 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
55 55 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
56 56 # better protetct against it.
57 57 has_pydb = True
58 58 from pydb import Pdb as OldPdb
59 59 except ImportError:
60 60 pass
61 61
62 62 if has_pydb:
63 63 from pydb import Pdb as OldPdb
64 64 prompt = 'ipydb> '
65 65 else:
66 66 from pdb import Pdb as OldPdb
67 67
68 68 # Allow the set_trace code to operate outside of an ipython instance, even if
69 69 # it does so with some limitations. The rest of this support is implemented in
70 70 # the Tracer constructor.
71 71 def BdbQuit_excepthook(et,ev,tb):
72 72 if et==bdb.BdbQuit:
73 73 print 'Exiting Debugger.'
74 74 else:
75 ehook.excepthook_ori(et,ev,tb)
75 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
76 76
77 77 def BdbQuit_IPython_excepthook(self,et,ev,tb):
78 78 print 'Exiting Debugger.'
79 79
80 80 class Tracer(object):
81 81 """Class for local debugging, similar to pdb.set_trace.
82 82
83 83 Instances of this class, when called, behave like pdb.set_trace, but
84 84 providing IPython's enhanced capabilities.
85 85
86 86 This is implemented as a class which must be initialized in your own code
87 87 and not as a standalone function because we need to detect at runtime
88 88 whether IPython is already active or not. That detection is done in the
89 89 constructor, ensuring that this code plays nicely with a running IPython,
90 90 while functioning acceptably (though with limitations) if outside of it.
91 91 """
92 92
93 93 def __init__(self,colors=None):
94 94 """Create a local debugger instance.
95 95
96 96 :Parameters:
97 97
98 98 - `colors` (None): a string containing the name of the color scheme to
99 99 use, it must be one of IPython's valid color schemes. If not given, the
100 100 function will default to the current IPython scheme when running inside
101 101 IPython, and to 'NoColor' otherwise.
102 102
103 103 Usage example:
104 104
105 105 from IPython.Debugger import Tracer; debug_here = Tracer()
106 106
107 107 ... later in your code
108 108 debug_here() # -> will open up the debugger at that point.
109 109
110 110 Once the debugger activates, you can use all of its regular commands to
111 111 step through code, set breakpoints, etc. See the pdb documentation
112 112 from the Python standard library for usage details.
113 113 """
114 114
115 115 global __IPYTHON__
116 116 try:
117 117 __IPYTHON__
118 118 except NameError:
119 119 # Outside of ipython, we set our own exception hook manually
120 120 __IPYTHON__ = ipapi.get(True,False)
121 121 BdbQuit_excepthook.excepthook_ori = sys.excepthook
122 122 sys.excepthook = BdbQuit_excepthook
123 123 def_colors = 'NoColor'
124 124 try:
125 125 # Limited tab completion support
126 126 import rlcompleter,readline
127 127 readline.parse_and_bind('tab: complete')
128 128 except ImportError:
129 129 pass
130 130 else:
131 131 # In ipython, we use its custom exception handler mechanism
132 132 ip = ipapi.get()
133 133 def_colors = ip.options.colors
134 134 ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook)
135 135
136 136 if colors is None:
137 137 colors = def_colors
138 138 self.debugger = Pdb(colors)
139 139
140 140 def __call__(self):
141 141 """Starts an interactive debugger at the point where called.
142 142
143 143 This is similar to the pdb.set_trace() function from the std lib, but
144 144 using IPython's enhanced debugger."""
145 145
146 146 self.debugger.set_trace(sys._getframe().f_back)
147 147
148 148 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
149 149 """Make new_fn have old_fn's doc string. This is particularly useful
150 150 for the do_... commands that hook into the help system.
151 151 Adapted from from a comp.lang.python posting
152 152 by Duncan Booth."""
153 153 def wrapper(*args, **kw):
154 154 return new_fn(*args, **kw)
155 155 if old_fn.__doc__:
156 156 wrapper.__doc__ = old_fn.__doc__ + additional_text
157 157 return wrapper
158 158
159 159 def _file_lines(fname):
160 160 """Return the contents of a named file as a list of lines.
161 161
162 162 This function never raises an IOError exception: if the file can't be
163 163 read, it simply returns an empty list."""
164 164
165 165 try:
166 166 outfile = open(fname)
167 167 except IOError:
168 168 return []
169 169 else:
170 170 out = outfile.readlines()
171 171 outfile.close()
172 172 return out
173 173
174 174 class Pdb(OldPdb):
175 175 """Modified Pdb class, does not load readline."""
176 176
177 177 if sys.version[:3] >= '2.5' or has_pydb:
178 178 def __init__(self,color_scheme='NoColor',completekey=None,
179 179 stdin=None, stdout=None):
180 180
181 181 # Parent constructor:
182 182 if has_pydb and completekey is None:
183 183 OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
184 184 else:
185 185 OldPdb.__init__(self,completekey,stdin,stdout)
186 186
187 187 self.prompt = prompt # The default prompt is '(Pdb)'
188 188
189 189 # IPython changes...
190 190 self.is_pydb = has_pydb
191 191
192 192 if self.is_pydb:
193 193
194 194 # iplib.py's ipalias seems to want pdb's checkline
195 195 # which located in pydb.fn
196 196 import pydb.fns
197 197 self.checkline = lambda filename, lineno: \
198 198 pydb.fns.checkline(self, filename, lineno)
199 199
200 200 self.curframe = None
201 201 self.do_restart = self.new_do_restart
202 202
203 203 self.old_all_completions = __IPYTHON__.Completer.all_completions
204 204 __IPYTHON__.Completer.all_completions=self.all_completions
205 205
206 206 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
207 207 OldPdb.do_list)
208 208 self.do_l = self.do_list
209 209 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
210 210 OldPdb.do_frame)
211 211
212 212 self.aliases = {}
213 213
214 214 # Create color table: we copy the default one from the traceback
215 215 # module and add a few attributes needed for debugging
216 216 self.color_scheme_table = ExceptionColors.copy()
217 217
218 218 # shorthands
219 219 C = ColorANSI.TermColors
220 220 cst = self.color_scheme_table
221 221
222 222 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
223 223 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
224 224
225 225 cst['Linux'].colors.breakpoint_enabled = C.LightRed
226 226 cst['Linux'].colors.breakpoint_disabled = C.Red
227 227
228 228 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
229 229 cst['LightBG'].colors.breakpoint_disabled = C.Red
230 230
231 231 self.set_colors(color_scheme)
232 232
233 233 # Add a python parser so we can syntax highlight source while
234 234 # debugging.
235 235 self.parser = PyColorize.Parser()
236 236
237 237
238 238 else:
239 239 # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor,
240 240 # because it binds readline and breaks tab-completion. This means we
241 241 # have to COPY the constructor here.
242 242 def __init__(self,color_scheme='NoColor'):
243 243 bdb.Bdb.__init__(self)
244 244 cmd.Cmd.__init__(self,completekey=None) # don't load readline
245 245 self.prompt = 'ipdb> ' # The default prompt is '(Pdb)'
246 246 self.aliases = {}
247 247
248 248 # These two lines are part of the py2.4 constructor, let's put them
249 249 # unconditionally here as they won't cause any problems in 2.3.
250 250 self.mainpyfile = ''
251 251 self._wait_for_mainpyfile = 0
252 252
253 253 # Read $HOME/.pdbrc and ./.pdbrc
254 254 try:
255 255 self.rcLines = _file_lines(os.path.join(os.environ['HOME'],
256 256 ".pdbrc"))
257 257 except KeyError:
258 258 self.rcLines = []
259 259 self.rcLines.extend(_file_lines(".pdbrc"))
260 260
261 261 # Create color table: we copy the default one from the traceback
262 262 # module and add a few attributes needed for debugging
263 263 ExceptionColors.set_active_scheme(color_scheme)
264 264 self.color_scheme_table = ExceptionColors.copy()
265 265
266 266 # shorthands
267 267 C = ColorANSI.TermColors
268 268 cst = self.color_scheme_table
269 269
270 270 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
271 271 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
272 272
273 273 cst['Linux'].colors.breakpoint_enabled = C.LightRed
274 274 cst['Linux'].colors.breakpoint_disabled = C.Red
275 275
276 276 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
277 277 cst['LightBG'].colors.breakpoint_disabled = C.Red
278 278
279 279 self.set_colors(color_scheme)
280 280
281 281 # Add a python parser so we can syntax highlight source while
282 282 # debugging.
283 283 self.parser = PyColorize.Parser()
284 284
285 285 def set_colors(self, scheme):
286 286 """Shorthand access to the color table scheme selector method."""
287 287 self.color_scheme_table.set_active_scheme(scheme)
288 288
289 289 def interaction(self, frame, traceback):
290 290 __IPYTHON__.set_completer_frame(frame)
291 291 OldPdb.interaction(self, frame, traceback)
292 292
293 293 def new_do_up(self, arg):
294 294 OldPdb.do_up(self, arg)
295 295 __IPYTHON__.set_completer_frame(self.curframe)
296 296 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
297 297
298 298 def new_do_down(self, arg):
299 299 OldPdb.do_down(self, arg)
300 300 __IPYTHON__.set_completer_frame(self.curframe)
301 301
302 302 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
303 303
304 304 def new_do_frame(self, arg):
305 305 OldPdb.do_frame(self, arg)
306 306 __IPYTHON__.set_completer_frame(self.curframe)
307 307
308 308 def new_do_quit(self, arg):
309 309
310 310 if hasattr(self, 'old_all_completions'):
311 311 __IPYTHON__.Completer.all_completions=self.old_all_completions
312 312
313 313
314 314 return OldPdb.do_quit(self, arg)
315 315
316 316 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
317 317
318 318 def new_do_restart(self, arg):
319 319 """Restart command. In the context of ipython this is exactly the same
320 320 thing as 'quit'."""
321 321 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
322 322 return self.do_quit(arg)
323 323
324 324 def postloop(self):
325 325 __IPYTHON__.set_completer_frame(None)
326 326
327 327 def print_stack_trace(self):
328 328 try:
329 329 for frame_lineno in self.stack:
330 330 self.print_stack_entry(frame_lineno, context = 5)
331 331 except KeyboardInterrupt:
332 332 pass
333 333
334 334 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
335 335 context = 3):
336 336 #frame, lineno = frame_lineno
337 337 print >>Term.cout, self.format_stack_entry(frame_lineno, '', context)
338 338
339 339 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
340 340 import linecache, repr
341 341
342 342 ret = []
343 343
344 344 Colors = self.color_scheme_table.active_colors
345 345 ColorsNormal = Colors.Normal
346 346 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
347 347 tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
348 348 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
349 349 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
350 350 ColorsNormal)
351 351
352 352 frame, lineno = frame_lineno
353 353
354 354 return_value = ''
355 355 if '__return__' in frame.f_locals:
356 356 rv = frame.f_locals['__return__']
357 357 #return_value += '->'
358 358 return_value += repr.repr(rv) + '\n'
359 359 ret.append(return_value)
360 360
361 361 #s = filename + '(' + `lineno` + ')'
362 362 filename = self.canonic(frame.f_code.co_filename)
363 363 link = tpl_link % filename
364 364
365 365 if frame.f_code.co_name:
366 366 func = frame.f_code.co_name
367 367 else:
368 368 func = "<lambda>"
369 369
370 370 call = ''
371 371 if func != '?':
372 372 if '__args__' in frame.f_locals:
373 373 args = repr.repr(frame.f_locals['__args__'])
374 374 else:
375 375 args = '()'
376 376 call = tpl_call % (func, args)
377 377
378 378 # The level info should be generated in the same format pdb uses, to
379 379 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
380 380 if frame is self.curframe:
381 381 ret.append('> ')
382 382 else:
383 383 ret.append(' ')
384 384 ret.append('%s(%s)%s\n' % (link,lineno,call))
385 385
386 386 start = lineno - 1 - context//2
387 387 lines = linecache.getlines(filename)
388 388 start = max(start, 0)
389 389 start = min(start, len(lines) - context)
390 390 lines = lines[start : start + context]
391 391
392 392 for i,line in enumerate(lines):
393 393 show_arrow = (start + 1 + i == lineno)
394 394 linetpl = (frame is self.curframe or show_arrow) \
395 395 and tpl_line_em \
396 396 or tpl_line
397 397 ret.append(self.__format_line(linetpl, filename,
398 398 start + 1 + i, line,
399 399 arrow = show_arrow) )
400 400
401 401 return ''.join(ret)
402 402
403 403 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
404 404 bp_mark = ""
405 405 bp_mark_color = ""
406 406
407 407 scheme = self.color_scheme_table.active_scheme_name
408 408 new_line, err = self.parser.format2(line, 'str', scheme)
409 409 if not err: line = new_line
410 410
411 411 bp = None
412 412 if lineno in self.get_file_breaks(filename):
413 413 bps = self.get_breaks(filename, lineno)
414 414 bp = bps[-1]
415 415
416 416 if bp:
417 417 Colors = self.color_scheme_table.active_colors
418 418 bp_mark = str(bp.number)
419 419 bp_mark_color = Colors.breakpoint_enabled
420 420 if not bp.enabled:
421 421 bp_mark_color = Colors.breakpoint_disabled
422 422
423 423 numbers_width = 7
424 424 if arrow:
425 425 # This is the line with the error
426 426 pad = numbers_width - len(str(lineno)) - len(bp_mark)
427 427 if pad >= 3:
428 428 marker = '-'*(pad-3) + '-> '
429 429 elif pad == 2:
430 430 marker = '> '
431 431 elif pad == 1:
432 432 marker = '>'
433 433 else:
434 434 marker = ''
435 435 num = '%s%s' % (marker, str(lineno))
436 436 line = tpl_line % (bp_mark_color + bp_mark, num, line)
437 437 else:
438 438 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
439 439 line = tpl_line % (bp_mark_color + bp_mark, num, line)
440 440
441 441 return line
442 442
443 443 def list_command_pydb(self, arg):
444 444 """List command to use if we have a newer pydb installed"""
445 445 filename, first, last = OldPdb.parse_list_cmd(self, arg)
446 446 if filename is not None:
447 447 self.print_list_lines(filename, first, last)
448 448
449 449 def print_list_lines(self, filename, first, last):
450 450 """The printing (as opposed to the parsing part of a 'list'
451 451 command."""
452 452 try:
453 453 Colors = self.color_scheme_table.active_colors
454 454 ColorsNormal = Colors.Normal
455 455 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
456 456 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
457 457 src = []
458 458 for lineno in range(first, last+1):
459 459 line = linecache.getline(filename, lineno)
460 460 if not line:
461 461 break
462 462
463 463 if lineno == self.curframe.f_lineno:
464 464 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
465 465 else:
466 466 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
467 467
468 468 src.append(line)
469 469 self.lineno = lineno
470 470
471 471 print >>Term.cout, ''.join(src)
472 472
473 473 except KeyboardInterrupt:
474 474 pass
475 475
476 476 def do_list(self, arg):
477 477 self.lastcmd = 'list'
478 478 last = None
479 479 if arg:
480 480 try:
481 481 x = eval(arg, {}, {})
482 482 if type(x) == type(()):
483 483 first, last = x
484 484 first = int(first)
485 485 last = int(last)
486 486 if last < first:
487 487 # Assume it's a count
488 488 last = first + last
489 489 else:
490 490 first = max(1, int(x) - 5)
491 491 except:
492 492 print '*** Error in argument:', `arg`
493 493 return
494 494 elif self.lineno is None:
495 495 first = max(1, self.curframe.f_lineno - 5)
496 496 else:
497 497 first = self.lineno + 1
498 498 if last is None:
499 499 last = first + 10
500 500 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
501 501
502 502 do_l = do_list
503 503
504 504 def do_pdef(self, arg):
505 505 """The debugger interface to magic_pdef"""
506 506 namespaces = [('Locals', self.curframe.f_locals),
507 507 ('Globals', self.curframe.f_globals)]
508 508 __IPYTHON__.magic_pdef(arg, namespaces=namespaces)
509 509
510 510 def do_pdoc(self, arg):
511 511 """The debugger interface to magic_pdoc"""
512 512 namespaces = [('Locals', self.curframe.f_locals),
513 513 ('Globals', self.curframe.f_globals)]
514 514 __IPYTHON__.magic_pdoc(arg, namespaces=namespaces)
515 515
516 516 def do_pinfo(self, arg):
517 517 """The debugger equivalant of ?obj"""
518 518 namespaces = [('Locals', self.curframe.f_locals),
519 519 ('Globals', self.curframe.f_globals)]
520 520 __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now