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