Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/IPython/core/debugger.py: 15%
490 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1# -*- coding: utf-8 -*-
2"""
3Pdb debugger class.
6This is an extension to PDB which adds a number of new features.
7Note that there is also the `IPython.terminal.debugger` class which provides UI
8improvements.
10We also strongly recommend to use this via the `ipdb` package, which provides
11extra configuration options.
13Among other things, this subclass of PDB:
14 - supports many IPython magics like pdef/psource
15 - hide frames in tracebacks based on `__tracebackhide__`
16 - allows to skip frames based on `__debuggerskip__`
18The skipping and hiding frames are configurable via the `skip_predicates`
19command.
21By default, frames from readonly files will be hidden, frames containing
22``__tracebackhide__=True`` will be hidden.
24Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
25frames value of ``__debuggerskip__`` is ``True`` will be skipped.
27 >>> def helpers_helper():
28 ... pass
29 ...
30 ... def helper_1():
31 ... print("don't step in me")
32 ... helpers_helpers() # will be stepped over unless breakpoint set.
33 ...
34 ...
35 ... def helper_2():
36 ... print("in me neither")
37 ...
39One can define a decorator that wraps a function between the two helpers:
41 >>> def pdb_skipped_decorator(function):
42 ...
43 ...
44 ... def wrapped_fn(*args, **kwargs):
45 ... __debuggerskip__ = True
46 ... helper_1()
47 ... __debuggerskip__ = False
48 ... result = function(*args, **kwargs)
49 ... __debuggerskip__ = True
50 ... helper_2()
51 ... # setting __debuggerskip__ to False again is not necessary
52 ... return result
53 ...
54 ... return wrapped_fn
56When decorating a function, ipdb will directly step into ``bar()`` by
57default:
59 >>> @foo_decorator
60 ... def bar(x, y):
61 ... return x * y
64You can toggle the behavior with
66 ipdb> skip_predicates debuggerskip false
68or configure it in your ``.pdbrc``
72License
73-------
75Modified from the standard pdb.Pdb class to avoid including readline, so that
76the command line completion of other programs which include this isn't
77damaged.
79In the future, this class will be expanded with improvements over the standard
80pdb.
82The original code in this file is mainly lifted out of cmd.py in Python 2.2,
83with minor changes. Licensing should therefore be under the standard Python
84terms. For details on the PSF (Python Software Foundation) standard license,
85see:
87https://docs.python.org/2/license.html
90All the changes since then are under the same license as IPython.
92"""
94#*****************************************************************************
95#
96# This file is licensed under the PSF license.
97#
98# Copyright (C) 2001 Python Software Foundation, www.python.org
99# Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
100#
101#
102#*****************************************************************************
104import inspect
105import linecache
106import sys
107import re
108import os
110from IPython import get_ipython
111from IPython.utils import PyColorize
112from IPython.utils import coloransi, py3compat
113from IPython.core.excolors import exception_colors
115# skip module docstests
116__skip_doctest__ = True
118prompt = 'ipdb> '
120# We have to check this directly from sys.argv, config struct not yet available
121from pdb import Pdb as OldPdb
123# Allow the set_trace code to operate outside of an ipython instance, even if
124# it does so with some limitations. The rest of this support is implemented in
125# the Tracer constructor.
127DEBUGGERSKIP = "__debuggerskip__"
130def make_arrow(pad):
131 """generate the leading arrow in front of traceback or debugger"""
132 if pad >= 2:
133 return '-'*(pad-2) + '> '
134 elif pad == 1:
135 return '>'
136 return ''
139def BdbQuit_excepthook(et, ev, tb, excepthook=None):
140 """Exception hook which handles `BdbQuit` exceptions.
142 All other exceptions are processed using the `excepthook`
143 parameter.
144 """
145 raise ValueError(
146 "`BdbQuit_excepthook` is deprecated since version 5.1",
147 )
150def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
151 raise ValueError(
152 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
153 DeprecationWarning, stacklevel=2)
156RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
159def strip_indentation(multiline_string):
160 return RGX_EXTRA_INDENT.sub('', multiline_string)
163def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
164 """Make new_fn have old_fn's doc string. This is particularly useful
165 for the ``do_...`` commands that hook into the help system.
166 Adapted from from a comp.lang.python posting
167 by Duncan Booth."""
168 def wrapper(*args, **kw):
169 return new_fn(*args, **kw)
170 if old_fn.__doc__:
171 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
172 return wrapper
175class Pdb(OldPdb):
176 """Modified Pdb class, does not load readline.
178 for a standalone version that uses prompt_toolkit, see
179 `IPython.terminal.debugger.TerminalPdb` and
180 `IPython.terminal.debugger.set_trace()`
183 This debugger can hide and skip frames that are tagged according to some predicates.
184 See the `skip_predicates` commands.
186 """
188 default_predicates = {
189 "tbhide": True,
190 "readonly": False,
191 "ipython_internal": True,
192 "debuggerskip": True,
193 }
195 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
196 """Create a new IPython debugger.
198 Parameters
199 ----------
200 completekey : default None
201 Passed to pdb.Pdb.
202 stdin : default None
203 Passed to pdb.Pdb.
204 stdout : default None
205 Passed to pdb.Pdb.
206 context : int
207 Number of lines of source code context to show when
208 displaying stacktrace information.
209 **kwargs
210 Passed to pdb.Pdb.
212 Notes
213 -----
214 The possibilities are python version dependent, see the python
215 docs for more info.
216 """
218 # Parent constructor:
219 try:
220 self.context = int(context)
221 if self.context <= 0:
222 raise ValueError("Context must be a positive integer")
223 except (TypeError, ValueError) as e:
224 raise ValueError("Context must be a positive integer") from e
226 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
227 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
229 # IPython changes...
230 self.shell = get_ipython()
232 if self.shell is None:
233 save_main = sys.modules['__main__']
234 # No IPython instance running, we must create one
235 from IPython.terminal.interactiveshell import \
236 TerminalInteractiveShell
237 self.shell = TerminalInteractiveShell.instance()
238 # needed by any code which calls __import__("__main__") after
239 # the debugger was entered. See also #9941.
240 sys.modules["__main__"] = save_main
243 color_scheme = self.shell.colors
245 self.aliases = {}
247 # Create color table: we copy the default one from the traceback
248 # module and add a few attributes needed for debugging
249 self.color_scheme_table = exception_colors()
251 # shorthands
252 C = coloransi.TermColors
253 cst = self.color_scheme_table
255 cst['NoColor'].colors.prompt = C.NoColor
256 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
257 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
259 cst['Linux'].colors.prompt = C.Green
260 cst['Linux'].colors.breakpoint_enabled = C.LightRed
261 cst['Linux'].colors.breakpoint_disabled = C.Red
263 cst['LightBG'].colors.prompt = C.Blue
264 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
265 cst['LightBG'].colors.breakpoint_disabled = C.Red
267 cst['Neutral'].colors.prompt = C.Blue
268 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
269 cst['Neutral'].colors.breakpoint_disabled = C.Red
271 # Add a python parser so we can syntax highlight source while
272 # debugging.
273 self.parser = PyColorize.Parser(style=color_scheme)
274 self.set_colors(color_scheme)
276 # Set the prompt - the default prompt is '(Pdb)'
277 self.prompt = prompt
278 self.skip_hidden = True
279 self.report_skipped = True
281 # list of predicates we use to skip frames
282 self._predicates = self.default_predicates
284 #
285 def set_colors(self, scheme):
286 """Shorthand access to the color table scheme selector method."""
287 self.color_scheme_table.set_active_scheme(scheme)
288 self.parser.style = scheme
290 def set_trace(self, frame=None):
291 if frame is None:
292 frame = sys._getframe().f_back
293 self.initial_frame = frame
294 return super().set_trace(frame)
296 def _hidden_predicate(self, frame):
297 """
298 Given a frame return whether it it should be hidden or not by IPython.
299 """
301 if self._predicates["readonly"]:
302 fname = frame.f_code.co_filename
303 # we need to check for file existence and interactively define
304 # function would otherwise appear as RO.
305 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
306 return True
308 if self._predicates["tbhide"]:
309 if frame in (self.curframe, getattr(self, "initial_frame", None)):
310 return False
311 frame_locals = self._get_frame_locals(frame)
312 if "__tracebackhide__" not in frame_locals:
313 return False
314 return frame_locals["__tracebackhide__"]
315 return False
317 def hidden_frames(self, stack):
318 """
319 Given an index in the stack return whether it should be skipped.
321 This is used in up/down and where to skip frames.
322 """
323 # The f_locals dictionary is updated from the actual frame
324 # locals whenever the .f_locals accessor is called, so we
325 # avoid calling it here to preserve self.curframe_locals.
326 # Furthermore, there is no good reason to hide the current frame.
327 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
328 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
329 if ip_start and self._predicates["ipython_internal"]:
330 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
331 return ip_hide
333 def interaction(self, frame, traceback):
334 try:
335 OldPdb.interaction(self, frame, traceback)
336 except KeyboardInterrupt:
337 self.stdout.write("\n" + self.shell.get_exception_only())
339 def precmd(self, line):
340 """Perform useful escapes on the command before it is executed."""
342 if line.endswith("??"):
343 line = "pinfo2 " + line[:-2]
344 elif line.endswith("?"):
345 line = "pinfo " + line[:-1]
347 line = super().precmd(line)
349 return line
351 def new_do_frame(self, arg):
352 OldPdb.do_frame(self, arg)
354 def new_do_quit(self, arg):
356 if hasattr(self, 'old_all_completions'):
357 self.shell.Completer.all_completions = self.old_all_completions
359 return OldPdb.do_quit(self, arg)
361 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
363 def new_do_restart(self, arg):
364 """Restart command. In the context of ipython this is exactly the same
365 thing as 'quit'."""
366 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
367 return self.do_quit(arg)
369 def print_stack_trace(self, context=None):
370 Colors = self.color_scheme_table.active_colors
371 ColorsNormal = Colors.Normal
372 if context is None:
373 context = self.context
374 try:
375 context = int(context)
376 if context <= 0:
377 raise ValueError("Context must be a positive integer")
378 except (TypeError, ValueError) as e:
379 raise ValueError("Context must be a positive integer") from e
380 try:
381 skipped = 0
382 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
383 if hidden and self.skip_hidden:
384 skipped += 1
385 continue
386 if skipped:
387 print(
388 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
389 )
390 skipped = 0
391 self.print_stack_entry(frame_lineno, context=context)
392 if skipped:
393 print(
394 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
395 )
396 except KeyboardInterrupt:
397 pass
399 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
400 context=None):
401 if context is None:
402 context = self.context
403 try:
404 context = int(context)
405 if context <= 0:
406 raise ValueError("Context must be a positive integer")
407 except (TypeError, ValueError) as e:
408 raise ValueError("Context must be a positive integer") from e
409 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
411 # vds: >>
412 frame, lineno = frame_lineno
413 filename = frame.f_code.co_filename
414 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
415 # vds: <<
417 def _get_frame_locals(self, frame):
418 """ "
419 Accessing f_local of current frame reset the namespace, so we want to avoid
420 that or the following can happen
422 ipdb> foo
423 "old"
424 ipdb> foo = "new"
425 ipdb> foo
426 "new"
427 ipdb> where
428 ipdb> foo
429 "old"
431 So if frame is self.current_frame we instead return self.curframe_locals
433 """
434 if frame is self.curframe:
435 return self.curframe_locals
436 else:
437 return frame.f_locals
439 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
440 if context is None:
441 context = self.context
442 try:
443 context = int(context)
444 if context <= 0:
445 print("Context must be a positive integer", file=self.stdout)
446 except (TypeError, ValueError):
447 print("Context must be a positive integer", file=self.stdout)
449 import reprlib
451 ret = []
453 Colors = self.color_scheme_table.active_colors
454 ColorsNormal = Colors.Normal
455 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
456 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
457 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
458 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
460 frame, lineno = frame_lineno
462 return_value = ''
463 loc_frame = self._get_frame_locals(frame)
464 if "__return__" in loc_frame:
465 rv = loc_frame["__return__"]
466 # return_value += '->'
467 return_value += reprlib.repr(rv) + "\n"
468 ret.append(return_value)
470 #s = filename + '(' + `lineno` + ')'
471 filename = self.canonic(frame.f_code.co_filename)
472 link = tpl_link % py3compat.cast_unicode(filename)
474 if frame.f_code.co_name:
475 func = frame.f_code.co_name
476 else:
477 func = "<lambda>"
479 call = ""
480 if func != "?":
481 if "__args__" in loc_frame:
482 args = reprlib.repr(loc_frame["__args__"])
483 else:
484 args = '()'
485 call = tpl_call % (func, args)
487 # The level info should be generated in the same format pdb uses, to
488 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
489 if frame is self.curframe:
490 ret.append('> ')
491 else:
492 ret.append(" ")
493 ret.append("%s(%s)%s\n" % (link, lineno, call))
495 start = lineno - 1 - context//2
496 lines = linecache.getlines(filename)
497 start = min(start, len(lines) - context)
498 start = max(start, 0)
499 lines = lines[start : start + context]
501 for i, line in enumerate(lines):
502 show_arrow = start + 1 + i == lineno
503 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
504 ret.append(
505 self.__format_line(
506 linetpl, filename, start + 1 + i, line, arrow=show_arrow
507 )
508 )
509 return "".join(ret)
511 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
512 bp_mark = ""
513 bp_mark_color = ""
515 new_line, err = self.parser.format2(line, 'str')
516 if not err:
517 line = new_line
519 bp = None
520 if lineno in self.get_file_breaks(filename):
521 bps = self.get_breaks(filename, lineno)
522 bp = bps[-1]
524 if bp:
525 Colors = self.color_scheme_table.active_colors
526 bp_mark = str(bp.number)
527 bp_mark_color = Colors.breakpoint_enabled
528 if not bp.enabled:
529 bp_mark_color = Colors.breakpoint_disabled
531 numbers_width = 7
532 if arrow:
533 # This is the line with the error
534 pad = numbers_width - len(str(lineno)) - len(bp_mark)
535 num = '%s%s' % (make_arrow(pad), str(lineno))
536 else:
537 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
539 return tpl_line % (bp_mark_color + bp_mark, num, line)
541 def print_list_lines(self, filename, first, last):
542 """The printing (as opposed to the parsing part of a 'list'
543 command."""
544 try:
545 Colors = self.color_scheme_table.active_colors
546 ColorsNormal = Colors.Normal
547 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
548 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
549 src = []
550 if filename == "<string>" and hasattr(self, "_exec_filename"):
551 filename = self._exec_filename
553 for lineno in range(first, last+1):
554 line = linecache.getline(filename, lineno)
555 if not line:
556 break
558 if lineno == self.curframe.f_lineno:
559 line = self.__format_line(
560 tpl_line_em, filename, lineno, line, arrow=True
561 )
562 else:
563 line = self.__format_line(
564 tpl_line, filename, lineno, line, arrow=False
565 )
567 src.append(line)
568 self.lineno = lineno
570 print(''.join(src), file=self.stdout)
572 except KeyboardInterrupt:
573 pass
575 def do_skip_predicates(self, args):
576 """
577 Turn on/off individual predicates as to whether a frame should be hidden/skip.
579 The global option to skip (or not) hidden frames is set with skip_hidden
581 To change the value of a predicate
583 skip_predicates key [true|false]
585 Call without arguments to see the current values.
587 To permanently change the value of an option add the corresponding
588 command to your ``~/.pdbrc`` file. If you are programmatically using the
589 Pdb instance you can also change the ``default_predicates`` class
590 attribute.
591 """
592 if not args.strip():
593 print("current predicates:")
594 for p, v in self._predicates.items():
595 print(" ", p, ":", v)
596 return
597 type_value = args.strip().split(" ")
598 if len(type_value) != 2:
599 print(
600 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
601 )
602 return
604 type_, value = type_value
605 if type_ not in self._predicates:
606 print(f"{type_!r} not in {set(self._predicates.keys())}")
607 return
608 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
609 print(
610 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
611 )
612 return
614 self._predicates[type_] = value.lower() in ("true", "yes", "1")
615 if not any(self._predicates.values()):
616 print(
617 "Warning, all predicates set to False, skip_hidden may not have any effects."
618 )
620 def do_skip_hidden(self, arg):
621 """
622 Change whether or not we should skip frames with the
623 __tracebackhide__ attribute.
624 """
625 if not arg.strip():
626 print(
627 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
628 )
629 elif arg.strip().lower() in ("true", "yes"):
630 self.skip_hidden = True
631 elif arg.strip().lower() in ("false", "no"):
632 self.skip_hidden = False
633 if not any(self._predicates.values()):
634 print(
635 "Warning, all predicates set to False, skip_hidden may not have any effects."
636 )
638 def do_list(self, arg):
639 """Print lines of code from the current stack frame
640 """
641 self.lastcmd = 'list'
642 last = None
643 if arg:
644 try:
645 x = eval(arg, {}, {})
646 if type(x) == type(()):
647 first, last = x
648 first = int(first)
649 last = int(last)
650 if last < first:
651 # Assume it's a count
652 last = first + last
653 else:
654 first = max(1, int(x) - 5)
655 except:
656 print('*** Error in argument:', repr(arg), file=self.stdout)
657 return
658 elif self.lineno is None:
659 first = max(1, self.curframe.f_lineno - 5)
660 else:
661 first = self.lineno + 1
662 if last is None:
663 last = first + 10
664 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
666 # vds: >>
667 lineno = first
668 filename = self.curframe.f_code.co_filename
669 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
670 # vds: <<
672 do_l = do_list
674 def getsourcelines(self, obj):
675 lines, lineno = inspect.findsource(obj)
676 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
677 # must be a module frame: do not try to cut a block out of it
678 return lines, 1
679 elif inspect.ismodule(obj):
680 return lines, 1
681 return inspect.getblock(lines[lineno:]), lineno+1
683 def do_longlist(self, arg):
684 """Print lines of code from the current stack frame.
686 Shows more lines than 'list' does.
687 """
688 self.lastcmd = 'longlist'
689 try:
690 lines, lineno = self.getsourcelines(self.curframe)
691 except OSError as err:
692 self.error(err)
693 return
694 last = lineno + len(lines)
695 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
696 do_ll = do_longlist
698 def do_debug(self, arg):
699 """debug code
700 Enter a recursive debugger that steps through the code
701 argument (which is an arbitrary expression or statement to be
702 executed in the current environment).
703 """
704 trace_function = sys.gettrace()
705 sys.settrace(None)
706 globals = self.curframe.f_globals
707 locals = self.curframe_locals
708 p = self.__class__(completekey=self.completekey,
709 stdin=self.stdin, stdout=self.stdout)
710 p.use_rawinput = self.use_rawinput
711 p.prompt = "(%s) " % self.prompt.strip()
712 self.message("ENTERING RECURSIVE DEBUGGER")
713 sys.call_tracing(p.run, (arg, globals, locals))
714 self.message("LEAVING RECURSIVE DEBUGGER")
715 sys.settrace(trace_function)
716 self.lastcmd = p.lastcmd
718 def do_pdef(self, arg):
719 """Print the call signature for any callable object.
721 The debugger interface to %pdef"""
722 namespaces = [
723 ("Locals", self.curframe_locals),
724 ("Globals", self.curframe.f_globals),
725 ]
726 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
728 def do_pdoc(self, arg):
729 """Print the docstring for an object.
731 The debugger interface to %pdoc."""
732 namespaces = [
733 ("Locals", self.curframe_locals),
734 ("Globals", self.curframe.f_globals),
735 ]
736 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
738 def do_pfile(self, arg):
739 """Print (or run through pager) the file where an object is defined.
741 The debugger interface to %pfile.
742 """
743 namespaces = [
744 ("Locals", self.curframe_locals),
745 ("Globals", self.curframe.f_globals),
746 ]
747 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
749 def do_pinfo(self, arg):
750 """Provide detailed information about an object.
752 The debugger interface to %pinfo, i.e., obj?."""
753 namespaces = [
754 ("Locals", self.curframe_locals),
755 ("Globals", self.curframe.f_globals),
756 ]
757 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
759 def do_pinfo2(self, arg):
760 """Provide extra detailed information about an object.
762 The debugger interface to %pinfo2, i.e., obj??."""
763 namespaces = [
764 ("Locals", self.curframe_locals),
765 ("Globals", self.curframe.f_globals),
766 ]
767 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
769 def do_psource(self, arg):
770 """Print (or run through pager) the source code for an object."""
771 namespaces = [
772 ("Locals", self.curframe_locals),
773 ("Globals", self.curframe.f_globals),
774 ]
775 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
777 def do_where(self, arg):
778 """w(here)
779 Print a stack trace, with the most recent frame at the bottom.
780 An arrow indicates the "current frame", which determines the
781 context of most commands. 'bt' is an alias for this command.
783 Take a number as argument as an (optional) number of context line to
784 print"""
785 if arg:
786 try:
787 context = int(arg)
788 except ValueError as err:
789 self.error(err)
790 return
791 self.print_stack_trace(context)
792 else:
793 self.print_stack_trace()
795 do_w = do_where
797 def break_anywhere(self, frame):
798 """
799 _stop_in_decorator_internals is overly restrictive, as we may still want
800 to trace function calls, so we need to also update break_anywhere so
801 that is we don't `stop_here`, because of debugger skip, we may still
802 stop at any point inside the function
804 """
806 sup = super().break_anywhere(frame)
807 if sup:
808 return sup
809 if self._predicates["debuggerskip"]:
810 if DEBUGGERSKIP in frame.f_code.co_varnames:
811 return True
812 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
813 return True
814 return False
816 def _is_in_decorator_internal_and_should_skip(self, frame):
817 """
818 Utility to tell us whether we are in a decorator internal and should stop.
820 """
822 # if we are disabled don't skip
823 if not self._predicates["debuggerskip"]:
824 return False
826 # if frame is tagged, skip by default.
827 if DEBUGGERSKIP in frame.f_code.co_varnames:
828 return True
830 # if one of the parent frame value set to True skip as well.
832 cframe = frame
833 while getattr(cframe, "f_back", None):
834 cframe = cframe.f_back
835 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
836 return True
838 return False
840 def stop_here(self, frame):
841 if self._is_in_decorator_internal_and_should_skip(frame) is True:
842 return False
844 hidden = False
845 if self.skip_hidden:
846 hidden = self._hidden_predicate(frame)
847 if hidden:
848 if self.report_skipped:
849 Colors = self.color_scheme_table.active_colors
850 ColorsNormal = Colors.Normal
851 print(
852 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
853 )
854 return super().stop_here(frame)
856 def do_up(self, arg):
857 """u(p) [count]
858 Move the current frame count (default one) levels up in the
859 stack trace (to an older frame).
861 Will skip hidden frames.
862 """
863 # modified version of upstream that skips
864 # frames with __tracebackhide__
865 if self.curindex == 0:
866 self.error("Oldest frame")
867 return
868 try:
869 count = int(arg or 1)
870 except ValueError:
871 self.error("Invalid frame count (%s)" % arg)
872 return
873 skipped = 0
874 if count < 0:
875 _newframe = 0
876 else:
877 counter = 0
878 hidden_frames = self.hidden_frames(self.stack)
879 for i in range(self.curindex - 1, -1, -1):
880 if hidden_frames[i] and self.skip_hidden:
881 skipped += 1
882 continue
883 counter += 1
884 if counter >= count:
885 break
886 else:
887 # if no break occurred.
888 self.error(
889 "all frames above hidden, use `skip_hidden False` to get get into those."
890 )
891 return
893 Colors = self.color_scheme_table.active_colors
894 ColorsNormal = Colors.Normal
895 _newframe = i
896 self._select_frame(_newframe)
897 if skipped:
898 print(
899 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
900 )
902 def do_down(self, arg):
903 """d(own) [count]
904 Move the current frame count (default one) levels down in the
905 stack trace (to a newer frame).
907 Will skip hidden frames.
908 """
909 if self.curindex + 1 == len(self.stack):
910 self.error("Newest frame")
911 return
912 try:
913 count = int(arg or 1)
914 except ValueError:
915 self.error("Invalid frame count (%s)" % arg)
916 return
917 if count < 0:
918 _newframe = len(self.stack) - 1
919 else:
920 counter = 0
921 skipped = 0
922 hidden_frames = self.hidden_frames(self.stack)
923 for i in range(self.curindex + 1, len(self.stack)):
924 if hidden_frames[i] and self.skip_hidden:
925 skipped += 1
926 continue
927 counter += 1
928 if counter >= count:
929 break
930 else:
931 self.error(
932 "all frames below hidden, use `skip_hidden False` to get get into those."
933 )
934 return
936 Colors = self.color_scheme_table.active_colors
937 ColorsNormal = Colors.Normal
938 if skipped:
939 print(
940 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
941 )
942 _newframe = i
944 self._select_frame(_newframe)
946 do_d = do_down
947 do_u = do_up
949 def do_context(self, context):
950 """context number_of_lines
951 Set the number of lines of source code to show when displaying
952 stacktrace information.
953 """
954 try:
955 new_context = int(context)
956 if new_context <= 0:
957 raise ValueError()
958 self.context = new_context
959 except ValueError:
960 self.error("The 'context' command requires a positive integer argument.")
963class InterruptiblePdb(Pdb):
964 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
966 def cmdloop(self, intro=None):
967 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
968 try:
969 return OldPdb.cmdloop(self, intro=intro)
970 except KeyboardInterrupt:
971 self.stop_here = lambda frame: False
972 self.do_quit("")
973 sys.settrace(None)
974 self.quitting = False
975 raise
977 def _cmdloop(self):
978 while True:
979 try:
980 # keyboard interrupts allow for an easy way to cancel
981 # the current command, so allow them during interactive input
982 self.allow_kbdint = True
983 self.cmdloop()
984 self.allow_kbdint = False
985 break
986 except KeyboardInterrupt:
987 self.message('--KeyboardInterrupt--')
988 raise
991def set_trace(frame=None):
992 """
993 Start debugging from `frame`.
995 If frame is not specified, debugging starts from caller's frame.
996 """
997 Pdb().set_trace(frame or sys._getframe().f_back)