##// END OF EJS Templates
Backport PR #13000: Allow to control if reporting hidden frames
Matthias Bussonnier -
Show More
@@ -1,946 +1,949 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 import os
37 37
38 38 from IPython import get_ipython
39 39 from IPython.utils import PyColorize
40 40 from IPython.utils import coloransi, py3compat
41 41 from IPython.core.excolors import exception_colors
42 42 from IPython.testing.skipdoctest import skip_doctest
43 43
44 44
45 45 prompt = 'ipdb> '
46 46
47 47 # We have to check this directly from sys.argv, config struct not yet available
48 48 from pdb import Pdb as OldPdb
49 49
50 50 # Allow the set_trace code to operate outside of an ipython instance, even if
51 51 # it does so with some limitations. The rest of this support is implemented in
52 52 # the Tracer constructor.
53 53
54 54
55 55 def make_arrow(pad):
56 56 """generate the leading arrow in front of traceback or debugger"""
57 57 if pad >= 2:
58 58 return '-'*(pad-2) + '> '
59 59 elif pad == 1:
60 60 return '>'
61 61 return ''
62 62
63 63
64 64 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
65 65 """Exception hook which handles `BdbQuit` exceptions.
66 66
67 67 All other exceptions are processed using the `excepthook`
68 68 parameter.
69 69 """
70 70 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
71 71 DeprecationWarning, stacklevel=2)
72 72 if et == bdb.BdbQuit:
73 73 print('Exiting Debugger.')
74 74 elif excepthook is not None:
75 75 excepthook(et, ev, tb)
76 76 else:
77 77 # Backwards compatibility. Raise deprecation warning?
78 78 BdbQuit_excepthook.excepthook_ori(et, ev, tb)
79 79
80 80
81 81 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
82 82 warnings.warn(
83 83 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
84 84 DeprecationWarning, stacklevel=2)
85 85 print('Exiting Debugger.')
86 86
87 87
88 88 class Tracer(object):
89 89 """
90 90 DEPRECATED
91 91
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 """
107 107 DEPRECATED
108 108
109 109 Create a local debugger instance.
110 110
111 111 Parameters
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 This debugger can hide and skip frames that are tagged according to some predicates.
205 205 See the `skip_predicates` commands.
206 206
207 207 """
208 208
209 209 default_predicates = {"tbhide": True, "readonly": False, "ipython_internal": True}
210 210
211 211 def __init__(self, color_scheme=None, completekey=None,
212 212 stdin=None, stdout=None, context=5, **kwargs):
213 213 """Create a new IPython debugger.
214 214
215 215 Parameters
216 216 ----------
217 217 color_scheme : default None
218 218 Deprecated, do not use.
219 219 completekey : default None
220 220 Passed to pdb.Pdb.
221 221 stdin : default None
222 222 Passed to pdb.Pdb.
223 223 stdout : default None
224 224 Passed to pdb.Pdb.
225 225 context : int
226 226 Number of lines of source code context to show when
227 227 displaying stacktrace information.
228 228 **kwargs
229 229 Passed to pdb.Pdb.
230 230
231 231 Notes
232 232 -----
233 233 The possibilities are python version dependent, see the python
234 234 docs for more info.
235 235 """
236 236
237 237 # Parent constructor:
238 238 try:
239 239 self.context = int(context)
240 240 if self.context <= 0:
241 241 raise ValueError("Context must be a positive integer")
242 242 except (TypeError, ValueError) as e:
243 243 raise ValueError("Context must be a positive integer") from e
244 244
245 245 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
246 246 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
247 247
248 248 # IPython changes...
249 249 self.shell = get_ipython()
250 250
251 251 if self.shell is None:
252 252 save_main = sys.modules['__main__']
253 253 # No IPython instance running, we must create one
254 254 from IPython.terminal.interactiveshell import \
255 255 TerminalInteractiveShell
256 256 self.shell = TerminalInteractiveShell.instance()
257 257 # needed by any code which calls __import__("__main__") after
258 258 # the debugger was entered. See also #9941.
259 259 sys.modules["__main__"] = save_main
260 260
261 261 if color_scheme is not None:
262 262 warnings.warn(
263 263 "The `color_scheme` argument is deprecated since version 5.1",
264 264 DeprecationWarning, stacklevel=2)
265 265 else:
266 266 color_scheme = self.shell.colors
267 267
268 268 self.aliases = {}
269 269
270 270 # Create color table: we copy the default one from the traceback
271 271 # module and add a few attributes needed for debugging
272 272 self.color_scheme_table = exception_colors()
273 273
274 274 # shorthands
275 275 C = coloransi.TermColors
276 276 cst = self.color_scheme_table
277 277
278 278 cst['NoColor'].colors.prompt = C.NoColor
279 279 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
280 280 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
281 281
282 282 cst['Linux'].colors.prompt = C.Green
283 283 cst['Linux'].colors.breakpoint_enabled = C.LightRed
284 284 cst['Linux'].colors.breakpoint_disabled = C.Red
285 285
286 286 cst['LightBG'].colors.prompt = C.Blue
287 287 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
288 288 cst['LightBG'].colors.breakpoint_disabled = C.Red
289 289
290 290 cst['Neutral'].colors.prompt = C.Blue
291 291 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
292 292 cst['Neutral'].colors.breakpoint_disabled = C.Red
293 293
294 294 # Add a python parser so we can syntax highlight source while
295 295 # debugging.
296 296 self.parser = PyColorize.Parser(style=color_scheme)
297 297 self.set_colors(color_scheme)
298 298
299 299 # Set the prompt - the default prompt is '(Pdb)'
300 300 self.prompt = prompt
301 301 self.skip_hidden = True
302 self.report_skipped = True
302 303
303 304 # list of predicates we use to skip frames
304 305 self._predicates = self.default_predicates
305 306
306 307 def set_colors(self, scheme):
307 308 """Shorthand access to the color table scheme selector method."""
308 309 self.color_scheme_table.set_active_scheme(scheme)
309 310 self.parser.style = scheme
310 311
311 312 def set_trace(self, frame=None):
312 313 if frame is None:
313 314 frame = sys._getframe().f_back
314 315 self.initial_frame = frame
315 316 return super().set_trace(frame)
316 317
317 318 def _hidden_predicate(self, frame):
318 319 """
319 320 Given a frame return whether it it should be hidden or not by IPython.
320 321 """
321 322
322 323 if self._predicates["readonly"]:
323 324 fname = frame.f_code.co_filename
324 325 # we need to check for file existence and interactively define
325 326 # function would otherwise appear as RO.
326 327 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
327 328 return True
328 329
329 330 if self._predicates["tbhide"]:
330 331 if frame in (self.curframe, getattr(self, "initial_frame", None)):
331 332 return False
332 333 else:
333 334 return frame.f_locals.get("__tracebackhide__", False)
334 335
335 336 return False
336 337
337 338 def hidden_frames(self, stack):
338 339 """
339 340 Given an index in the stack return whether it should be skipped.
340 341
341 342 This is used in up/down and where to skip frames.
342 343 """
343 344 # The f_locals dictionary is updated from the actual frame
344 345 # locals whenever the .f_locals accessor is called, so we
345 346 # avoid calling it here to preserve self.curframe_locals.
346 347 # Futhermore, there is no good reason to hide the current frame.
347 348 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
348 349 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
349 350 if ip_start and self._predicates["ipython_internal"]:
350 351 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
351 352 return ip_hide
352 353
353 354 def interaction(self, frame, traceback):
354 355 try:
355 356 OldPdb.interaction(self, frame, traceback)
356 357 except KeyboardInterrupt:
357 358 self.stdout.write("\n" + self.shell.get_exception_only())
358 359
359 360 def precmd(self, line):
360 361 """Perform useful escapes on the command before it is executed."""
361 362
362 363 if line.endswith("??"):
363 364 line = "pinfo2 " + line[:-2]
364 365 elif line.endswith("?"):
365 366 line = "pinfo " + line[:-1]
366 367
367 368 line = super().precmd(line)
368 369
369 370 return line
370 371
371 372 def new_do_frame(self, arg):
372 373 OldPdb.do_frame(self, arg)
373 374
374 375 def new_do_quit(self, arg):
375 376
376 377 if hasattr(self, 'old_all_completions'):
377 378 self.shell.Completer.all_completions = self.old_all_completions
378 379
379 380 return OldPdb.do_quit(self, arg)
380 381
381 382 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
382 383
383 384 def new_do_restart(self, arg):
384 385 """Restart command. In the context of ipython this is exactly the same
385 386 thing as 'quit'."""
386 387 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
387 388 return self.do_quit(arg)
388 389
389 390 def print_stack_trace(self, context=None):
390 391 Colors = self.color_scheme_table.active_colors
391 392 ColorsNormal = Colors.Normal
392 393 if context is None:
393 394 context = self.context
394 395 try:
395 396 context = int(context)
396 397 if context <= 0:
397 398 raise ValueError("Context must be a positive integer")
398 399 except (TypeError, ValueError) as e:
399 400 raise ValueError("Context must be a positive integer") from e
400 401 try:
401 402 skipped = 0
402 403 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
403 404 if hidden and self.skip_hidden:
404 405 skipped += 1
405 406 continue
406 407 if skipped:
407 408 print(
408 409 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
409 410 )
410 411 skipped = 0
411 412 self.print_stack_entry(frame_lineno, context=context)
412 413 if skipped:
413 414 print(
414 415 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
415 416 )
416 417 except KeyboardInterrupt:
417 418 pass
418 419
419 420 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
420 421 context=None):
421 422 if context is None:
422 423 context = self.context
423 424 try:
424 425 context = int(context)
425 426 if context <= 0:
426 427 raise ValueError("Context must be a positive integer")
427 428 except (TypeError, ValueError) as e:
428 429 raise ValueError("Context must be a positive integer") from e
429 430 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
430 431
431 432 # vds: >>
432 433 frame, lineno = frame_lineno
433 434 filename = frame.f_code.co_filename
434 435 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
435 436 # vds: <<
436 437
437 438 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
438 439 if context is None:
439 440 context = self.context
440 441 try:
441 442 context = int(context)
442 443 if context <= 0:
443 444 print("Context must be a positive integer", file=self.stdout)
444 445 except (TypeError, ValueError):
445 446 print("Context must be a positive integer", file=self.stdout)
446 447
447 448 import reprlib
448 449
449 450 ret = []
450 451
451 452 Colors = self.color_scheme_table.active_colors
452 453 ColorsNormal = Colors.Normal
453 454 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
454 455 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
455 456 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
456 457 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
457 458
458 459 frame, lineno = frame_lineno
459 460
460 461 return_value = ''
461 462 if '__return__' in frame.f_locals:
462 463 rv = frame.f_locals['__return__']
463 464 #return_value += '->'
464 465 return_value += reprlib.repr(rv) + '\n'
465 466 ret.append(return_value)
466 467
467 468 #s = filename + '(' + `lineno` + ')'
468 469 filename = self.canonic(frame.f_code.co_filename)
469 470 link = tpl_link % py3compat.cast_unicode(filename)
470 471
471 472 if frame.f_code.co_name:
472 473 func = frame.f_code.co_name
473 474 else:
474 475 func = "<lambda>"
475 476
476 477 call = ''
477 478 if func != '?':
478 479 if '__args__' in frame.f_locals:
479 480 args = reprlib.repr(frame.f_locals['__args__'])
480 481 else:
481 482 args = '()'
482 483 call = tpl_call % (func, args)
483 484
484 485 # The level info should be generated in the same format pdb uses, to
485 486 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
486 487 if frame is self.curframe:
487 488 ret.append('> ')
488 489 else:
489 490 ret.append(" ")
490 491 ret.append("%s(%s)%s\n" % (link, lineno, call))
491 492
492 493 start = lineno - 1 - context//2
493 494 lines = linecache.getlines(filename)
494 495 start = min(start, len(lines) - context)
495 496 start = max(start, 0)
496 497 lines = lines[start : start + context]
497 498
498 499 for i, line in enumerate(lines):
499 500 show_arrow = start + 1 + i == lineno
500 501 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
501 502 ret.append(
502 503 self.__format_line(
503 504 linetpl, filename, start + 1 + i, line, arrow=show_arrow
504 505 )
505 506 )
506 507 return "".join(ret)
507 508
508 509 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
509 510 bp_mark = ""
510 511 bp_mark_color = ""
511 512
512 513 new_line, err = self.parser.format2(line, 'str')
513 514 if not err:
514 515 line = new_line
515 516
516 517 bp = None
517 518 if lineno in self.get_file_breaks(filename):
518 519 bps = self.get_breaks(filename, lineno)
519 520 bp = bps[-1]
520 521
521 522 if bp:
522 523 Colors = self.color_scheme_table.active_colors
523 524 bp_mark = str(bp.number)
524 525 bp_mark_color = Colors.breakpoint_enabled
525 526 if not bp.enabled:
526 527 bp_mark_color = Colors.breakpoint_disabled
527 528
528 529 numbers_width = 7
529 530 if arrow:
530 531 # This is the line with the error
531 532 pad = numbers_width - len(str(lineno)) - len(bp_mark)
532 533 num = '%s%s' % (make_arrow(pad), str(lineno))
533 534 else:
534 535 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
535 536
536 537 return tpl_line % (bp_mark_color + bp_mark, num, line)
537 538
538 539 def print_list_lines(self, filename, first, last):
539 540 """The printing (as opposed to the parsing part of a 'list'
540 541 command."""
541 542 try:
542 543 Colors = self.color_scheme_table.active_colors
543 544 ColorsNormal = Colors.Normal
544 545 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
545 546 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
546 547 src = []
547 548 if filename == "<string>" and hasattr(self, "_exec_filename"):
548 549 filename = self._exec_filename
549 550
550 551 for lineno in range(first, last+1):
551 552 line = linecache.getline(filename, lineno)
552 553 if not line:
553 554 break
554 555
555 556 if lineno == self.curframe.f_lineno:
556 557 line = self.__format_line(
557 558 tpl_line_em, filename, lineno, line, arrow=True
558 559 )
559 560 else:
560 561 line = self.__format_line(
561 562 tpl_line, filename, lineno, line, arrow=False
562 563 )
563 564
564 565 src.append(line)
565 566 self.lineno = lineno
566 567
567 568 print(''.join(src), file=self.stdout)
568 569
569 570 except KeyboardInterrupt:
570 571 pass
571 572
572 573 def do_skip_predicates(self, args):
573 574 """
574 575 Turn on/off individual predicates as to whether a frame should be hidden/skip.
575 576
576 577 The global option to skip (or not) hidden frames is set with skip_hidden
577 578
578 579 To change the value of a predicate
579 580
580 581 skip_predicates key [true|false]
581 582
582 583 Call without arguments to see the current values.
583 584
584 585 To permanently change the value of an option add the corresponding
585 586 command to your ``~/.pdbrc`` file. If you are programmatically using the
586 587 Pdb instance you can also change the ``default_predicates`` class
587 588 attribute.
588 589 """
589 590 if not args.strip():
590 591 print("current predicates:")
591 592 for (p, v) in self._predicates.items():
592 593 print(" ", p, ":", v)
593 594 return
594 595 type_value = args.strip().split(" ")
595 596 if len(type_value) != 2:
596 597 print(
597 598 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
598 599 )
599 600 return
600 601
601 602 type_, value = type_value
602 603 if type_ not in self._predicates:
603 604 print(f"{type_!r} not in {set(self._predicates.keys())}")
604 605 return
605 606 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
606 607 print(
607 608 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
608 609 )
609 610 return
610 611
611 612 self._predicates[type_] = value.lower() in ("true", "yes", "1")
612 613 if not any(self._predicates.values()):
613 614 print(
614 615 "Warning, all predicates set to False, skip_hidden may not have any effects."
615 616 )
616 617
617 618 def do_skip_hidden(self, arg):
618 619 """
619 620 Change whether or not we should skip frames with the
620 621 __tracebackhide__ attribute.
621 622 """
622 623 if not arg.strip():
623 624 print(
624 625 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
625 626 )
626 627 elif arg.strip().lower() in ("true", "yes"):
627 628 self.skip_hidden = True
628 629 elif arg.strip().lower() in ("false", "no"):
629 630 self.skip_hidden = False
630 631 if not any(self._predicates.values()):
631 632 print(
632 633 "Warning, all predicates set to False, skip_hidden may not have any effects."
633 634 )
634 635
635 636 def do_list(self, arg):
636 637 """Print lines of code from the current stack frame
637 638 """
638 639 self.lastcmd = 'list'
639 640 last = None
640 641 if arg:
641 642 try:
642 643 x = eval(arg, {}, {})
643 644 if type(x) == type(()):
644 645 first, last = x
645 646 first = int(first)
646 647 last = int(last)
647 648 if last < first:
648 649 # Assume it's a count
649 650 last = first + last
650 651 else:
651 652 first = max(1, int(x) - 5)
652 653 except:
653 654 print('*** Error in argument:', repr(arg), file=self.stdout)
654 655 return
655 656 elif self.lineno is None:
656 657 first = max(1, self.curframe.f_lineno - 5)
657 658 else:
658 659 first = self.lineno + 1
659 660 if last is None:
660 661 last = first + 10
661 662 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
662 663
663 664 # vds: >>
664 665 lineno = first
665 666 filename = self.curframe.f_code.co_filename
666 667 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
667 668 # vds: <<
668 669
669 670 do_l = do_list
670 671
671 672 def getsourcelines(self, obj):
672 673 lines, lineno = inspect.findsource(obj)
673 674 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
674 675 # must be a module frame: do not try to cut a block out of it
675 676 return lines, 1
676 677 elif inspect.ismodule(obj):
677 678 return lines, 1
678 679 return inspect.getblock(lines[lineno:]), lineno+1
679 680
680 681 def do_longlist(self, arg):
681 682 """Print lines of code from the current stack frame.
682 683
683 684 Shows more lines than 'list' does.
684 685 """
685 686 self.lastcmd = 'longlist'
686 687 try:
687 688 lines, lineno = self.getsourcelines(self.curframe)
688 689 except OSError as err:
689 690 self.error(err)
690 691 return
691 692 last = lineno + len(lines)
692 693 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
693 694 do_ll = do_longlist
694 695
695 696 def do_debug(self, arg):
696 697 """debug code
697 698 Enter a recursive debugger that steps through the code
698 699 argument (which is an arbitrary expression or statement to be
699 700 executed in the current environment).
700 701 """
701 702 trace_function = sys.gettrace()
702 703 sys.settrace(None)
703 704 globals = self.curframe.f_globals
704 705 locals = self.curframe_locals
705 706 p = self.__class__(completekey=self.completekey,
706 707 stdin=self.stdin, stdout=self.stdout)
707 708 p.use_rawinput = self.use_rawinput
708 709 p.prompt = "(%s) " % self.prompt.strip()
709 710 self.message("ENTERING RECURSIVE DEBUGGER")
710 711 sys.call_tracing(p.run, (arg, globals, locals))
711 712 self.message("LEAVING RECURSIVE DEBUGGER")
712 713 sys.settrace(trace_function)
713 714 self.lastcmd = p.lastcmd
714 715
715 716 def do_pdef(self, arg):
716 717 """Print the call signature for any callable object.
717 718
718 719 The debugger interface to %pdef"""
719 720 namespaces = [
720 721 ("Locals", self.curframe_locals),
721 722 ("Globals", self.curframe.f_globals),
722 723 ]
723 724 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
724 725
725 726 def do_pdoc(self, arg):
726 727 """Print the docstring for an object.
727 728
728 729 The debugger interface to %pdoc."""
729 730 namespaces = [
730 731 ("Locals", self.curframe_locals),
731 732 ("Globals", self.curframe.f_globals),
732 733 ]
733 734 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
734 735
735 736 def do_pfile(self, arg):
736 737 """Print (or run through pager) the file where an object is defined.
737 738
738 739 The debugger interface to %pfile.
739 740 """
740 741 namespaces = [
741 742 ("Locals", self.curframe_locals),
742 743 ("Globals", self.curframe.f_globals),
743 744 ]
744 745 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
745 746
746 747 def do_pinfo(self, arg):
747 748 """Provide detailed information about an object.
748 749
749 750 The debugger interface to %pinfo, i.e., obj?."""
750 751 namespaces = [
751 752 ("Locals", self.curframe_locals),
752 753 ("Globals", self.curframe.f_globals),
753 754 ]
754 755 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
755 756
756 757 def do_pinfo2(self, arg):
757 758 """Provide extra detailed information about an object.
758 759
759 760 The debugger interface to %pinfo2, i.e., obj??."""
760 761 namespaces = [
761 762 ("Locals", self.curframe_locals),
762 763 ("Globals", self.curframe.f_globals),
763 764 ]
764 765 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
765 766
766 767 def do_psource(self, arg):
767 768 """Print (or run through pager) the source code for an object."""
768 769 namespaces = [
769 770 ("Locals", self.curframe_locals),
770 771 ("Globals", self.curframe.f_globals),
771 772 ]
772 773 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
773 774
774 775 def do_where(self, arg):
775 776 """w(here)
776 777 Print a stack trace, with the most recent frame at the bottom.
777 778 An arrow indicates the "current frame", which determines the
778 779 context of most commands. 'bt' is an alias for this command.
779 780
780 781 Take a number as argument as an (optional) number of context line to
781 782 print"""
782 783 if arg:
783 784 try:
784 785 context = int(arg)
785 786 except ValueError as err:
786 787 self.error(err)
787 788 return
788 789 self.print_stack_trace(context)
789 790 else:
790 791 self.print_stack_trace()
791 792
792 793 do_w = do_where
793 794
794 795 def stop_here(self, frame):
795 796 hidden = False
796 797 if self.skip_hidden:
797 798 hidden = self._hidden_predicate(frame)
798 799 if hidden:
799 Colors = self.color_scheme_table.active_colors
800 ColorsNormal = Colors.Normal
801 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
802
800 if self.report_skipped:
801 Colors = self.color_scheme_table.active_colors
802 ColorsNormal = Colors.Normal
803 print(
804 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
805 )
803 806 return super().stop_here(frame)
804 807
805 808 def do_up(self, arg):
806 809 """u(p) [count]
807 810 Move the current frame count (default one) levels up in the
808 811 stack trace (to an older frame).
809 812
810 813 Will skip hidden frames.
811 814 """
812 815 # modified version of upstream that skips
813 816 # frames with __tracebackhide__
814 817 if self.curindex == 0:
815 818 self.error("Oldest frame")
816 819 return
817 820 try:
818 821 count = int(arg or 1)
819 822 except ValueError:
820 823 self.error("Invalid frame count (%s)" % arg)
821 824 return
822 825 skipped = 0
823 826 if count < 0:
824 827 _newframe = 0
825 828 else:
826 829 counter = 0
827 830 hidden_frames = self.hidden_frames(self.stack)
828 831 for i in range(self.curindex - 1, -1, -1):
829 832 if hidden_frames[i] and self.skip_hidden:
830 833 skipped += 1
831 834 continue
832 835 counter += 1
833 836 if counter >= count:
834 837 break
835 838 else:
836 839 # if no break occured.
837 840 self.error(
838 841 "all frames above hidden, use `skip_hidden False` to get get into those."
839 842 )
840 843 return
841 844
842 845 Colors = self.color_scheme_table.active_colors
843 846 ColorsNormal = Colors.Normal
844 847 _newframe = i
845 848 self._select_frame(_newframe)
846 849 if skipped:
847 850 print(
848 851 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
849 852 )
850 853
851 854 def do_down(self, arg):
852 855 """d(own) [count]
853 856 Move the current frame count (default one) levels down in the
854 857 stack trace (to a newer frame).
855 858
856 859 Will skip hidden frames.
857 860 """
858 861 if self.curindex + 1 == len(self.stack):
859 862 self.error("Newest frame")
860 863 return
861 864 try:
862 865 count = int(arg or 1)
863 866 except ValueError:
864 867 self.error("Invalid frame count (%s)" % arg)
865 868 return
866 869 if count < 0:
867 870 _newframe = len(self.stack) - 1
868 871 else:
869 872 counter = 0
870 873 skipped = 0
871 874 hidden_frames = self.hidden_frames(self.stack)
872 875 for i in range(self.curindex + 1, len(self.stack)):
873 876 if hidden_frames[i] and self.skip_hidden:
874 877 skipped += 1
875 878 continue
876 879 counter += 1
877 880 if counter >= count:
878 881 break
879 882 else:
880 883 self.error(
881 884 "all frames bellow hidden, use `skip_hidden False` to get get into those."
882 885 )
883 886 return
884 887
885 888 Colors = self.color_scheme_table.active_colors
886 889 ColorsNormal = Colors.Normal
887 890 if skipped:
888 891 print(
889 892 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
890 893 )
891 894 _newframe = i
892 895
893 896 self._select_frame(_newframe)
894 897
895 898 do_d = do_down
896 899 do_u = do_up
897 900
898 901 def do_context(self, context):
899 902 """context number_of_lines
900 903 Set the number of lines of source code to show when displaying
901 904 stacktrace information.
902 905 """
903 906 try:
904 907 new_context = int(context)
905 908 if new_context <= 0:
906 909 raise ValueError()
907 910 self.context = new_context
908 911 except ValueError:
909 912 self.error("The 'context' command requires a positive integer argument.")
910 913
911 914
912 915 class InterruptiblePdb(Pdb):
913 916 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
914 917
915 918 def cmdloop(self):
916 919 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
917 920 try:
918 921 return OldPdb.cmdloop(self)
919 922 except KeyboardInterrupt:
920 923 self.stop_here = lambda frame: False
921 924 self.do_quit("")
922 925 sys.settrace(None)
923 926 self.quitting = False
924 927 raise
925 928
926 929 def _cmdloop(self):
927 930 while True:
928 931 try:
929 932 # keyboard interrupts allow for an easy way to cancel
930 933 # the current command, so allow them during interactive input
931 934 self.allow_kbdint = True
932 935 self.cmdloop()
933 936 self.allow_kbdint = False
934 937 break
935 938 except KeyboardInterrupt:
936 939 self.message('--KeyboardInterrupt--')
937 940 raise
938 941
939 942
940 943 def set_trace(frame=None):
941 944 """
942 945 Start debugging from `frame`.
943 946
944 947 If frame is not specified, debugging starts from caller's frame.
945 948 """
946 949 Pdb().set_trace(frame or sys._getframe().f_back)
General Comments 0
You need to be logged in to leave comments. Login now