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