##// END OF EJS Templates
ipdb: fix formatting in core.debugger
Blazej Michalik -
Show More
@@ -1,833 +1,832 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 44 prompt = 'ipdb> '
45 45
46 #We have to check this directly from sys.argv, config struct not yet available
46 # We have to check this directly from sys.argv, config struct not yet available
47 47 from pdb import Pdb as OldPdb
48 48
49 49 # Allow the set_trace code to operate outside of an ipython instance, even if
50 50 # it does so with some limitations. The rest of this support is implemented in
51 51 # the Tracer constructor.
52 52
53
53 54 def make_arrow(pad):
54 55 """generate the leading arrow in front of traceback or debugger"""
55 56 if pad >= 2:
56 57 return '-'*(pad-2) + '> '
57 58 elif pad == 1:
58 59 return '>'
59 60 return ''
60 61
61 62
62 63 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
63 64 """Exception hook which handles `BdbQuit` exceptions.
64 65
65 66 All other exceptions are processed using the `excepthook`
66 67 parameter.
67 68 """
68 69 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
69 70 DeprecationWarning, stacklevel=2)
70 if et==bdb.BdbQuit:
71 if et == bdb.BdbQuit:
71 72 print('Exiting Debugger.')
72 73 elif excepthook is not None:
73 74 excepthook(et, ev, tb)
74 75 else:
75 76 # Backwards compatibility. Raise deprecation warning?
76 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
77 BdbQuit_excepthook.excepthook_ori(et, ev, tb)
77 78
78 79
79 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
80 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
80 81 warnings.warn(
81 82 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
82 83 DeprecationWarning, stacklevel=2)
83 84 print('Exiting Debugger.')
84 85
85 86
86 87 class Tracer(object):
87 88 """
88 89 DEPRECATED
89 90
90 91 Class for local debugging, similar to pdb.set_trace.
91 92
92 93 Instances of this class, when called, behave like pdb.set_trace, but
93 94 providing IPython's enhanced capabilities.
94 95
95 96 This is implemented as a class which must be initialized in your own code
96 97 and not as a standalone function because we need to detect at runtime
97 98 whether IPython is already active or not. That detection is done in the
98 99 constructor, ensuring that this code plays nicely with a running IPython,
99 100 while functioning acceptably (though with limitations) if outside of it.
100 101 """
101 102
102 103 @skip_doctest
103 104 def __init__(self, colors=None):
104 105 """
105 106 DEPRECATED
106 107
107 108 Create a local debugger instance.
108 109
109 110 Parameters
110 111 ----------
111 112
112 113 colors : str, optional
113 114 The name of the color scheme to use, it must be one of IPython's
114 115 valid color schemes. If not given, the function will default to
115 116 the current IPython scheme when running inside IPython, and to
116 117 'NoColor' otherwise.
117 118
118 119 Examples
119 120 --------
120 121 ::
121 122
122 123 from IPython.core.debugger import Tracer; debug_here = Tracer()
123 124
124 125 Later in your code::
125 126
126 127 debug_here() # -> will open up the debugger at that point.
127 128
128 129 Once the debugger activates, you can use all of its regular commands to
129 130 step through code, set breakpoints, etc. See the pdb documentation
130 131 from the Python standard library for usage details.
131 132 """
132 133 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
133 134 "`IPython.core.debugger.Pdb.set_trace()`",
134 135 DeprecationWarning, stacklevel=2)
135 136
136 137 ip = get_ipython()
137 138 if ip is None:
138 139 # Outside of ipython, we set our own exception hook manually
139 140 sys.excepthook = functools.partial(BdbQuit_excepthook,
140 141 excepthook=sys.excepthook)
141 142 def_colors = 'NoColor'
142 143 else:
143 144 # In ipython, we use its custom exception handler mechanism
144 145 def_colors = ip.colors
145 146 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
146 147
147 148 if colors is None:
148 149 colors = def_colors
149 150
150 151 # The stdlib debugger internally uses a modified repr from the `repr`
151 152 # module, that limits the length of printed strings to a hardcoded
152 153 # limit of 30 characters. That much trimming is too aggressive, let's
153 154 # at least raise that limit to 80 chars, which should be enough for
154 155 # most interactive uses.
155 156 try:
156 157 from reprlib import aRepr
157 158 aRepr.maxstring = 80
158 159 except:
159 160 # This is only a user-facing convenience, so any error we encounter
160 161 # here can be warned about but can be otherwise ignored. These
161 162 # printouts will tell us about problems if this API changes
162 163 import traceback
163 164 traceback.print_exc()
164 165
165 166 self.debugger = Pdb(colors)
166 167
167 168 def __call__(self):
168 169 """Starts an interactive debugger at the point where called.
169 170
170 171 This is similar to the pdb.set_trace() function from the std lib, but
171 172 using IPython's enhanced debugger."""
172 173
173 174 self.debugger.set_trace(sys._getframe().f_back)
174 175
175 176
176 177 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
177 178
178 179
179 180 def strip_indentation(multiline_string):
180 181 return RGX_EXTRA_INDENT.sub('', multiline_string)
181 182
182 183
183 184 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
184 185 """Make new_fn have old_fn's doc string. This is particularly useful
185 186 for the ``do_...`` commands that hook into the help system.
186 187 Adapted from from a comp.lang.python posting
187 188 by Duncan Booth."""
188 189 def wrapper(*args, **kw):
189 190 return new_fn(*args, **kw)
190 191 if old_fn.__doc__:
191 192 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
192 193 return wrapper
193 194
194 195
195 196 class Pdb(OldPdb):
196 197 """Modified Pdb class, does not load readline.
197 198
198 199 for a standalone version that uses prompt_toolkit, see
199 200 `IPython.terminal.debugger.TerminalPdb` and
200 201 `IPython.terminal.debugger.set_trace()`
201 202 """
202 203
203 204 def __init__(self, color_scheme=None, completekey=None,
204 205 stdin=None, stdout=None, context=5, **kwargs):
205 206 """Create a new IPython debugger.
206
207
207 208 :param color_scheme: Deprecated, do not use.
208 209 :param completekey: Passed to pdb.Pdb.
209 210 :param stdin: Passed to pdb.Pdb.
210 211 :param stdout: Passed to pdb.Pdb.
211 212 :param context: Number of lines of source code context to show when
212 213 displaying stacktrace information.
213 214 :param kwargs: Passed to pdb.Pdb.
214 215 The possibilities are python version dependent, see the python
215 216 docs for more info.
216 217 """
217 218
218 219 # Parent constructor:
219 220 try:
220 221 self.context = int(context)
221 222 if self.context <= 0:
222 223 raise ValueError("Context must be a positive integer")
223 224 except (TypeError, ValueError) as e:
224 225 raise ValueError("Context must be a positive integer") from e
225 226
226 227 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
227 228 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
228 229
229 230 # IPython changes...
230 231 self.shell = get_ipython()
231 232
232 233 if self.shell is None:
233 234 save_main = sys.modules['__main__']
234 235 # No IPython instance running, we must create one
235 236 from IPython.terminal.interactiveshell import \
236 237 TerminalInteractiveShell
237 238 self.shell = TerminalInteractiveShell.instance()
238 239 # needed by any code which calls __import__("__main__") after
239 240 # the debugger was entered. See also #9941.
240 sys.modules['__main__'] = save_main
241 sys.modules['__main__'] = save_main
241 242
242 243 if color_scheme is not None:
243 244 warnings.warn(
244 245 "The `color_scheme` argument is deprecated since version 5.1",
245 246 DeprecationWarning, stacklevel=2)
246 247 else:
247 248 color_scheme = self.shell.colors
248 249
249 250 self.aliases = {}
250 251
251 252 # Create color table: we copy the default one from the traceback
252 253 # module and add a few attributes needed for debugging
253 254 self.color_scheme_table = exception_colors()
254 255
255 256 # shorthands
256 257 C = coloransi.TermColors
257 258 cst = self.color_scheme_table
258 259
259 260 cst['NoColor'].colors.prompt = C.NoColor
260 261 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
261 262 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
262 263
263 264 cst['Linux'].colors.prompt = C.Green
264 265 cst['Linux'].colors.breakpoint_enabled = C.LightRed
265 266 cst['Linux'].colors.breakpoint_disabled = C.Red
266 267
267 268 cst['LightBG'].colors.prompt = C.Blue
268 269 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
269 270 cst['LightBG'].colors.breakpoint_disabled = C.Red
270 271
271 272 cst['Neutral'].colors.prompt = C.Blue
272 273 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
273 274 cst['Neutral'].colors.breakpoint_disabled = C.Red
274 275
275
276 276 # Add a python parser so we can syntax highlight source while
277 277 # debugging.
278 278 self.parser = PyColorize.Parser(style=color_scheme)
279 279 self.set_colors(color_scheme)
280 280
281 281 # Set the prompt - the default prompt is '(Pdb)'
282 282 self.prompt = prompt
283 283 self.skip_hidden = True
284 284
285 285 def set_colors(self, scheme):
286 286 """Shorthand access to the color table scheme selector method."""
287 287 self.color_scheme_table.set_active_scheme(scheme)
288 288 self.parser.style = scheme
289 289
290 290 def set_trace(self, frame=None):
291 291 if frame is None:
292 292 frame = sys._getframe().f_back
293 293 self.initial_frame = frame
294 294 return super().set_trace(frame)
295 295
296 296 def hidden_frames(self, stack):
297 297 """
298 298 Given an index in the stack return whether it should be skipped.
299 299
300 300 This is used in up/down and where to skip frames.
301 301 """
302 302 # The f_locals dictionary is updated from the actual frame
303 303 # locals whenever the .f_locals accessor is called, so we
304 304 # avoid calling it here to preserve self.curframe_locals.
305 305 # Futhermore, there is no good reason to hide the current frame.
306 306 ip_hide = [
307 307 False
308 308 if s[0] in (self.curframe, getattr(self, "initial_frame", None))
309 309 else s[0].f_locals.get("__tracebackhide__", False)
310 310 for s in stack
311 311 ]
312 312 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
313 313 if ip_start:
314 314 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
315 315 return ip_hide
316 316
317 317 def interaction(self, frame, traceback):
318 318 try:
319 319 OldPdb.interaction(self, frame, traceback)
320 320 except KeyboardInterrupt:
321 321 self.stdout.write("\n" + self.shell.get_exception_only())
322 322
323 323 def new_do_frame(self, arg):
324 324 OldPdb.do_frame(self, arg)
325 325
326 326 def new_do_quit(self, arg):
327 327
328 328 if hasattr(self, 'old_all_completions'):
329 self.shell.Completer.all_completions=self.old_all_completions
329 self.shell.Completer.all_completions = self.old_all_completions
330 330
331 331 return OldPdb.do_quit(self, arg)
332 332
333 333 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
334 334
335 335 def new_do_restart(self, arg):
336 336 """Restart command. In the context of ipython this is exactly the same
337 337 thing as 'quit'."""
338 338 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
339 339 return self.do_quit(arg)
340 340
341 341 def print_stack_trace(self, context=None):
342 342 Colors = self.color_scheme_table.active_colors
343 343 ColorsNormal = Colors.Normal
344 344 if context is None:
345 345 context = self.context
346 346 try:
347 context=int(context)
347 context = int(context)
348 348 if context <= 0:
349 349 raise ValueError("Context must be a positive integer")
350 350 except (TypeError, ValueError) as e:
351 351 raise ValueError("Context must be a positive integer") from e
352 352 try:
353 353 skipped = 0
354 354 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
355 355 if hidden and self.skip_hidden:
356 356 skipped += 1
357 357 continue
358 358 if skipped:
359 359 print(
360 360 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
361 361 )
362 362 skipped = 0
363 363 self.print_stack_entry(frame_lineno, context=context)
364 364 if skipped:
365 365 print(
366 366 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
367 367 )
368 368 except KeyboardInterrupt:
369 369 pass
370 370
371 371 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
372 372 context=None):
373 373 if context is None:
374 374 context = self.context
375 375 try:
376 context=int(context)
376 context = int(context)
377 377 if context <= 0:
378 378 raise ValueError("Context must be a positive integer")
379 379 except (TypeError, ValueError) as e:
380 380 raise ValueError("Context must be a positive integer") from e
381 381 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
382 382
383 383 # vds: >>
384 384 frame, lineno = frame_lineno
385 385 filename = frame.f_code.co_filename
386 386 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
387 387 # vds: <<
388 388
389 389 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
390 390 if context is None:
391 391 context = self.context
392 392 try:
393 context=int(context)
393 context = int(context)
394 394 if context <= 0:
395 395 print("Context must be a positive integer", file=self.stdout)
396 396 except (TypeError, ValueError):
397 397 print("Context must be a positive integer", file=self.stdout)
398 398
399 399 import reprlib
400 400
401 401 ret = []
402 402
403 403 Colors = self.color_scheme_table.active_colors
404 404 ColorsNormal = Colors.Normal
405 405 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
406 406 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
407 407 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
408 408 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
409 ColorsNormal)
409 ColorsNormal)
410 410
411 411 frame, lineno = frame_lineno
412 412
413 413 return_value = ''
414 414 if '__return__' in frame.f_locals:
415 415 rv = frame.f_locals['__return__']
416 416 #return_value += '->'
417 417 return_value += reprlib.repr(rv) + '\n'
418 418 ret.append(return_value)
419 419
420 420 #s = filename + '(' + `lineno` + ')'
421 421 filename = self.canonic(frame.f_code.co_filename)
422 422 link = tpl_link % py3compat.cast_unicode(filename)
423 423
424 424 if frame.f_code.co_name:
425 425 func = frame.f_code.co_name
426 426 else:
427 427 func = "<lambda>"
428 428
429 429 call = ''
430 430 if func != '?':
431 431 if '__args__' in frame.f_locals:
432 432 args = reprlib.repr(frame.f_locals['__args__'])
433 433 else:
434 434 args = '()'
435 435 call = tpl_call % (func, args)
436 436
437 437 # The level info should be generated in the same format pdb uses, to
438 438 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
439 439 if frame is self.curframe:
440 440 ret.append('> ')
441 441 else:
442 442 ret.append(' ')
443 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
443 ret.append(u'%s(%s)%s\n' % (link, lineno, call))
444 444
445 445 start = lineno - 1 - context//2
446 446 lines = linecache.getlines(filename)
447 447 start = min(start, len(lines) - context)
448 448 start = max(start, 0)
449 449 lines = lines[start : start + context]
450 450
451 for i,line in enumerate(lines):
451 for i, line in enumerate(lines):
452 452 show_arrow = (start + 1 + i == lineno)
453 453 linetpl = (frame is self.curframe or show_arrow) \
454 454 and tpl_line_em \
455 455 or tpl_line
456 456 ret.append(self.__format_line(linetpl, filename,
457 457 start + 1 + i, line,
458 arrow = show_arrow) )
458 arrow=show_arrow))
459 459 return ''.join(ret)
460 460
461 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
461 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
462 462 bp_mark = ""
463 463 bp_mark_color = ""
464 464
465 465 new_line, err = self.parser.format2(line, 'str')
466 466 if not err:
467 467 line = new_line
468 468
469 469 bp = None
470 470 if lineno in self.get_file_breaks(filename):
471 471 bps = self.get_breaks(filename, lineno)
472 472 bp = bps[-1]
473 473
474 474 if bp:
475 475 Colors = self.color_scheme_table.active_colors
476 476 bp_mark = str(bp.number)
477 477 bp_mark_color = Colors.breakpoint_enabled
478 478 if not bp.enabled:
479 479 bp_mark_color = Colors.breakpoint_disabled
480 480
481 481 numbers_width = 7
482 482 if arrow:
483 483 # This is the line with the error
484 484 pad = numbers_width - len(str(lineno)) - len(bp_mark)
485 485 num = '%s%s' % (make_arrow(pad), str(lineno))
486 486 else:
487 487 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
488 488
489 489 return tpl_line % (bp_mark_color + bp_mark, num, line)
490 490
491
492 491 def print_list_lines(self, filename, first, last):
493 492 """The printing (as opposed to the parsing part of a 'list'
494 493 command."""
495 494 try:
496 495 Colors = self.color_scheme_table.active_colors
497 496 ColorsNormal = Colors.Normal
498 497 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
499 498 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
500 499 src = []
501 500 if filename == "<string>" and hasattr(self, "_exec_filename"):
502 501 filename = self._exec_filename
503 502
504 503 for lineno in range(first, last+1):
505 504 line = linecache.getline(filename, lineno)
506 505 if not line:
507 506 break
508 507
509 508 if lineno == self.curframe.f_lineno:
510 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
509 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow=True)
511 510 else:
512 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
511 line = self.__format_line(tpl_line, filename, lineno, line, arrow=False)
513 512
514 513 src.append(line)
515 514 self.lineno = lineno
516 515
517 516 print(''.join(src), file=self.stdout)
518 517
519 518 except KeyboardInterrupt:
520 519 pass
521 520
522 521 def do_skip_hidden(self, arg):
523 522 """
524 523 Change whether or not we should skip frames with the
525 524 __tracebackhide__ attribute.
526 525 """
527 526 if arg.strip().lower() in ("true", "yes"):
528 527 self.skip_hidden = True
529 528 elif arg.strip().lower() in ("false", "no"):
530 529 self.skip_hidden = False
531 530
532 531 def do_list(self, arg):
533 532 """Print lines of code from the current stack frame
534 533 """
535 534 self.lastcmd = 'list'
536 535 last = None
537 536 if arg:
538 537 try:
539 538 x = eval(arg, {}, {})
540 539 if type(x) == type(()):
541 540 first, last = x
542 541 first = int(first)
543 542 last = int(last)
544 543 if last < first:
545 544 # Assume it's a count
546 545 last = first + last
547 546 else:
548 547 first = max(1, int(x) - 5)
549 548 except:
550 549 print('*** Error in argument:', repr(arg), file=self.stdout)
551 550 return
552 551 elif self.lineno is None:
553 552 first = max(1, self.curframe.f_lineno - 5)
554 553 else:
555 554 first = self.lineno + 1
556 555 if last is None:
557 556 last = first + 10
558 557 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
559 558
560 559 # vds: >>
561 560 lineno = first
562 561 filename = self.curframe.f_code.co_filename
563 562 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
564 563 # vds: <<
565 564
566 565 do_l = do_list
567 566
568 567 def getsourcelines(self, obj):
569 568 lines, lineno = inspect.findsource(obj)
570 569 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
571 570 # must be a module frame: do not try to cut a block out of it
572 571 return lines, 1
573 572 elif inspect.ismodule(obj):
574 573 return lines, 1
575 574 return inspect.getblock(lines[lineno:]), lineno+1
576 575
577 576 def do_longlist(self, arg):
578 577 """Print lines of code from the current stack frame.
579 578
580 579 Shows more lines than 'list' does.
581 580 """
582 581 self.lastcmd = 'longlist'
583 582 try:
584 583 lines, lineno = self.getsourcelines(self.curframe)
585 584 except OSError as err:
586 585 self.error(err)
587 586 return
588 587 last = lineno + len(lines)
589 588 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
590 589 do_ll = do_longlist
591 590
592 591 def do_debug(self, arg):
593 592 """debug code
594 593 Enter a recursive debugger that steps through the code
595 594 argument (which is an arbitrary expression or statement to be
596 595 executed in the current environment).
597 596 """
598 597 trace_function = sys.gettrace()
599 598 sys.settrace(None)
600 599 globals = self.curframe.f_globals
601 600 locals = self.curframe_locals
602 601 p = self.__class__(completekey=self.completekey,
603 602 stdin=self.stdin, stdout=self.stdout)
604 603 p.use_rawinput = self.use_rawinput
605 604 p.prompt = "(%s) " % self.prompt.strip()
606 605 self.message("ENTERING RECURSIVE DEBUGGER")
607 606 sys.call_tracing(p.run, (arg, globals, locals))
608 607 self.message("LEAVING RECURSIVE DEBUGGER")
609 608 sys.settrace(trace_function)
610 609 self.lastcmd = p.lastcmd
611 610
612 611 def do_pdef(self, arg):
613 612 """Print the call signature for any callable object.
614 613
615 614 The debugger interface to %pdef"""
616 615 namespaces = [
617 616 ("Locals", self.curframe_locals),
618 617 ("Globals", self.curframe.f_globals),
619 618 ]
620 619 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
621 620
622 621 def do_pdoc(self, arg):
623 622 """Print the docstring for an object.
624 623
625 624 The debugger interface to %pdoc."""
626 625 namespaces = [
627 626 ("Locals", self.curframe_locals),
628 627 ("Globals", self.curframe.f_globals),
629 628 ]
630 629 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
631 630
632 631 def do_pfile(self, arg):
633 632 """Print (or run through pager) the file where an object is defined.
634 633
635 634 The debugger interface to %pfile.
636 635 """
637 636 namespaces = [
638 637 ("Locals", self.curframe_locals),
639 638 ("Globals", self.curframe.f_globals),
640 639 ]
641 640 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
642 641
643 642 def do_pinfo(self, arg):
644 643 """Provide detailed information about an object.
645 644
646 645 The debugger interface to %pinfo, i.e., obj?."""
647 646 namespaces = [
648 647 ("Locals", self.curframe_locals),
649 648 ("Globals", self.curframe.f_globals),
650 649 ]
651 650 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
652 651
653 652 def do_pinfo2(self, arg):
654 653 """Provide extra detailed information about an object.
655 654
656 655 The debugger interface to %pinfo2, i.e., obj??."""
657 656 namespaces = [
658 657 ("Locals", self.curframe_locals),
659 658 ("Globals", self.curframe.f_globals),
660 659 ]
661 660 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
662 661
663 662 def do_psource(self, arg):
664 663 """Print (or run through pager) the source code for an object."""
665 664 namespaces = [
666 665 ("Locals", self.curframe_locals),
667 666 ("Globals", self.curframe.f_globals),
668 667 ]
669 668 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
670 669
671 670 def do_where(self, arg):
672 671 """w(here)
673 672 Print a stack trace, with the most recent frame at the bottom.
674 673 An arrow indicates the "current frame", which determines the
675 674 context of most commands. 'bt' is an alias for this command.
676 675
677 676 Take a number as argument as an (optional) number of context line to
678 677 print"""
679 678 if arg:
680 679 try:
681 680 context = int(arg)
682 681 except ValueError as err:
683 682 self.error(err)
684 683 return
685 684 self.print_stack_trace(context)
686 685 else:
687 686 self.print_stack_trace()
688 687
689 688 do_w = do_where
690 689
691 690 def stop_here(self, frame):
692 691 hidden = False
693 692 if self.skip_hidden:
694 693 hidden = frame.f_locals.get("__tracebackhide__", False)
695 694 if hidden:
696 695 Colors = self.color_scheme_table.active_colors
697 696 ColorsNormal = Colors.Normal
698 697 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
699 698
700 699 return super().stop_here(frame)
701 700
702 701 def do_up(self, arg):
703 702 """u(p) [count]
704 703 Move the current frame count (default one) levels up in the
705 704 stack trace (to an older frame).
706 705
707 706 Will skip hidden frames.
708 707 """
709 ## modified version of upstream that skips
708 # modified version of upstream that skips
710 709 # frames with __tracebackide__
711 710 if self.curindex == 0:
712 711 self.error("Oldest frame")
713 712 return
714 713 try:
715 714 count = int(arg or 1)
716 715 except ValueError:
717 716 self.error("Invalid frame count (%s)" % arg)
718 717 return
719 718 skipped = 0
720 719 if count < 0:
721 720 _newframe = 0
722 721 else:
723 722 _newindex = self.curindex
724 723 counter = 0
725 724 hidden_frames = self.hidden_frames(self.stack)
726 725 for i in range(self.curindex - 1, -1, -1):
727 726 frame = self.stack[i][0]
728 727 if hidden_frames[i] and self.skip_hidden:
729 728 skipped += 1
730 729 continue
731 730 counter += 1
732 731 if counter >= count:
733 732 break
734 733 else:
735 734 # if no break occured.
736 735 self.error(
737 736 "all frames above hidden, use `skip_hidden False` to get get into those."
738 737 )
739 738 return
740 739
741 740 Colors = self.color_scheme_table.active_colors
742 741 ColorsNormal = Colors.Normal
743 742 _newframe = i
744 743 self._select_frame(_newframe)
745 744 if skipped:
746 745 print(
747 746 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
748 747 )
749 748
750 749 def do_down(self, arg):
751 750 """d(own) [count]
752 751 Move the current frame count (default one) levels down in the
753 752 stack trace (to a newer frame).
754 753
755 754 Will skip hidden frames.
756 755 """
757 756 if self.curindex + 1 == len(self.stack):
758 757 self.error("Newest frame")
759 758 return
760 759 try:
761 760 count = int(arg or 1)
762 761 except ValueError:
763 762 self.error("Invalid frame count (%s)" % arg)
764 763 return
765 764 if count < 0:
766 765 _newframe = len(self.stack) - 1
767 766 else:
768 767 _newindex = self.curindex
769 768 counter = 0
770 769 skipped = 0
771 770 hidden_frames = self.hidden_frames(self.stack)
772 771 for i in range(self.curindex + 1, len(self.stack)):
773 772 frame = self.stack[i][0]
774 773 if hidden_frames[i] and self.skip_hidden:
775 774 skipped += 1
776 775 continue
777 776 counter += 1
778 777 if counter >= count:
779 778 break
780 779 else:
781 780 self.error(
782 781 "all frames bellow hidden, use `skip_hidden False` to get get into those."
783 782 )
784 783 return
785 784
786 785 Colors = self.color_scheme_table.active_colors
787 786 ColorsNormal = Colors.Normal
788 787 if skipped:
789 788 print(
790 789 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
791 790 )
792 791 _newframe = i
793 792
794 793 self._select_frame(_newframe)
795 794
796 795 do_d = do_down
797 796 do_u = do_up
798 797
799 798 class InterruptiblePdb(Pdb):
800 799 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
801 800
802 801 def cmdloop(self):
803 802 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
804 803 try:
805 804 return OldPdb.cmdloop(self)
806 805 except KeyboardInterrupt:
807 806 self.stop_here = lambda frame: False
808 807 self.do_quit("")
809 808 sys.settrace(None)
810 809 self.quitting = False
811 810 raise
812 811
813 812 def _cmdloop(self):
814 813 while True:
815 814 try:
816 815 # keyboard interrupts allow for an easy way to cancel
817 816 # the current command, so allow them during interactive input
818 817 self.allow_kbdint = True
819 818 self.cmdloop()
820 819 self.allow_kbdint = False
821 820 break
822 821 except KeyboardInterrupt:
823 822 self.message('--KeyboardInterrupt--')
824 823 raise
825 824
826 825
827 826 def set_trace(frame=None):
828 827 """
829 828 Start debugging from `frame`.
830 829
831 830 If frame is not specified, debugging starts from caller's frame.
832 831 """
833 832 Pdb().set_trace(frame or sys._getframe().f_back)
General Comments 0
You need to be logged in to leave comments. Login now