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