123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618 |
- # -*- coding: utf-8 -*-
- """
- Pdb debugger class.
- Modified from the standard pdb.Pdb class to avoid including readline, so that
- the command line completion of other programs which include this isn't
- damaged.
- In the future, this class will be expanded with improvements over the standard
- pdb.
- The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
- changes. Licensing should therefore be under the standard Python terms. For
- details on the PSF (Python Software Foundation) standard license, see:
- https://docs.python.org/2/license.html
- """
- #*****************************************************************************
- #
- # This file is licensed under the PSF license.
- #
- # Copyright (C) 2001 Python Software Foundation, www.python.org
- # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
- #
- #
- #*****************************************************************************
- from __future__ import print_function
- import bdb
- import functools
- import inspect
- import sys
- import warnings
- from IPython import get_ipython
- from IPython.utils import PyColorize, ulinecache
- from IPython.utils import coloransi, py3compat
- from IPython.core.excolors import exception_colors
- from IPython.testing.skipdoctest import skip_doctest
- prompt = 'ipdb> '
- #We have to check this directly from sys.argv, config struct not yet available
- from pdb import Pdb as OldPdb
- # Allow the set_trace code to operate outside of an ipython instance, even if
- # it does so with some limitations. The rest of this support is implemented in
- # the Tracer constructor.
- def make_arrow(pad):
- """generate the leading arrow in front of traceback or debugger"""
- if pad >= 2:
- return '-'*(pad-2) + '> '
- elif pad == 1:
- return '>'
- return ''
- def BdbQuit_excepthook(et, ev, tb, excepthook=None):
- """Exception hook which handles `BdbQuit` exceptions.
- All other exceptions are processed using the `excepthook`
- parameter.
- """
- warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
- DeprecationWarning, stacklevel=2)
- if et==bdb.BdbQuit:
- print('Exiting Debugger.')
- elif excepthook is not None:
- excepthook(et, ev, tb)
- else:
- # Backwards compatibility. Raise deprecation warning?
- BdbQuit_excepthook.excepthook_ori(et,ev,tb)
- def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
- warnings.warn(
- "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
- DeprecationWarning, stacklevel=2)
- print('Exiting Debugger.')
- class Tracer(object):
- """
- DEPRECATED
- Class for local debugging, similar to pdb.set_trace.
- Instances of this class, when called, behave like pdb.set_trace, but
- providing IPython's enhanced capabilities.
- This is implemented as a class which must be initialized in your own code
- and not as a standalone function because we need to detect at runtime
- whether IPython is already active or not. That detection is done in the
- constructor, ensuring that this code plays nicely with a running IPython,
- while functioning acceptably (though with limitations) if outside of it.
- """
- @skip_doctest
- def __init__(self, colors=None):
- """
- DEPRECATED
- Create a local debugger instance.
- Parameters
- ----------
- colors : str, optional
- The name of the color scheme to use, it must be one of IPython's
- valid color schemes. If not given, the function will default to
- the current IPython scheme when running inside IPython, and to
- 'NoColor' otherwise.
- Examples
- --------
- ::
- from IPython.core.debugger import Tracer; debug_here = Tracer()
- Later in your code::
- debug_here() # -> will open up the debugger at that point.
- Once the debugger activates, you can use all of its regular commands to
- step through code, set breakpoints, etc. See the pdb documentation
- from the Python standard library for usage details.
- """
- warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
- "`IPython.core.debugger.Pdb.set_trace()`",
- DeprecationWarning, stacklevel=2)
- ip = get_ipython()
- if ip is None:
- # Outside of ipython, we set our own exception hook manually
- sys.excepthook = functools.partial(BdbQuit_excepthook,
- excepthook=sys.excepthook)
- def_colors = 'NoColor'
- else:
- # In ipython, we use its custom exception handler mechanism
- def_colors = ip.colors
- ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
- if colors is None:
- colors = def_colors
- # The stdlib debugger internally uses a modified repr from the `repr`
- # module, that limits the length of printed strings to a hardcoded
- # limit of 30 characters. That much trimming is too aggressive, let's
- # at least raise that limit to 80 chars, which should be enough for
- # most interactive uses.
- try:
- try:
- from reprlib import aRepr # Py 3
- except ImportError:
- from repr import aRepr # Py 2
- aRepr.maxstring = 80
- except:
- # This is only a user-facing convenience, so any error we encounter
- # here can be warned about but can be otherwise ignored. These
- # printouts will tell us about problems if this API changes
- import traceback
- traceback.print_exc()
- self.debugger = Pdb(colors)
- def __call__(self):
- """Starts an interactive debugger at the point where called.
- This is similar to the pdb.set_trace() function from the std lib, but
- using IPython's enhanced debugger."""
- self.debugger.set_trace(sys._getframe().f_back)
- def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
- """Make new_fn have old_fn's doc string. This is particularly useful
- for the ``do_...`` commands that hook into the help system.
- Adapted from from a comp.lang.python posting
- by Duncan Booth."""
- def wrapper(*args, **kw):
- return new_fn(*args, **kw)
- if old_fn.__doc__:
- wrapper.__doc__ = old_fn.__doc__ + additional_text
- return wrapper
- def _file_lines(fname):
- """Return the contents of a named file as a list of lines.
- This function never raises an IOError exception: if the file can't be
- read, it simply returns an empty list."""
- try:
- outfile = open(fname)
- except IOError:
- return []
- else:
- out = outfile.readlines()
- outfile.close()
- return out
- class Pdb(OldPdb):
- """Modified Pdb class, does not load readline.
- for a standalone version that uses prompt_toolkit, see
- `IPython.terminal.debugger.TerminalPdb` and
- `IPython.terminal.debugger.set_trace()`
- """
- def __init__(self, color_scheme=None, completekey=None,
- stdin=None, stdout=None, context=5):
- # Parent constructor:
- try:
- self.context = int(context)
- if self.context <= 0:
- raise ValueError("Context must be a positive integer")
- except (TypeError, ValueError):
- raise ValueError("Context must be a positive integer")
- OldPdb.__init__(self, completekey, stdin, stdout)
- # IPython changes...
- self.shell = get_ipython()
- if self.shell is None:
- save_main = sys.modules['__main__']
- # No IPython instance running, we must create one
- from IPython.terminal.interactiveshell import \
- TerminalInteractiveShell
- self.shell = TerminalInteractiveShell.instance()
- # needed by any code which calls __import__("__main__") after
- # the debugger was entered. See also #9941.
- sys.modules['__main__'] = save_main
- if color_scheme is not None:
- warnings.warn(
- "The `color_scheme` argument is deprecated since version 5.1",
- DeprecationWarning)
- else:
- color_scheme = self.shell.colors
- self.aliases = {}
- # Create color table: we copy the default one from the traceback
- # module and add a few attributes needed for debugging
- self.color_scheme_table = exception_colors()
- # shorthands
- C = coloransi.TermColors
- cst = self.color_scheme_table
- cst['NoColor'].colors.prompt = C.NoColor
- cst['NoColor'].colors.breakpoint_enabled = C.NoColor
- cst['NoColor'].colors.breakpoint_disabled = C.NoColor
- cst['Linux'].colors.prompt = C.Green
- cst['Linux'].colors.breakpoint_enabled = C.LightRed
- cst['Linux'].colors.breakpoint_disabled = C.Red
- cst['LightBG'].colors.prompt = C.Blue
- cst['LightBG'].colors.breakpoint_enabled = C.LightRed
- cst['LightBG'].colors.breakpoint_disabled = C.Red
- cst['Neutral'].colors.prompt = C.Blue
- cst['Neutral'].colors.breakpoint_enabled = C.LightRed
- cst['Neutral'].colors.breakpoint_disabled = C.Red
- self.set_colors(color_scheme)
- # Add a python parser so we can syntax highlight source while
- # debugging.
- self.parser = PyColorize.Parser()
- # Set the prompt - the default prompt is '(Pdb)'
- self.prompt = prompt
- def set_colors(self, scheme):
- """Shorthand access to the color table scheme selector method."""
- self.color_scheme_table.set_active_scheme(scheme)
- def interaction(self, frame, traceback):
- try:
- OldPdb.interaction(self, frame, traceback)
- except KeyboardInterrupt:
- sys.stdout.write('\n' + self.shell.get_exception_only())
- def new_do_up(self, arg):
- OldPdb.do_up(self, arg)
- do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
- def new_do_down(self, arg):
- OldPdb.do_down(self, arg)
- do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
- def new_do_frame(self, arg):
- OldPdb.do_frame(self, arg)
- def new_do_quit(self, arg):
- if hasattr(self, 'old_all_completions'):
- self.shell.Completer.all_completions=self.old_all_completions
- return OldPdb.do_quit(self, arg)
- do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
- def new_do_restart(self, arg):
- """Restart command. In the context of ipython this is exactly the same
- thing as 'quit'."""
- self.msg("Restart doesn't make sense here. Using 'quit' instead.")
- return self.do_quit(arg)
- def print_stack_trace(self, context=None):
- if context is None:
- context = self.context
- try:
- context=int(context)
- if context <= 0:
- raise ValueError("Context must be a positive integer")
- except (TypeError, ValueError):
- raise ValueError("Context must be a positive integer")
- try:
- for frame_lineno in self.stack:
- self.print_stack_entry(frame_lineno, context=context)
- except KeyboardInterrupt:
- pass
- def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
- context=None):
- if context is None:
- context = self.context
- try:
- context=int(context)
- if context <= 0:
- raise ValueError("Context must be a positive integer")
- except (TypeError, ValueError):
- raise ValueError("Context must be a positive integer")
- print(self.format_stack_entry(frame_lineno, '', context))
- # vds: >>
- frame, lineno = frame_lineno
- filename = frame.f_code.co_filename
- self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
- # vds: <<
- def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
- if context is None:
- context = self.context
- try:
- context=int(context)
- if context <= 0:
- print("Context must be a positive integer")
- except (TypeError, ValueError):
- print("Context must be a positive integer")
- try:
- import reprlib # Py 3
- except ImportError:
- import repr as reprlib # Py 2
- ret = []
- Colors = self.color_scheme_table.active_colors
- ColorsNormal = Colors.Normal
- tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
- tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
- tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
- tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
- ColorsNormal)
- frame, lineno = frame_lineno
- return_value = ''
- if '__return__' in frame.f_locals:
- rv = frame.f_locals['__return__']
- #return_value += '->'
- return_value += reprlib.repr(rv) + '\n'
- ret.append(return_value)
- #s = filename + '(' + `lineno` + ')'
- filename = self.canonic(frame.f_code.co_filename)
- link = tpl_link % py3compat.cast_unicode(filename)
- if frame.f_code.co_name:
- func = frame.f_code.co_name
- else:
- func = "<lambda>"
- call = ''
- if func != '?':
- if '__args__' in frame.f_locals:
- args = reprlib.repr(frame.f_locals['__args__'])
- else:
- args = '()'
- call = tpl_call % (func, args)
- # The level info should be generated in the same format pdb uses, to
- # avoid breaking the pdbtrack functionality of python-mode in *emacs.
- if frame is self.curframe:
- ret.append('> ')
- else:
- ret.append(' ')
- ret.append(u'%s(%s)%s\n' % (link,lineno,call))
- start = lineno - 1 - context//2
- lines = ulinecache.getlines(filename, frame.f_globals)
- start = min(start, len(lines) - context)
- start = max(start, 0)
- lines = lines[start : start + context]
- for i,line in enumerate(lines):
- show_arrow = (start + 1 + i == lineno)
- linetpl = (frame is self.curframe or show_arrow) \
- and tpl_line_em \
- or tpl_line
- ret.append(self.__format_line(linetpl, filename,
- start + 1 + i, line,
- arrow = show_arrow) )
- return ''.join(ret)
- def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
- bp_mark = ""
- bp_mark_color = ""
- scheme = self.color_scheme_table.active_scheme_name
- new_line, err = self.parser.format2(line, 'str', scheme)
- if not err: line = new_line
- bp = None
- if lineno in self.get_file_breaks(filename):
- bps = self.get_breaks(filename, lineno)
- bp = bps[-1]
- if bp:
- Colors = self.color_scheme_table.active_colors
- bp_mark = str(bp.number)
- bp_mark_color = Colors.breakpoint_enabled
- if not bp.enabled:
- bp_mark_color = Colors.breakpoint_disabled
- numbers_width = 7
- if arrow:
- # This is the line with the error
- pad = numbers_width - len(str(lineno)) - len(bp_mark)
- num = '%s%s' % (make_arrow(pad), str(lineno))
- else:
- num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
- return tpl_line % (bp_mark_color + bp_mark, num, line)
- def print_list_lines(self, filename, first, last):
- """The printing (as opposed to the parsing part of a 'list'
- command."""
- try:
- Colors = self.color_scheme_table.active_colors
- ColorsNormal = Colors.Normal
- tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
- tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
- src = []
- if filename == "<string>" and hasattr(self, "_exec_filename"):
- filename = self._exec_filename
- for lineno in range(first, last+1):
- line = ulinecache.getline(filename, lineno)
- if not line:
- break
- if lineno == self.curframe.f_lineno:
- line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
- else:
- line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
- src.append(line)
- self.lineno = lineno
- print(''.join(src))
- except KeyboardInterrupt:
- pass
- def do_list(self, arg):
- """Print lines of code from the current stack frame
- """
- self.lastcmd = 'list'
- last = None
- if arg:
- try:
- x = eval(arg, {}, {})
- if type(x) == type(()):
- first, last = x
- first = int(first)
- last = int(last)
- if last < first:
- # Assume it's a count
- last = first + last
- else:
- first = max(1, int(x) - 5)
- except:
- print('*** Error in argument:', repr(arg))
- return
- elif self.lineno is None:
- first = max(1, self.curframe.f_lineno - 5)
- else:
- first = self.lineno + 1
- if last is None:
- last = first + 10
- self.print_list_lines(self.curframe.f_code.co_filename, first, last)
- # vds: >>
- lineno = first
- filename = self.curframe.f_code.co_filename
- self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
- # vds: <<
- do_l = do_list
- def getsourcelines(self, obj):
- lines, lineno = inspect.findsource(obj)
- if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
- # must be a module frame: do not try to cut a block out of it
- return lines, 1
- elif inspect.ismodule(obj):
- return lines, 1
- return inspect.getblock(lines[lineno:]), lineno+1
- def do_longlist(self, arg):
- """Print lines of code from the current stack frame.
- Shows more lines than 'list' does.
- """
- self.lastcmd = 'longlist'
- try:
- lines, lineno = self.getsourcelines(self.curframe)
- except OSError as err:
- self.error(err)
- return
- last = lineno + len(lines)
- self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
- do_ll = do_longlist
- def do_pdef(self, arg):
- """Print the call signature for any callable object.
- The debugger interface to %pdef"""
- namespaces = [('Locals', self.curframe.f_locals),
- ('Globals', self.curframe.f_globals)]
- self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
- def do_pdoc(self, arg):
- """Print the docstring for an object.
- The debugger interface to %pdoc."""
- namespaces = [('Locals', self.curframe.f_locals),
- ('Globals', self.curframe.f_globals)]
- self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
- def do_pfile(self, arg):
- """Print (or run through pager) the file where an object is defined.
- The debugger interface to %pfile.
- """
- namespaces = [('Locals', self.curframe.f_locals),
- ('Globals', self.curframe.f_globals)]
- self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
- def do_pinfo(self, arg):
- """Provide detailed information about an object.
- The debugger interface to %pinfo, i.e., obj?."""
- namespaces = [('Locals', self.curframe.f_locals),
- ('Globals', self.curframe.f_globals)]
- self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
- def do_pinfo2(self, arg):
- """Provide extra detailed information about an object.
- The debugger interface to %pinfo2, i.e., obj??."""
- namespaces = [('Locals', self.curframe.f_locals),
- ('Globals', self.curframe.f_globals)]
- self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
- def do_psource(self, arg):
- """Print (or run through pager) the source code for an object."""
- namespaces = [('Locals', self.curframe.f_locals),
- ('Globals', self.curframe.f_globals)]
- self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
- if sys.version_info > (3, ):
- def do_where(self, arg):
- """w(here)
- Print a stack trace, with the most recent frame at the bottom.
- An arrow indicates the "current frame", which determines the
- context of most commands. 'bt' is an alias for this command.
- Take a number as argument as an (optional) number of context line to
- print"""
- if arg:
- context = int(arg)
- self.print_stack_trace(context)
- else:
- self.print_stack_trace()
- do_w = do_where
- def set_trace(frame=None):
- """
- Start debugging from `frame`.
- If frame is not specified, debugging starts from caller's frame.
- """
- Pdb().set_trace(frame or sys._getframe().f_back)
|