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