1# -*- coding: utf-8 -*-
2"""
3Pdb debugger class.
4
5
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.
9
10We also strongly recommend to use this via the `ipdb` package, which provides
11extra configuration options.
12
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__`
17
18The skipping and hiding frames are configurable via the `skip_predicates`
19command.
20
21By default, frames from readonly files will be hidden, frames containing
22``__tracebackhide__=True`` will be hidden.
23
24Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
25frames value of ``__debuggerskip__`` is ``True`` will be skipped.
26
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    ...
38
39One can define a decorator that wraps a function between the two helpers:
40
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
55
56When decorating a function, ipdb will directly step into ``bar()`` by
57default:
58
59    >>> @foo_decorator
60    ... def bar(x, y):
61    ...     return x * y
62
63
64You can toggle the behavior with
65
66    ipdb> skip_predicates debuggerskip false
67
68or configure it in your ``.pdbrc``
69
70
71
72Licencse
73--------
74
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.
78
79In the future, this class will be expanded with improvements over the standard
80pdb.
81
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:
86
87https://docs.python.org/2/license.html
88
89
90All the changes since then are under the same license as IPython.
91
92"""
93
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#*****************************************************************************
103
104import bdb
105import functools
106import inspect
107import linecache
108import sys
109import warnings
110import re
111import os
112
113from IPython import get_ipython
114from IPython.utils import PyColorize
115from IPython.utils import coloransi, py3compat
116from IPython.core.excolors import exception_colors
117from IPython.testing.skipdoctest import skip_doctest
118
119
120prompt = 'ipdb> '
121
122#We have to check this directly from sys.argv, config struct not yet available
123from pdb import Pdb as OldPdb
124
125# Allow the set_trace code to operate outside of an ipython instance, even if
126# it does so with some limitations.  The rest of this support is implemented in
127# the Tracer constructor.
128
129DEBUGGERSKIP = "__debuggerskip__"
130
131
132def make_arrow(pad):
133    """generate the leading arrow in front of traceback or debugger"""
134    if pad >= 2:
135        return '-'*(pad-2) + '> '
136    elif pad == 1:
137        return '>'
138    return ''
139
140
141def BdbQuit_excepthook(et, ev, tb, excepthook=None):
142    """Exception hook which handles `BdbQuit` exceptions.
143
144    All other exceptions are processed using the `excepthook`
145    parameter.
146    """
147    warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
148                  DeprecationWarning, stacklevel=2)
149    if et==bdb.BdbQuit:
150        print('Exiting Debugger.')
151    elif excepthook is not None:
152        excepthook(et, ev, tb)
153    else:
154        # Backwards compatibility. Raise deprecation warning?
155        BdbQuit_excepthook.excepthook_ori(et,ev,tb)
156
157
158def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
159    warnings.warn(
160        "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
161        DeprecationWarning, stacklevel=2)
162    print('Exiting Debugger.')
163
164
165class Tracer(object):
166    """
167    DEPRECATED
168
169    Class for local debugging, similar to pdb.set_trace.
170
171    Instances of this class, when called, behave like pdb.set_trace, but
172    providing IPython's enhanced capabilities.
173
174    This is implemented as a class which must be initialized in your own code
175    and not as a standalone function because we need to detect at runtime
176    whether IPython is already active or not.  That detection is done in the
177    constructor, ensuring that this code plays nicely with a running IPython,
178    while functioning acceptably (though with limitations) if outside of it.
179    """
180
181    @skip_doctest
182    def __init__(self, colors=None):
183        """
184        DEPRECATED
185
186        Create a local debugger instance.
187
188        Parameters
189        ----------
190
191        colors : str, optional
192            The name of the color scheme to use, it must be one of IPython's
193            valid color schemes.  If not given, the function will default to
194            the current IPython scheme when running inside IPython, and to
195            'NoColor' otherwise.
196
197        Examples
198        --------
199        ::
200
201            from IPython.core.debugger import Tracer; debug_here = Tracer()
202
203        Later in your code::
204
205            debug_here()  # -> will open up the debugger at that point.
206
207        Once the debugger activates, you can use all of its regular commands to
208        step through code, set breakpoints, etc.  See the pdb documentation
209        from the Python standard library for usage details.
210        """
211        warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
212                      "`IPython.core.debugger.Pdb.set_trace()`",
213                      DeprecationWarning, stacklevel=2)
214
215        ip = get_ipython()
216        if ip is None:
217            # Outside of ipython, we set our own exception hook manually
218            sys.excepthook = functools.partial(BdbQuit_excepthook,
219                                               excepthook=sys.excepthook)
220            def_colors = 'NoColor'
221        else:
222            # In ipython, we use its custom exception handler mechanism
223            def_colors = ip.colors
224            ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
225
226        if colors is None:
227            colors = def_colors
228
229        # The stdlib debugger internally uses a modified repr from the `repr`
230        # module, that limits the length of printed strings to a hardcoded
231        # limit of 30 characters.  That much trimming is too aggressive, let's
232        # at least raise that limit to 80 chars, which should be enough for
233        # most interactive uses.
234        try:
235            from reprlib import aRepr
236            aRepr.maxstring = 80
237        except:
238            # This is only a user-facing convenience, so any error we encounter
239            # here can be warned about but can be otherwise ignored.  These
240            # printouts will tell us about problems if this API changes
241            import traceback
242            traceback.print_exc()
243
244        self.debugger = Pdb(colors)
245
246    def __call__(self):
247        """Starts an interactive debugger at the point where called.
248
249        This is similar to the pdb.set_trace() function from the std lib, but
250        using IPython's enhanced debugger."""
251
252        self.debugger.set_trace(sys._getframe().f_back)
253
254
255RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
256
257
258def strip_indentation(multiline_string):
259    return RGX_EXTRA_INDENT.sub('', multiline_string)
260
261
262def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
263    """Make new_fn have old_fn's doc string. This is particularly useful
264    for the ``do_...`` commands that hook into the help system.
265    Adapted from from a comp.lang.python posting
266    by Duncan Booth."""
267    def wrapper(*args, **kw):
268        return new_fn(*args, **kw)
269    if old_fn.__doc__:
270        wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
271    return wrapper
272
273
274class Pdb(OldPdb):
275    """Modified Pdb class, does not load readline.
276
277    for a standalone version that uses prompt_toolkit, see
278    `IPython.terminal.debugger.TerminalPdb` and
279    `IPython.terminal.debugger.set_trace()`
280
281
282    This debugger can hide and skip frames that are tagged according to some predicates.
283    See the `skip_predicates` commands.
284
285    """
286
287    default_predicates = {
288        "tbhide": True,
289        "readonly": False,
290        "ipython_internal": True,
291        "debuggerskip": True,
292    }
293
294    def __init__(self, color_scheme=None, completekey=None,
295                 stdin=None, stdout=None, context=5, **kwargs):
296        """Create a new IPython debugger.
297
298        Parameters
299        ----------
300        color_scheme : default None
301            Deprecated, do not use.
302        completekey : default None
303            Passed to pdb.Pdb.
304        stdin : default None
305            Passed to pdb.Pdb.
306        stdout : default None
307            Passed to pdb.Pdb.
308        context : int
309            Number of lines of source code context to show when
310            displaying stacktrace information.
311        **kwargs
312            Passed to pdb.Pdb.
313
314        Notes
315        -----
316        The possibilities are python version dependent, see the python
317        docs for more info.
318        """
319
320        # Parent constructor:
321        try:
322            self.context = int(context)
323            if self.context <= 0:
324                raise ValueError("Context must be a positive integer")
325        except (TypeError, ValueError):
326                raise ValueError("Context must be a positive integer")
327
328        # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
329        OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
330
331        # IPython changes...
332        self.shell = get_ipython()
333
334        if self.shell is None:
335            save_main = sys.modules['__main__']
336            # No IPython instance running, we must create one
337            from IPython.terminal.interactiveshell import \
338                TerminalInteractiveShell
339            self.shell = TerminalInteractiveShell.instance()
340            # needed by any code which calls __import__("__main__") after
341            # the debugger was entered. See also #9941.
342            sys.modules['__main__'] = save_main
343
344        if color_scheme is not None:
345            warnings.warn(
346                "The `color_scheme` argument is deprecated since version 5.1",
347                DeprecationWarning, stacklevel=2)
348        else:
349            color_scheme = self.shell.colors
350
351        self.aliases = {}
352
353        # Create color table: we copy the default one from the traceback
354        # module and add a few attributes needed for debugging
355        self.color_scheme_table = exception_colors()
356
357        # shorthands
358        C = coloransi.TermColors
359        cst = self.color_scheme_table
360
361        cst['NoColor'].colors.prompt = C.NoColor
362        cst['NoColor'].colors.breakpoint_enabled = C.NoColor
363        cst['NoColor'].colors.breakpoint_disabled = C.NoColor
364
365        cst['Linux'].colors.prompt = C.Green
366        cst['Linux'].colors.breakpoint_enabled = C.LightRed
367        cst['Linux'].colors.breakpoint_disabled = C.Red
368
369        cst['LightBG'].colors.prompt = C.Blue
370        cst['LightBG'].colors.breakpoint_enabled = C.LightRed
371        cst['LightBG'].colors.breakpoint_disabled = C.Red
372
373        cst['Neutral'].colors.prompt = C.Blue
374        cst['Neutral'].colors.breakpoint_enabled = C.LightRed
375        cst['Neutral'].colors.breakpoint_disabled = C.Red
376
377
378        # Add a python parser so we can syntax highlight source while
379        # debugging.
380        self.parser = PyColorize.Parser(style=color_scheme)
381        self.set_colors(color_scheme)
382
383        # Set the prompt - the default prompt is '(Pdb)'
384        self.prompt = prompt
385        self.skip_hidden = True
386        self.report_skipped = True
387
388        # list of predicates we use to skip frames
389        self._predicates = self.default_predicates
390
391    #
392    def set_colors(self, scheme):
393        """Shorthand access to the color table scheme selector method."""
394        self.color_scheme_table.set_active_scheme(scheme)
395        self.parser.style = scheme
396
397    def set_trace(self, frame=None):
398        if frame is None:
399            frame = sys._getframe().f_back
400        self.initial_frame = frame
401        return super().set_trace(frame)
402
403    def _hidden_predicate(self, frame):
404        """
405        Given a frame return whether it it should be hidden or not by IPython.
406        """
407
408        if self._predicates["readonly"]:
409            fname = frame.f_code.co_filename
410            # we need to check for file existence and interactively define
411            # function would otherwise appear as RO.
412            if os.path.isfile(fname) and not os.access(fname, os.W_OK):
413                return True
414
415        if self._predicates["tbhide"]:
416            if frame in (self.curframe, getattr(self, "initial_frame", None)):
417                return False
418            else:
419                return self._get_frame_locals(frame).get("__tracebackhide__", False)
420
421        return False
422
423    def hidden_frames(self, stack):
424        """
425        Given an index in the stack return wether it should be skipped.
426
427        This is used in up/down and where to skip frames.
428        """
429        # The f_locals dictionary is updated from the actual frame
430        # locals whenever the .f_locals accessor is called, so we
431        # avoid calling it here to preserve self.curframe_locals.
432        # Futhermore, there is no good reason to hide the current frame.
433        ip_hide = [self._hidden_predicate(s[0]) for s in stack]
434        ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
435        if ip_start and self._predicates["ipython_internal"]:
436            ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
437        return ip_hide
438
439    def interaction(self, frame, traceback):
440        try:
441            OldPdb.interaction(self, frame, traceback)
442        except KeyboardInterrupt:
443            self.stdout.write("\n" + self.shell.get_exception_only())
444
445    def new_do_frame(self, arg):
446        OldPdb.do_frame(self, arg)
447
448    def new_do_quit(self, arg):
449
450        if hasattr(self, 'old_all_completions'):
451            self.shell.Completer.all_completions=self.old_all_completions
452
453        return OldPdb.do_quit(self, arg)
454
455    do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
456
457    def new_do_restart(self, arg):
458        """Restart command. In the context of ipython this is exactly the same
459        thing as 'quit'."""
460        self.msg("Restart doesn't make sense here. Using 'quit' instead.")
461        return self.do_quit(arg)
462
463    def print_stack_trace(self, context=None):
464        Colors = self.color_scheme_table.active_colors
465        ColorsNormal = Colors.Normal
466        if context is None:
467            context = self.context
468        try:
469            context=int(context)
470            if context <= 0:
471                raise ValueError("Context must be a positive integer")
472        except (TypeError, ValueError):
473                raise ValueError("Context must be a positive integer")
474        try:
475            skipped = 0
476            for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
477                if hidden and self.skip_hidden:
478                    skipped += 1
479                    continue
480                if skipped:
481                    print(
482                        f"{Colors.excName}    [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
483                    )
484                    skipped = 0
485                self.print_stack_entry(frame_lineno, context=context)
486            if skipped:
487                print(
488                    f"{Colors.excName}    [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
489                )
490        except KeyboardInterrupt:
491            pass
492
493    def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
494                          context=None):
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):
502                raise ValueError("Context must be a positive integer")
503        print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
504
505        # vds: >>
506        frame, lineno = frame_lineno
507        filename = frame.f_code.co_filename
508        self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
509        # vds: <<
510
511    def _get_frame_locals(self, frame):
512        """ "
513        Acessing f_local of current frame reset the namespace, so we want to avoid
514        that or the following can happend
515
516        ipdb> foo
517        "old"
518        ipdb> foo = "new"
519        ipdb> foo
520        "new"
521        ipdb> where
522        ipdb> foo
523        "old"
524
525        So if frame is self.current_frame we instead return self.curframe_locals
526
527        """
528        if frame is self.curframe:
529            return self.curframe_locals
530        else:
531            return frame.f_locals
532
533    def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
534        if context is None:
535            context = self.context
536        try:
537            context=int(context)
538            if context <= 0:
539                print("Context must be a positive integer", file=self.stdout)
540        except (TypeError, ValueError):
541                print("Context must be a positive integer", file=self.stdout)
542        try:
543            import reprlib  # Py 3
544        except ImportError:
545            import repr as reprlib  # Py 2
546
547        ret = []
548
549        Colors = self.color_scheme_table.active_colors
550        ColorsNormal = Colors.Normal
551        tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
552        tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
553        tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
554        tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
555                                            ColorsNormal)
556
557        frame, lineno = frame_lineno
558
559        return_value = ''
560        loc_frame = self._get_frame_locals(frame)
561        if "__return__" in loc_frame:
562            rv = loc_frame["__return__"]
563            # return_value += '->'
564            return_value += reprlib.repr(rv) + "\n"
565        ret.append(return_value)
566
567        #s = filename + '(' + `lineno` + ')'
568        filename = self.canonic(frame.f_code.co_filename)
569        link = tpl_link % py3compat.cast_unicode(filename)
570
571        if frame.f_code.co_name:
572            func = frame.f_code.co_name
573        else:
574            func = "<lambda>"
575
576        call = ""
577        if func != "?":
578            if "__args__" in loc_frame:
579                args = reprlib.repr(loc_frame["__args__"])
580            else:
581                args = '()'
582            call = tpl_call % (func, args)
583
584        # The level info should be generated in the same format pdb uses, to
585        # avoid breaking the pdbtrack functionality of python-mode in *emacs.
586        if frame is self.curframe:
587            ret.append('> ')
588        else:
589            ret.append('  ')
590        ret.append(u'%s(%s)%s\n' % (link,lineno,call))
591
592        start = lineno - 1 - context//2
593        lines = linecache.getlines(filename)
594        start = min(start, len(lines) - context)
595        start = max(start, 0)
596        lines = lines[start : start + context]
597
598        for i,line in enumerate(lines):
599            show_arrow = (start + 1 + i == lineno)
600            linetpl = (frame is self.curframe or show_arrow) \
601                      and tpl_line_em \
602                      or tpl_line
603            ret.append(self.__format_line(linetpl, filename,
604                                          start + 1 + i, line,
605                                          arrow = show_arrow) )
606        return ''.join(ret)
607
608    def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
609        bp_mark = ""
610        bp_mark_color = ""
611
612        new_line, err = self.parser.format2(line, 'str')
613        if not err:
614            line = new_line
615
616        bp = None
617        if lineno in self.get_file_breaks(filename):
618            bps = self.get_breaks(filename, lineno)
619            bp = bps[-1]
620
621        if bp:
622            Colors = self.color_scheme_table.active_colors
623            bp_mark = str(bp.number)
624            bp_mark_color = Colors.breakpoint_enabled
625            if not bp.enabled:
626                bp_mark_color = Colors.breakpoint_disabled
627
628        numbers_width = 7
629        if arrow:
630            # This is the line with the error
631            pad = numbers_width - len(str(lineno)) - len(bp_mark)
632            num = '%s%s' % (make_arrow(pad), str(lineno))
633        else:
634            num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
635
636        return tpl_line % (bp_mark_color + bp_mark, num, line)
637
638
639    def print_list_lines(self, filename, first, last):
640        """The printing (as opposed to the parsing part of a 'list'
641        command."""
642        try:
643            Colors = self.color_scheme_table.active_colors
644            ColorsNormal = Colors.Normal
645            tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
646            tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
647            src = []
648            if filename == "<string>" and hasattr(self, "_exec_filename"):
649                filename = self._exec_filename
650
651            for lineno in range(first, last+1):
652                line = linecache.getline(filename, lineno)
653                if not line:
654                    break
655
656                if lineno == self.curframe.f_lineno:
657                    line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
658                else:
659                    line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
660
661                src.append(line)
662                self.lineno = lineno
663
664            print(''.join(src), file=self.stdout)
665
666        except KeyboardInterrupt:
667            pass
668
669    def do_skip_predicates(self, args):
670        """
671        Turn on/off individual predicates as to whether a frame should be hidden/skip.
672
673        The global option to skip (or not) hidden frames is set with skip_hidden
674
675        To change the value of a predicate
676
677            skip_predicates key [true|false]
678
679        Call without arguments to see the current values.
680
681        To permanently change the value of an option add the corresponding
682        command to your ``~/.pdbrc`` file. If you are programmatically using the
683        Pdb instance you can also change the ``default_predicates`` class
684        attribute.
685        """
686        if not args.strip():
687            print("current predicates:")
688            for (p, v) in self._predicates.items():
689                print("   ", p, ":", v)
690            return
691        type_value = args.strip().split(" ")
692        if len(type_value) != 2:
693            print(
694                f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
695            )
696            return
697
698        type_, value = type_value
699        if type_ not in self._predicates:
700            print(f"{type_!r} not in {set(self._predicates.keys())}")
701            return
702        if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
703            print(
704                f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
705            )
706            return
707
708        self._predicates[type_] = value.lower() in ("true", "yes", "1")
709        if not any(self._predicates.values()):
710            print(
711                "Warning, all predicates set to False, skip_hidden may not have any effects."
712            )
713
714    def do_skip_hidden(self, arg):
715        """
716        Change whether or not we should skip frames with the
717        __tracebackhide__ attribute.
718        """
719        if not arg.strip():
720            print(
721                f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
722            )
723        elif arg.strip().lower() in ("true", "yes"):
724            self.skip_hidden = True
725        elif arg.strip().lower() in ("false", "no"):
726            self.skip_hidden = False
727        if not any(self._predicates.values()):
728            print(
729                "Warning, all predicates set to False, skip_hidden may not have any effects."
730            )
731
732    def do_list(self, arg):
733        """Print lines of code from the current stack frame
734        """
735        self.lastcmd = 'list'
736        last = None
737        if arg:
738            try:
739                x = eval(arg, {}, {})
740                if type(x) == type(()):
741                    first, last = x
742                    first = int(first)
743                    last = int(last)
744                    if last < first:
745                        # Assume it's a count
746                        last = first + last
747                else:
748                    first = max(1, int(x) - 5)
749            except:
750                print('*** Error in argument:', repr(arg), file=self.stdout)
751                return
752        elif self.lineno is None:
753            first = max(1, self.curframe.f_lineno - 5)
754        else:
755            first = self.lineno + 1
756        if last is None:
757            last = first + 10
758        self.print_list_lines(self.curframe.f_code.co_filename, first, last)
759
760        # vds: >>
761        lineno = first
762        filename = self.curframe.f_code.co_filename
763        self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
764        # vds: <<
765
766    do_l = do_list
767
768    def getsourcelines(self, obj):
769        lines, lineno = inspect.findsource(obj)
770        if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
771            # must be a module frame: do not try to cut a block out of it
772            return lines, 1
773        elif inspect.ismodule(obj):
774            return lines, 1
775        return inspect.getblock(lines[lineno:]), lineno+1
776
777    def do_longlist(self, arg):
778        """Print lines of code from the current stack frame.
779
780        Shows more lines than 'list' does.
781        """
782        self.lastcmd = 'longlist'
783        try:
784            lines, lineno = self.getsourcelines(self.curframe)
785        except OSError as err:
786            self.error(err)
787            return
788        last = lineno + len(lines)
789        self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
790    do_ll = do_longlist
791
792    def do_debug(self, arg):
793        """debug code
794        Enter a recursive debugger that steps through the code
795        argument (which is an arbitrary expression or statement to be
796        executed in the current environment).
797        """
798        trace_function = sys.gettrace()
799        sys.settrace(None)
800        globals = self.curframe.f_globals
801        locals = self.curframe_locals
802        p = self.__class__(completekey=self.completekey,
803                           stdin=self.stdin, stdout=self.stdout)
804        p.use_rawinput = self.use_rawinput
805        p.prompt = "(%s) " % self.prompt.strip()
806        self.message("ENTERING RECURSIVE DEBUGGER")
807        sys.call_tracing(p.run, (arg, globals, locals))
808        self.message("LEAVING RECURSIVE DEBUGGER")
809        sys.settrace(trace_function)
810        self.lastcmd = p.lastcmd
811
812    def do_pdef(self, arg):
813        """Print the call signature for any callable object.
814
815        The debugger interface to %pdef"""
816        namespaces = [
817            ("Locals", self.curframe_locals),
818            ("Globals", self.curframe.f_globals),
819        ]
820        self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
821
822    def do_pdoc(self, arg):
823        """Print the docstring for an object.
824
825        The debugger interface to %pdoc."""
826        namespaces = [
827            ("Locals", self.curframe_locals),
828            ("Globals", self.curframe.f_globals),
829        ]
830        self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
831
832    def do_pfile(self, arg):
833        """Print (or run through pager) the file where an object is defined.
834
835        The debugger interface to %pfile.
836        """
837        namespaces = [
838            ("Locals", self.curframe_locals),
839            ("Globals", self.curframe.f_globals),
840        ]
841        self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
842
843    def do_pinfo(self, arg):
844        """Provide detailed information about an object.
845
846        The debugger interface to %pinfo, i.e., obj?."""
847        namespaces = [
848            ("Locals", self.curframe_locals),
849            ("Globals", self.curframe.f_globals),
850        ]
851        self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
852
853    def do_pinfo2(self, arg):
854        """Provide extra detailed information about an object.
855
856        The debugger interface to %pinfo2, i.e., obj??."""
857        namespaces = [
858            ("Locals", self.curframe_locals),
859            ("Globals", self.curframe.f_globals),
860        ]
861        self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
862
863    def do_psource(self, arg):
864        """Print (or run through pager) the source code for an object."""
865        namespaces = [
866            ("Locals", self.curframe_locals),
867            ("Globals", self.curframe.f_globals),
868        ]
869        self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
870
871    def do_where(self, arg):
872        """w(here)
873        Print a stack trace, with the most recent frame at the bottom.
874        An arrow indicates the "current frame", which determines the
875        context of most commands. 'bt' is an alias for this command.
876
877        Take a number as argument as an (optional) number of context line to
878        print"""
879        if arg:
880            try:
881                context = int(arg)
882            except ValueError as err:
883                self.error(err)
884                return
885            self.print_stack_trace(context)
886        else:
887            self.print_stack_trace()
888
889    do_w = do_where
890
891    def break_anywhere(self, frame):
892        """
893
894        _stop_in_decorator_internals is overly restrictive, as we may still want
895        to trace function calls, so we need to also update break_anywhere so
896        that is we don't `stop_here`, because of debugger skip, we may still
897        stop at any point inside the function
898
899        """
900
901        sup = super().break_anywhere(frame)
902        if sup:
903            return sup
904        if self._predicates["debuggerskip"]:
905            if DEBUGGERSKIP in frame.f_code.co_varnames:
906                return True
907            if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
908                return True
909        return False
910
911    @skip_doctest
912    def _is_in_decorator_internal_and_should_skip(self, frame):
913        """
914        Utility to tell us whether we are in a decorator internal and should stop.
915
916
917
918        """
919
920        # if we are disabled don't skip
921        if not self._predicates["debuggerskip"]:
922            return False
923
924        # if frame is tagged, skip by default.
925        if DEBUGGERSKIP in frame.f_code.co_varnames:
926            return True
927
928        # if one of the parent frame value set to True skip as well.
929
930        cframe = frame
931        while getattr(cframe, "f_back", None):
932            cframe = cframe.f_back
933            if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
934                return True
935
936        return False
937
938    def stop_here(self, frame):
939        """Check if pdb should stop here"""
940        if not super().stop_here(frame):
941            return False
942
943        if self._is_in_decorator_internal_and_should_skip(frame) is True:
944            return False
945
946        hidden = False
947        if self.skip_hidden:
948            hidden = self._hidden_predicate(frame)
949        if hidden:
950            if self.report_skipped:
951                Colors = self.color_scheme_table.active_colors
952                ColorsNormal = Colors.Normal
953                print(f"{Colors.excName}    [... skipped 1 hidden frame]{ColorsNormal}\n")
954            return False
955        return True
956
957    def do_up(self, arg):
958        """u(p) [count]
959        Move the current frame count (default one) levels up in the
960        stack trace (to an older frame).
961
962        Will skip hidden frames.
963        """
964        # modified version of upstream that skips
965        # frames with __tracebackhide__
966        if self.curindex == 0:
967            self.error("Oldest frame")
968            return
969        try:
970            count = int(arg or 1)
971        except ValueError:
972            self.error("Invalid frame count (%s)" % arg)
973            return
974        skipped = 0
975        if count < 0:
976            _newframe = 0
977        else:
978            _newindex = self.curindex
979            counter = 0
980            hidden_frames = self.hidden_frames(self.stack)
981            for i in range(self.curindex - 1, -1, -1):
982                frame = self.stack[i][0]
983                if hidden_frames[i] and self.skip_hidden:
984                    skipped += 1
985                    continue
986                counter += 1
987                if counter >= count:
988                    break
989            else:
990                # if no break occured.
991                self.error("all frames above hidden")
992                return
993
994            Colors = self.color_scheme_table.active_colors
995            ColorsNormal = Colors.Normal
996            _newframe = i
997        self._select_frame(_newframe)
998        if skipped:
999            print(
1000                f"{Colors.excName}    [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1001            )
1002
1003    def do_down(self, arg):
1004        """d(own) [count]
1005        Move the current frame count (default one) levels down in the
1006        stack trace (to a newer frame).
1007
1008        Will skip hidden frames.
1009        """
1010        if self.curindex + 1 == len(self.stack):
1011            self.error("Newest frame")
1012            return
1013        try:
1014            count = int(arg or 1)
1015        except ValueError:
1016            self.error("Invalid frame count (%s)" % arg)
1017            return
1018        if count < 0:
1019            _newframe = len(self.stack) - 1
1020        else:
1021            _newindex = self.curindex
1022            counter = 0
1023            skipped = 0
1024            hidden_frames = self.hidden_frames(self.stack)
1025            for i in range(self.curindex + 1, len(self.stack)):
1026                frame = self.stack[i][0]
1027                if hidden_frames[i] and self.skip_hidden:
1028                    skipped += 1
1029                    continue
1030                counter += 1
1031                if counter >= count:
1032                    break
1033            else:
1034                self.error("all frames bellow hidden")
1035                return
1036
1037            Colors = self.color_scheme_table.active_colors
1038            ColorsNormal = Colors.Normal
1039            if skipped:
1040                print(
1041                    f"{Colors.excName}    [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1042                )
1043            _newframe = i
1044
1045        self._select_frame(_newframe)
1046
1047    do_d = do_down
1048    do_u = do_up
1049
1050    def do_context(self, context):
1051        """context number_of_lines
1052        Set the number of lines of source code to show when displaying
1053        stacktrace information.
1054        """
1055        try:
1056            new_context = int(context)
1057            if new_context <= 0:
1058                raise ValueError()
1059            self.context = new_context
1060        except ValueError:
1061            self.error("The 'context' command requires a positive integer argument.")
1062
1063
1064class InterruptiblePdb(Pdb):
1065    """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1066
1067    def cmdloop(self, intro=None):
1068        """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1069        try:
1070            return OldPdb.cmdloop(self, intro=intro)
1071        except KeyboardInterrupt:
1072            self.stop_here = lambda frame: False
1073            self.do_quit("")
1074            sys.settrace(None)
1075            self.quitting = False
1076            raise
1077
1078    def _cmdloop(self):
1079        while True:
1080            try:
1081                # keyboard interrupts allow for an easy way to cancel
1082                # the current command, so allow them during interactive input
1083                self.allow_kbdint = True
1084                self.cmdloop()
1085                self.allow_kbdint = False
1086                break
1087            except KeyboardInterrupt:
1088                self.message('--KeyboardInterrupt--')
1089                raise
1090
1091
1092def set_trace(frame=None):
1093    """
1094    Start debugging from `frame`.
1095
1096    If frame is not specified, debugging starts from caller's frame.
1097    """
1098    Pdb().set_trace(frame or sys._getframe().f_back)
1099