##// END OF EJS Templates
restore sys.modules["__main__"] in the debugger...
mbyt -
Show More
@@ -1,632 +1,636 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 save_main = sys.modules['__main__']
230 231 # No IPython instance running, we must create one
231 232 from IPython.terminal.interactiveshell import \
232 233 TerminalInteractiveShell
233 234 self.shell = TerminalInteractiveShell.instance()
235 # needed by any code which calls __import__("__main__") after
236 # the debugger was entered. See also #9941.
237 sys.modules['__main__'] = save_main
234 238
235 239 if color_scheme is not None:
236 240 warnings.warn(
237 241 "The `color_scheme` argument is deprecated since version 5.1",
238 242 DeprecationWarning)
239 243 else:
240 244 color_scheme = self.shell.colors
241 245
242 246 self.aliases = {}
243 247
244 248 # Create color table: we copy the default one from the traceback
245 249 # module and add a few attributes needed for debugging
246 250 self.color_scheme_table = exception_colors()
247 251
248 252 # shorthands
249 253 C = coloransi.TermColors
250 254 cst = self.color_scheme_table
251 255
252 256 cst['NoColor'].colors.prompt = C.NoColor
253 257 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
254 258 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
255 259
256 260 cst['Linux'].colors.prompt = C.Green
257 261 cst['Linux'].colors.breakpoint_enabled = C.LightRed
258 262 cst['Linux'].colors.breakpoint_disabled = C.Red
259 263
260 264 cst['LightBG'].colors.prompt = C.Blue
261 265 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
262 266 cst['LightBG'].colors.breakpoint_disabled = C.Red
263 267
264 268 cst['Neutral'].colors.prompt = C.Blue
265 269 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
266 270 cst['Neutral'].colors.breakpoint_disabled = C.Red
267 271
268 272 self.set_colors(color_scheme)
269 273
270 274 # Add a python parser so we can syntax highlight source while
271 275 # debugging.
272 276 self.parser = PyColorize.Parser()
273 277
274 278 # Set the prompt - the default prompt is '(Pdb)'
275 279 self.prompt = prompt
276 280
277 281 def set_colors(self, scheme):
278 282 """Shorthand access to the color table scheme selector method."""
279 283 self.color_scheme_table.set_active_scheme(scheme)
280 284
281 285 def trace_dispatch(self, frame, event, arg):
282 286 try:
283 287 return super(Pdb, self).trace_dispatch(frame, event, arg)
284 288 except bdb.BdbQuit:
285 289 pass
286 290
287 291 def interaction(self, frame, traceback):
288 292 try:
289 293 OldPdb.interaction(self, frame, traceback)
290 294 except KeyboardInterrupt:
291 295 sys.stdout.write('\n' + self.shell.get_exception_only())
292 296
293 297 def parseline(self, line):
294 298 if line.startswith("!!"):
295 299 # Force standard behavior.
296 300 return super(Pdb, self).parseline(line[2:])
297 301 # "Smart command mode" from pdb++: don't execute commands if a variable
298 302 # with the same name exists.
299 303 cmd, arg, newline = super(Pdb, self).parseline(line)
300 304 # Fix for #9611: Do not trigger smart command if the command is `exit`
301 305 # or `quit` and it would resolve to their *global* value (the
302 306 # `ExitAutocall` object). Just checking that it is not present in the
303 307 # locals dict is not enough as locals and globals match at the
304 308 # toplevel.
305 309 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
306 310 and not (cmd in ["exit", "quit"]
307 311 and (self.curframe.f_locals is self.curframe.f_globals
308 312 or cmd not in self.curframe.f_locals))):
309 313 return super(Pdb, self).parseline("!" + line)
310 314 return super(Pdb, self).parseline(line)
311 315
312 316 def new_do_up(self, arg):
313 317 OldPdb.do_up(self, arg)
314 318 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
315 319
316 320 def new_do_down(self, arg):
317 321 OldPdb.do_down(self, arg)
318 322
319 323 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
320 324
321 325 def new_do_frame(self, arg):
322 326 OldPdb.do_frame(self, arg)
323 327
324 328 def new_do_quit(self, arg):
325 329
326 330 if hasattr(self, 'old_all_completions'):
327 331 self.shell.Completer.all_completions=self.old_all_completions
328 332
329 333 return OldPdb.do_quit(self, arg)
330 334
331 335 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
332 336
333 337 def new_do_restart(self, arg):
334 338 """Restart command. In the context of ipython this is exactly the same
335 339 thing as 'quit'."""
336 340 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
337 341 return self.do_quit(arg)
338 342
339 343 def print_stack_trace(self, context=None):
340 344 if context is None:
341 345 context = self.context
342 346 try:
343 347 context=int(context)
344 348 if context <= 0:
345 349 raise ValueError("Context must be a positive integer")
346 350 except (TypeError, ValueError):
347 351 raise ValueError("Context must be a positive integer")
348 352 try:
349 353 for frame_lineno in self.stack:
350 354 self.print_stack_entry(frame_lineno, context=context)
351 355 except KeyboardInterrupt:
352 356 pass
353 357
354 358 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
355 359 context=None):
356 360 if context is None:
357 361 context = self.context
358 362 try:
359 363 context=int(context)
360 364 if context <= 0:
361 365 raise ValueError("Context must be a positive integer")
362 366 except (TypeError, ValueError):
363 367 raise ValueError("Context must be a positive integer")
364 368 print(self.format_stack_entry(frame_lineno, '', context))
365 369
366 370 # vds: >>
367 371 frame, lineno = frame_lineno
368 372 filename = frame.f_code.co_filename
369 373 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
370 374 # vds: <<
371 375
372 376 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
373 377 if context is None:
374 378 context = self.context
375 379 try:
376 380 context=int(context)
377 381 if context <= 0:
378 382 print("Context must be a positive integer")
379 383 except (TypeError, ValueError):
380 384 print("Context must be a positive integer")
381 385 try:
382 386 import reprlib # Py 3
383 387 except ImportError:
384 388 import repr as reprlib # Py 2
385 389
386 390 ret = []
387 391
388 392 Colors = self.color_scheme_table.active_colors
389 393 ColorsNormal = Colors.Normal
390 394 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
391 395 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
392 396 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
393 397 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
394 398 ColorsNormal)
395 399
396 400 frame, lineno = frame_lineno
397 401
398 402 return_value = ''
399 403 if '__return__' in frame.f_locals:
400 404 rv = frame.f_locals['__return__']
401 405 #return_value += '->'
402 406 return_value += reprlib.repr(rv) + '\n'
403 407 ret.append(return_value)
404 408
405 409 #s = filename + '(' + `lineno` + ')'
406 410 filename = self.canonic(frame.f_code.co_filename)
407 411 link = tpl_link % py3compat.cast_unicode(filename)
408 412
409 413 if frame.f_code.co_name:
410 414 func = frame.f_code.co_name
411 415 else:
412 416 func = "<lambda>"
413 417
414 418 call = ''
415 419 if func != '?':
416 420 if '__args__' in frame.f_locals:
417 421 args = reprlib.repr(frame.f_locals['__args__'])
418 422 else:
419 423 args = '()'
420 424 call = tpl_call % (func, args)
421 425
422 426 # The level info should be generated in the same format pdb uses, to
423 427 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
424 428 if frame is self.curframe:
425 429 ret.append('> ')
426 430 else:
427 431 ret.append(' ')
428 432 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
429 433
430 434 start = lineno - 1 - context//2
431 435 lines = ulinecache.getlines(filename)
432 436 start = min(start, len(lines) - context)
433 437 start = max(start, 0)
434 438 lines = lines[start : start + context]
435 439
436 440 for i,line in enumerate(lines):
437 441 show_arrow = (start + 1 + i == lineno)
438 442 linetpl = (frame is self.curframe or show_arrow) \
439 443 and tpl_line_em \
440 444 or tpl_line
441 445 ret.append(self.__format_line(linetpl, filename,
442 446 start + 1 + i, line,
443 447 arrow = show_arrow) )
444 448 return ''.join(ret)
445 449
446 450 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
447 451 bp_mark = ""
448 452 bp_mark_color = ""
449 453
450 454 scheme = self.color_scheme_table.active_scheme_name
451 455 new_line, err = self.parser.format2(line, 'str', scheme)
452 456 if not err: line = new_line
453 457
454 458 bp = None
455 459 if lineno in self.get_file_breaks(filename):
456 460 bps = self.get_breaks(filename, lineno)
457 461 bp = bps[-1]
458 462
459 463 if bp:
460 464 Colors = self.color_scheme_table.active_colors
461 465 bp_mark = str(bp.number)
462 466 bp_mark_color = Colors.breakpoint_enabled
463 467 if not bp.enabled:
464 468 bp_mark_color = Colors.breakpoint_disabled
465 469
466 470 numbers_width = 7
467 471 if arrow:
468 472 # This is the line with the error
469 473 pad = numbers_width - len(str(lineno)) - len(bp_mark)
470 474 num = '%s%s' % (make_arrow(pad), str(lineno))
471 475 else:
472 476 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
473 477
474 478 return tpl_line % (bp_mark_color + bp_mark, num, line)
475 479
476 480
477 481 def print_list_lines(self, filename, first, last):
478 482 """The printing (as opposed to the parsing part of a 'list'
479 483 command."""
480 484 try:
481 485 Colors = self.color_scheme_table.active_colors
482 486 ColorsNormal = Colors.Normal
483 487 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
484 488 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
485 489 src = []
486 490 if filename == "<string>" and hasattr(self, "_exec_filename"):
487 491 filename = self._exec_filename
488 492
489 493 for lineno in range(first, last+1):
490 494 line = ulinecache.getline(filename, lineno)
491 495 if not line:
492 496 break
493 497
494 498 if lineno == self.curframe.f_lineno:
495 499 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
496 500 else:
497 501 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
498 502
499 503 src.append(line)
500 504 self.lineno = lineno
501 505
502 506 print(''.join(src))
503 507
504 508 except KeyboardInterrupt:
505 509 pass
506 510
507 511 def do_list(self, arg):
508 512 self.lastcmd = 'list'
509 513 last = None
510 514 if arg:
511 515 try:
512 516 x = eval(arg, {}, {})
513 517 if type(x) == type(()):
514 518 first, last = x
515 519 first = int(first)
516 520 last = int(last)
517 521 if last < first:
518 522 # Assume it's a count
519 523 last = first + last
520 524 else:
521 525 first = max(1, int(x) - 5)
522 526 except:
523 527 print('*** Error in argument:', repr(arg))
524 528 return
525 529 elif self.lineno is None:
526 530 first = max(1, self.curframe.f_lineno - 5)
527 531 else:
528 532 first = self.lineno + 1
529 533 if last is None:
530 534 last = first + 10
531 535 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
532 536
533 537 # vds: >>
534 538 lineno = first
535 539 filename = self.curframe.f_code.co_filename
536 540 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
537 541 # vds: <<
538 542
539 543 do_l = do_list
540 544
541 545 def getsourcelines(self, obj):
542 546 lines, lineno = inspect.findsource(obj)
543 547 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
544 548 # must be a module frame: do not try to cut a block out of it
545 549 return lines, 1
546 550 elif inspect.ismodule(obj):
547 551 return lines, 1
548 552 return inspect.getblock(lines[lineno:]), lineno+1
549 553
550 554 def do_longlist(self, arg):
551 555 self.lastcmd = 'longlist'
552 556 try:
553 557 lines, lineno = self.getsourcelines(self.curframe)
554 558 except OSError as err:
555 559 self.error(err)
556 560 return
557 561 last = lineno + len(lines)
558 562 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
559 563 do_ll = do_longlist
560 564
561 565 def do_pdef(self, arg):
562 566 """Print the call signature for any callable object.
563 567
564 568 The debugger interface to %pdef"""
565 569 namespaces = [('Locals', self.curframe.f_locals),
566 570 ('Globals', self.curframe.f_globals)]
567 571 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
568 572
569 573 def do_pdoc(self, arg):
570 574 """Print the docstring for an object.
571 575
572 576 The debugger interface to %pdoc."""
573 577 namespaces = [('Locals', self.curframe.f_locals),
574 578 ('Globals', self.curframe.f_globals)]
575 579 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
576 580
577 581 def do_pfile(self, arg):
578 582 """Print (or run through pager) the file where an object is defined.
579 583
580 584 The debugger interface to %pfile.
581 585 """
582 586 namespaces = [('Locals', self.curframe.f_locals),
583 587 ('Globals', self.curframe.f_globals)]
584 588 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
585 589
586 590 def do_pinfo(self, arg):
587 591 """Provide detailed information about an object.
588 592
589 593 The debugger interface to %pinfo, i.e., obj?."""
590 594 namespaces = [('Locals', self.curframe.f_locals),
591 595 ('Globals', self.curframe.f_globals)]
592 596 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
593 597
594 598 def do_pinfo2(self, arg):
595 599 """Provide extra detailed information about an object.
596 600
597 601 The debugger interface to %pinfo2, i.e., obj??."""
598 602 namespaces = [('Locals', self.curframe.f_locals),
599 603 ('Globals', self.curframe.f_globals)]
600 604 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
601 605
602 606 def do_psource(self, arg):
603 607 """Print (or run through pager) the source code for an object."""
604 608 namespaces = [('Locals', self.curframe.f_locals),
605 609 ('Globals', self.curframe.f_globals)]
606 610 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
607 611
608 612 if sys.version_info > (3, ):
609 613 def do_where(self, arg):
610 614 """w(here)
611 615 Print a stack trace, with the most recent frame at the bottom.
612 616 An arrow indicates the "current frame", which determines the
613 617 context of most commands. 'bt' is an alias for this command.
614 618
615 619 Take a number as argument as an (optional) number of context line to
616 620 print"""
617 621 if arg:
618 622 context = int(arg)
619 623 self.print_stack_trace(context)
620 624 else:
621 625 self.print_stack_trace()
622 626
623 627 do_w = do_where
624 628
625 629
626 630 def set_trace(frame=None):
627 631 """
628 632 Start debugging from `frame`.
629 633
630 634 If frame is not specified, debugging starts from caller's frame.
631 635 """
632 636 Pdb().set_trace(frame or sys._getframe().f_back)
General Comments 0
You need to be logged in to leave comments. Login now