Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/IPython/core/debugger.py: 15%
554 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:05 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:05 +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 contextlib import contextmanager
112from IPython.utils import PyColorize
113from IPython.utils import coloransi, py3compat
114from IPython.core.excolors import exception_colors
116# skip module docstests
117__skip_doctest__ = True
119prompt = 'ipdb> '
121# We have to check this directly from sys.argv, config struct not yet available
122from pdb import Pdb as OldPdb
124# Allow the set_trace code to operate outside of an ipython instance, even if
125# it does so with some limitations. The rest of this support is implemented in
126# the Tracer constructor.
128DEBUGGERSKIP = "__debuggerskip__"
131# this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676
132# on lower python versions, we backported the feature.
133CHAIN_EXCEPTIONS = sys.version_info < (3, 13)
136def make_arrow(pad):
137 """generate the leading arrow in front of traceback or debugger"""
138 if pad >= 2:
139 return '-'*(pad-2) + '> '
140 elif pad == 1:
141 return '>'
142 return ''
145def BdbQuit_excepthook(et, ev, tb, excepthook=None):
146 """Exception hook which handles `BdbQuit` exceptions.
148 All other exceptions are processed using the `excepthook`
149 parameter.
150 """
151 raise ValueError(
152 "`BdbQuit_excepthook` is deprecated since version 5.1",
153 )
156def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
157 raise ValueError(
158 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
159 DeprecationWarning, stacklevel=2)
162RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
165def strip_indentation(multiline_string):
166 return RGX_EXTRA_INDENT.sub('', multiline_string)
169def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
170 """Make new_fn have old_fn's doc string. This is particularly useful
171 for the ``do_...`` commands that hook into the help system.
172 Adapted from from a comp.lang.python posting
173 by Duncan Booth."""
174 def wrapper(*args, **kw):
175 return new_fn(*args, **kw)
176 if old_fn.__doc__:
177 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
178 return wrapper
181class Pdb(OldPdb):
182 """Modified Pdb class, does not load readline.
184 for a standalone version that uses prompt_toolkit, see
185 `IPython.terminal.debugger.TerminalPdb` and
186 `IPython.terminal.debugger.set_trace()`
189 This debugger can hide and skip frames that are tagged according to some predicates.
190 See the `skip_predicates` commands.
192 """
194 if CHAIN_EXCEPTIONS:
195 MAX_CHAINED_EXCEPTION_DEPTH = 999
197 default_predicates = {
198 "tbhide": True,
199 "readonly": False,
200 "ipython_internal": True,
201 "debuggerskip": True,
202 }
204 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
205 """Create a new IPython debugger.
207 Parameters
208 ----------
209 completekey : default None
210 Passed to pdb.Pdb.
211 stdin : default None
212 Passed to pdb.Pdb.
213 stdout : default None
214 Passed to pdb.Pdb.
215 context : int
216 Number of lines of source code context to show when
217 displaying stacktrace information.
218 **kwargs
219 Passed to pdb.Pdb.
221 Notes
222 -----
223 The possibilities are python version dependent, see the python
224 docs for more info.
225 """
227 # Parent constructor:
228 try:
229 self.context = int(context)
230 if self.context <= 0:
231 raise ValueError("Context must be a positive integer")
232 except (TypeError, ValueError) as e:
233 raise ValueError("Context must be a positive integer") from e
235 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
236 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
238 # IPython changes...
239 self.shell = get_ipython()
241 if self.shell is None:
242 save_main = sys.modules['__main__']
243 # No IPython instance running, we must create one
244 from IPython.terminal.interactiveshell import \
245 TerminalInteractiveShell
246 self.shell = TerminalInteractiveShell.instance()
247 # needed by any code which calls __import__("__main__") after
248 # the debugger was entered. See also #9941.
249 sys.modules["__main__"] = save_main
252 color_scheme = self.shell.colors
254 self.aliases = {}
256 # Create color table: we copy the default one from the traceback
257 # module and add a few attributes needed for debugging
258 self.color_scheme_table = exception_colors()
260 # shorthands
261 C = coloransi.TermColors
262 cst = self.color_scheme_table
264 cst['NoColor'].colors.prompt = C.NoColor
265 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
266 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
268 cst['Linux'].colors.prompt = C.Green
269 cst['Linux'].colors.breakpoint_enabled = C.LightRed
270 cst['Linux'].colors.breakpoint_disabled = C.Red
272 cst['LightBG'].colors.prompt = C.Blue
273 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
274 cst['LightBG'].colors.breakpoint_disabled = C.Red
276 cst['Neutral'].colors.prompt = C.Blue
277 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
278 cst['Neutral'].colors.breakpoint_disabled = C.Red
280 # Add a python parser so we can syntax highlight source while
281 # debugging.
282 self.parser = PyColorize.Parser(style=color_scheme)
283 self.set_colors(color_scheme)
285 # Set the prompt - the default prompt is '(Pdb)'
286 self.prompt = prompt
287 self.skip_hidden = True
288 self.report_skipped = True
290 # list of predicates we use to skip frames
291 self._predicates = self.default_predicates
293 if CHAIN_EXCEPTIONS:
294 self._chained_exceptions = tuple()
295 self._chained_exception_index = 0
297 #
298 def set_colors(self, scheme):
299 """Shorthand access to the color table scheme selector method."""
300 self.color_scheme_table.set_active_scheme(scheme)
301 self.parser.style = scheme
303 def set_trace(self, frame=None):
304 if frame is None:
305 frame = sys._getframe().f_back
306 self.initial_frame = frame
307 return super().set_trace(frame)
309 def _hidden_predicate(self, frame):
310 """
311 Given a frame return whether it it should be hidden or not by IPython.
312 """
314 if self._predicates["readonly"]:
315 fname = frame.f_code.co_filename
316 # we need to check for file existence and interactively define
317 # function would otherwise appear as RO.
318 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
319 return True
321 if self._predicates["tbhide"]:
322 if frame in (self.curframe, getattr(self, "initial_frame", None)):
323 return False
324 frame_locals = self._get_frame_locals(frame)
325 if "__tracebackhide__" not in frame_locals:
326 return False
327 return frame_locals["__tracebackhide__"]
328 return False
330 def hidden_frames(self, stack):
331 """
332 Given an index in the stack return whether it should be skipped.
334 This is used in up/down and where to skip frames.
335 """
336 # The f_locals dictionary is updated from the actual frame
337 # locals whenever the .f_locals accessor is called, so we
338 # avoid calling it here to preserve self.curframe_locals.
339 # Furthermore, there is no good reason to hide the current frame.
340 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
341 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
342 if ip_start and self._predicates["ipython_internal"]:
343 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
344 return ip_hide
346 if CHAIN_EXCEPTIONS:
348 def _get_tb_and_exceptions(self, tb_or_exc):
349 """
350 Given a tracecack or an exception, return a tuple of chained exceptions
351 and current traceback to inspect.
352 This will deal with selecting the right ``__cause__`` or ``__context__``
353 as well as handling cycles, and return a flattened list of exceptions we
354 can jump to with do_exceptions.
355 """
356 _exceptions = []
357 if isinstance(tb_or_exc, BaseException):
358 traceback, current = tb_or_exc.__traceback__, tb_or_exc
360 while current is not None:
361 if current in _exceptions:
362 break
363 _exceptions.append(current)
364 if current.__cause__ is not None:
365 current = current.__cause__
366 elif (
367 current.__context__ is not None
368 and not current.__suppress_context__
369 ):
370 current = current.__context__
372 if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH:
373 self.message(
374 f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}"
375 " chained exceptions found, not all exceptions"
376 "will be browsable with `exceptions`."
377 )
378 break
379 else:
380 traceback = tb_or_exc
381 return tuple(reversed(_exceptions)), traceback
383 @contextmanager
384 def _hold_exceptions(self, exceptions):
385 """
386 Context manager to ensure proper cleaning of exceptions references
387 When given a chained exception instead of a traceback,
388 pdb may hold references to many objects which may leak memory.
389 We use this context manager to make sure everything is properly cleaned
390 """
391 try:
392 self._chained_exceptions = exceptions
393 self._chained_exception_index = len(exceptions) - 1
394 yield
395 finally:
396 # we can't put those in forget as otherwise they would
397 # be cleared on exception change
398 self._chained_exceptions = tuple()
399 self._chained_exception_index = 0
401 def do_exceptions(self, arg):
402 """exceptions [number]
403 List or change current exception in an exception chain.
404 Without arguments, list all the current exception in the exception
405 chain. Exceptions will be numbered, with the current exception indicated
406 with an arrow.
407 If given an integer as argument, switch to the exception at that index.
408 """
409 if not self._chained_exceptions:
410 self.message(
411 "Did not find chained exceptions. To move between"
412 " exceptions, pdb/post_mortem must be given an exception"
413 " object rather than a traceback."
414 )
415 return
416 if not arg:
417 for ix, exc in enumerate(self._chained_exceptions):
418 prompt = ">" if ix == self._chained_exception_index else " "
419 rep = repr(exc)
420 if len(rep) > 80:
421 rep = rep[:77] + "..."
422 indicator = (
423 " -"
424 if self._chained_exceptions[ix].__traceback__ is None
425 else f"{ix:>3}"
426 )
427 self.message(f"{prompt} {indicator} {rep}")
428 else:
429 try:
430 number = int(arg)
431 except ValueError:
432 self.error("Argument must be an integer")
433 return
434 if 0 <= number < len(self._chained_exceptions):
435 if self._chained_exceptions[number].__traceback__ is None:
436 self.error(
437 "This exception does not have a traceback, cannot jump to it"
438 )
439 return
441 self._chained_exception_index = number
442 self.setup(None, self._chained_exceptions[number].__traceback__)
443 self.print_stack_entry(self.stack[self.curindex])
444 else:
445 self.error("No exception with that number")
447 def interaction(self, frame, tb_or_exc):
448 try:
449 if CHAIN_EXCEPTIONS:
450 # this context manager is part of interaction in 3.13
451 _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
452 if isinstance(tb_or_exc, BaseException):
453 assert tb is not None, "main exception must have a traceback"
454 with self._hold_exceptions(_chained_exceptions):
455 OldPdb.interaction(self, frame, tb)
456 else:
457 OldPdb.interaction(self, frame, traceback)
459 except KeyboardInterrupt:
460 self.stdout.write("\n" + self.shell.get_exception_only())
462 def precmd(self, line):
463 """Perform useful escapes on the command before it is executed."""
465 if line.endswith("??"):
466 line = "pinfo2 " + line[:-2]
467 elif line.endswith("?"):
468 line = "pinfo " + line[:-1]
470 line = super().precmd(line)
472 return line
474 def new_do_frame(self, arg):
475 OldPdb.do_frame(self, arg)
477 def new_do_quit(self, arg):
479 if hasattr(self, 'old_all_completions'):
480 self.shell.Completer.all_completions = self.old_all_completions
482 return OldPdb.do_quit(self, arg)
484 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
486 def new_do_restart(self, arg):
487 """Restart command. In the context of ipython this is exactly the same
488 thing as 'quit'."""
489 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
490 return self.do_quit(arg)
492 def print_stack_trace(self, context=None):
493 Colors = self.color_scheme_table.active_colors
494 ColorsNormal = Colors.Normal
495 if context is None:
496 context = self.context
497 try:
498 context = int(context)
499 if context <= 0:
500 raise ValueError("Context must be a positive integer")
501 except (TypeError, ValueError) as e:
502 raise ValueError("Context must be a positive integer") from e
503 try:
504 skipped = 0
505 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
506 if hidden and self.skip_hidden:
507 skipped += 1
508 continue
509 if skipped:
510 print(
511 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
512 )
513 skipped = 0
514 self.print_stack_entry(frame_lineno, context=context)
515 if skipped:
516 print(
517 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
518 )
519 except KeyboardInterrupt:
520 pass
522 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
523 context=None):
524 if context is None:
525 context = self.context
526 try:
527 context = int(context)
528 if context <= 0:
529 raise ValueError("Context must be a positive integer")
530 except (TypeError, ValueError) as e:
531 raise ValueError("Context must be a positive integer") from e
532 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
534 # vds: >>
535 frame, lineno = frame_lineno
536 filename = frame.f_code.co_filename
537 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
538 # vds: <<
540 def _get_frame_locals(self, frame):
541 """ "
542 Accessing f_local of current frame reset the namespace, so we want to avoid
543 that or the following can happen
545 ipdb> foo
546 "old"
547 ipdb> foo = "new"
548 ipdb> foo
549 "new"
550 ipdb> where
551 ipdb> foo
552 "old"
554 So if frame is self.current_frame we instead return self.curframe_locals
556 """
557 if frame is self.curframe:
558 return self.curframe_locals
559 else:
560 return frame.f_locals
562 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
563 if context is None:
564 context = self.context
565 try:
566 context = int(context)
567 if context <= 0:
568 print("Context must be a positive integer", file=self.stdout)
569 except (TypeError, ValueError):
570 print("Context must be a positive integer", file=self.stdout)
572 import reprlib
574 ret = []
576 Colors = self.color_scheme_table.active_colors
577 ColorsNormal = Colors.Normal
578 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
579 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
580 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
581 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
583 frame, lineno = frame_lineno
585 return_value = ''
586 loc_frame = self._get_frame_locals(frame)
587 if "__return__" in loc_frame:
588 rv = loc_frame["__return__"]
589 # return_value += '->'
590 return_value += reprlib.repr(rv) + "\n"
591 ret.append(return_value)
593 #s = filename + '(' + `lineno` + ')'
594 filename = self.canonic(frame.f_code.co_filename)
595 link = tpl_link % py3compat.cast_unicode(filename)
597 if frame.f_code.co_name:
598 func = frame.f_code.co_name
599 else:
600 func = "<lambda>"
602 call = ""
603 if func != "?":
604 if "__args__" in loc_frame:
605 args = reprlib.repr(loc_frame["__args__"])
606 else:
607 args = '()'
608 call = tpl_call % (func, args)
610 # The level info should be generated in the same format pdb uses, to
611 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
612 if frame is self.curframe:
613 ret.append('> ')
614 else:
615 ret.append(" ")
616 ret.append("%s(%s)%s\n" % (link, lineno, call))
618 start = lineno - 1 - context//2
619 lines = linecache.getlines(filename)
620 start = min(start, len(lines) - context)
621 start = max(start, 0)
622 lines = lines[start : start + context]
624 for i, line in enumerate(lines):
625 show_arrow = start + 1 + i == lineno
626 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
627 ret.append(
628 self.__format_line(
629 linetpl, filename, start + 1 + i, line, arrow=show_arrow
630 )
631 )
632 return "".join(ret)
634 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
635 bp_mark = ""
636 bp_mark_color = ""
638 new_line, err = self.parser.format2(line, 'str')
639 if not err:
640 line = new_line
642 bp = None
643 if lineno in self.get_file_breaks(filename):
644 bps = self.get_breaks(filename, lineno)
645 bp = bps[-1]
647 if bp:
648 Colors = self.color_scheme_table.active_colors
649 bp_mark = str(bp.number)
650 bp_mark_color = Colors.breakpoint_enabled
651 if not bp.enabled:
652 bp_mark_color = Colors.breakpoint_disabled
654 numbers_width = 7
655 if arrow:
656 # This is the line with the error
657 pad = numbers_width - len(str(lineno)) - len(bp_mark)
658 num = '%s%s' % (make_arrow(pad), str(lineno))
659 else:
660 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
662 return tpl_line % (bp_mark_color + bp_mark, num, line)
664 def print_list_lines(self, filename, first, last):
665 """The printing (as opposed to the parsing part of a 'list'
666 command."""
667 try:
668 Colors = self.color_scheme_table.active_colors
669 ColorsNormal = Colors.Normal
670 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
671 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
672 src = []
673 if filename == "<string>" and hasattr(self, "_exec_filename"):
674 filename = self._exec_filename
676 for lineno in range(first, last+1):
677 line = linecache.getline(filename, lineno)
678 if not line:
679 break
681 if lineno == self.curframe.f_lineno:
682 line = self.__format_line(
683 tpl_line_em, filename, lineno, line, arrow=True
684 )
685 else:
686 line = self.__format_line(
687 tpl_line, filename, lineno, line, arrow=False
688 )
690 src.append(line)
691 self.lineno = lineno
693 print(''.join(src), file=self.stdout)
695 except KeyboardInterrupt:
696 pass
698 def do_skip_predicates(self, args):
699 """
700 Turn on/off individual predicates as to whether a frame should be hidden/skip.
702 The global option to skip (or not) hidden frames is set with skip_hidden
704 To change the value of a predicate
706 skip_predicates key [true|false]
708 Call without arguments to see the current values.
710 To permanently change the value of an option add the corresponding
711 command to your ``~/.pdbrc`` file. If you are programmatically using the
712 Pdb instance you can also change the ``default_predicates`` class
713 attribute.
714 """
715 if not args.strip():
716 print("current predicates:")
717 for p, v in self._predicates.items():
718 print(" ", p, ":", v)
719 return
720 type_value = args.strip().split(" ")
721 if len(type_value) != 2:
722 print(
723 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
724 )
725 return
727 type_, value = type_value
728 if type_ not in self._predicates:
729 print(f"{type_!r} not in {set(self._predicates.keys())}")
730 return
731 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
732 print(
733 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
734 )
735 return
737 self._predicates[type_] = value.lower() in ("true", "yes", "1")
738 if not any(self._predicates.values()):
739 print(
740 "Warning, all predicates set to False, skip_hidden may not have any effects."
741 )
743 def do_skip_hidden(self, arg):
744 """
745 Change whether or not we should skip frames with the
746 __tracebackhide__ attribute.
747 """
748 if not arg.strip():
749 print(
750 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
751 )
752 elif arg.strip().lower() in ("true", "yes"):
753 self.skip_hidden = True
754 elif arg.strip().lower() in ("false", "no"):
755 self.skip_hidden = False
756 if not any(self._predicates.values()):
757 print(
758 "Warning, all predicates set to False, skip_hidden may not have any effects."
759 )
761 def do_list(self, arg):
762 """Print lines of code from the current stack frame
763 """
764 self.lastcmd = 'list'
765 last = None
766 if arg and arg != ".":
767 try:
768 x = eval(arg, {}, {})
769 if type(x) == type(()):
770 first, last = x
771 first = int(first)
772 last = int(last)
773 if last < first:
774 # Assume it's a count
775 last = first + last
776 else:
777 first = max(1, int(x) - 5)
778 except:
779 print('*** Error in argument:', repr(arg), file=self.stdout)
780 return
781 elif self.lineno is None or arg == ".":
782 first = max(1, self.curframe.f_lineno - 5)
783 else:
784 first = self.lineno + 1
785 if last is None:
786 last = first + 10
787 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
789 # vds: >>
790 lineno = first
791 filename = self.curframe.f_code.co_filename
792 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
793 # vds: <<
795 do_l = do_list
797 def getsourcelines(self, obj):
798 lines, lineno = inspect.findsource(obj)
799 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
800 # must be a module frame: do not try to cut a block out of it
801 return lines, 1
802 elif inspect.ismodule(obj):
803 return lines, 1
804 return inspect.getblock(lines[lineno:]), lineno+1
806 def do_longlist(self, arg):
807 """Print lines of code from the current stack frame.
809 Shows more lines than 'list' does.
810 """
811 self.lastcmd = 'longlist'
812 try:
813 lines, lineno = self.getsourcelines(self.curframe)
814 except OSError as err:
815 self.error(err)
816 return
817 last = lineno + len(lines)
818 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
819 do_ll = do_longlist
821 def do_debug(self, arg):
822 """debug code
823 Enter a recursive debugger that steps through the code
824 argument (which is an arbitrary expression or statement to be
825 executed in the current environment).
826 """
827 trace_function = sys.gettrace()
828 sys.settrace(None)
829 globals = self.curframe.f_globals
830 locals = self.curframe_locals
831 p = self.__class__(completekey=self.completekey,
832 stdin=self.stdin, stdout=self.stdout)
833 p.use_rawinput = self.use_rawinput
834 p.prompt = "(%s) " % self.prompt.strip()
835 self.message("ENTERING RECURSIVE DEBUGGER")
836 sys.call_tracing(p.run, (arg, globals, locals))
837 self.message("LEAVING RECURSIVE DEBUGGER")
838 sys.settrace(trace_function)
839 self.lastcmd = p.lastcmd
841 def do_pdef(self, arg):
842 """Print the call signature for any callable object.
844 The debugger interface to %pdef"""
845 namespaces = [
846 ("Locals", self.curframe_locals),
847 ("Globals", self.curframe.f_globals),
848 ]
849 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
851 def do_pdoc(self, arg):
852 """Print the docstring for an object.
854 The debugger interface to %pdoc."""
855 namespaces = [
856 ("Locals", self.curframe_locals),
857 ("Globals", self.curframe.f_globals),
858 ]
859 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
861 def do_pfile(self, arg):
862 """Print (or run through pager) the file where an object is defined.
864 The debugger interface to %pfile.
865 """
866 namespaces = [
867 ("Locals", self.curframe_locals),
868 ("Globals", self.curframe.f_globals),
869 ]
870 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
872 def do_pinfo(self, arg):
873 """Provide detailed information about an object.
875 The debugger interface to %pinfo, i.e., obj?."""
876 namespaces = [
877 ("Locals", self.curframe_locals),
878 ("Globals", self.curframe.f_globals),
879 ]
880 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
882 def do_pinfo2(self, arg):
883 """Provide extra detailed information about an object.
885 The debugger interface to %pinfo2, i.e., obj??."""
886 namespaces = [
887 ("Locals", self.curframe_locals),
888 ("Globals", self.curframe.f_globals),
889 ]
890 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
892 def do_psource(self, arg):
893 """Print (or run through pager) the source code for an object."""
894 namespaces = [
895 ("Locals", self.curframe_locals),
896 ("Globals", self.curframe.f_globals),
897 ]
898 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
900 def do_where(self, arg):
901 """w(here)
902 Print a stack trace, with the most recent frame at the bottom.
903 An arrow indicates the "current frame", which determines the
904 context of most commands. 'bt' is an alias for this command.
906 Take a number as argument as an (optional) number of context line to
907 print"""
908 if arg:
909 try:
910 context = int(arg)
911 except ValueError as err:
912 self.error(err)
913 return
914 self.print_stack_trace(context)
915 else:
916 self.print_stack_trace()
918 do_w = do_where
920 def break_anywhere(self, frame):
921 """
922 _stop_in_decorator_internals is overly restrictive, as we may still want
923 to trace function calls, so we need to also update break_anywhere so
924 that is we don't `stop_here`, because of debugger skip, we may still
925 stop at any point inside the function
927 """
929 sup = super().break_anywhere(frame)
930 if sup:
931 return sup
932 if self._predicates["debuggerskip"]:
933 if DEBUGGERSKIP in frame.f_code.co_varnames:
934 return True
935 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
936 return True
937 return False
939 def _is_in_decorator_internal_and_should_skip(self, frame):
940 """
941 Utility to tell us whether we are in a decorator internal and should stop.
943 """
945 # if we are disabled don't skip
946 if not self._predicates["debuggerskip"]:
947 return False
949 # if frame is tagged, skip by default.
950 if DEBUGGERSKIP in frame.f_code.co_varnames:
951 return True
953 # if one of the parent frame value set to True skip as well.
955 cframe = frame
956 while getattr(cframe, "f_back", None):
957 cframe = cframe.f_back
958 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
959 return True
961 return False
963 def stop_here(self, frame):
964 if self._is_in_decorator_internal_and_should_skip(frame) is True:
965 return False
967 hidden = False
968 if self.skip_hidden:
969 hidden = self._hidden_predicate(frame)
970 if hidden:
971 if self.report_skipped:
972 Colors = self.color_scheme_table.active_colors
973 ColorsNormal = Colors.Normal
974 print(
975 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
976 )
977 return super().stop_here(frame)
979 def do_up(self, arg):
980 """u(p) [count]
981 Move the current frame count (default one) levels up in the
982 stack trace (to an older frame).
984 Will skip hidden frames.
985 """
986 # modified version of upstream that skips
987 # frames with __tracebackhide__
988 if self.curindex == 0:
989 self.error("Oldest frame")
990 return
991 try:
992 count = int(arg or 1)
993 except ValueError:
994 self.error("Invalid frame count (%s)" % arg)
995 return
996 skipped = 0
997 if count < 0:
998 _newframe = 0
999 else:
1000 counter = 0
1001 hidden_frames = self.hidden_frames(self.stack)
1002 for i in range(self.curindex - 1, -1, -1):
1003 if hidden_frames[i] and self.skip_hidden:
1004 skipped += 1
1005 continue
1006 counter += 1
1007 if counter >= count:
1008 break
1009 else:
1010 # if no break occurred.
1011 self.error(
1012 "all frames above hidden, use `skip_hidden False` to get get into those."
1013 )
1014 return
1016 Colors = self.color_scheme_table.active_colors
1017 ColorsNormal = Colors.Normal
1018 _newframe = i
1019 self._select_frame(_newframe)
1020 if skipped:
1021 print(
1022 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1023 )
1025 def do_down(self, arg):
1026 """d(own) [count]
1027 Move the current frame count (default one) levels down in the
1028 stack trace (to a newer frame).
1030 Will skip hidden frames.
1031 """
1032 if self.curindex + 1 == len(self.stack):
1033 self.error("Newest frame")
1034 return
1035 try:
1036 count = int(arg or 1)
1037 except ValueError:
1038 self.error("Invalid frame count (%s)" % arg)
1039 return
1040 if count < 0:
1041 _newframe = len(self.stack) - 1
1042 else:
1043 counter = 0
1044 skipped = 0
1045 hidden_frames = self.hidden_frames(self.stack)
1046 for i in range(self.curindex + 1, len(self.stack)):
1047 if hidden_frames[i] and self.skip_hidden:
1048 skipped += 1
1049 continue
1050 counter += 1
1051 if counter >= count:
1052 break
1053 else:
1054 self.error(
1055 "all frames below hidden, use `skip_hidden False` to get get into those."
1056 )
1057 return
1059 Colors = self.color_scheme_table.active_colors
1060 ColorsNormal = Colors.Normal
1061 if skipped:
1062 print(
1063 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1064 )
1065 _newframe = i
1067 self._select_frame(_newframe)
1069 do_d = do_down
1070 do_u = do_up
1072 def do_context(self, context):
1073 """context number_of_lines
1074 Set the number of lines of source code to show when displaying
1075 stacktrace information.
1076 """
1077 try:
1078 new_context = int(context)
1079 if new_context <= 0:
1080 raise ValueError()
1081 self.context = new_context
1082 except ValueError:
1083 self.error("The 'context' command requires a positive integer argument.")
1086class InterruptiblePdb(Pdb):
1087 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1089 def cmdloop(self, intro=None):
1090 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1091 try:
1092 return OldPdb.cmdloop(self, intro=intro)
1093 except KeyboardInterrupt:
1094 self.stop_here = lambda frame: False
1095 self.do_quit("")
1096 sys.settrace(None)
1097 self.quitting = False
1098 raise
1100 def _cmdloop(self):
1101 while True:
1102 try:
1103 # keyboard interrupts allow for an easy way to cancel
1104 # the current command, so allow them during interactive input
1105 self.allow_kbdint = True
1106 self.cmdloop()
1107 self.allow_kbdint = False
1108 break
1109 except KeyboardInterrupt:
1110 self.message('--KeyboardInterrupt--')
1111 raise
1114def set_trace(frame=None):
1115 """
1116 Start debugging from `frame`.
1118 If frame is not specified, debugging starts from caller's frame.
1119 """
1120 Pdb().set_trace(frame or sys._getframe().f_back)