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