diff options
author | root <root@ghunt-desktop.(none)> | 2010-11-10 16:58:38 (GMT) |
---|---|---|
committer | root <root@ghunt-desktop.(none)> | 2010-11-10 16:58:38 (GMT) |
commit | fba87852b019e48ab3db508138f3ac35932a6f5a (patch) | |
tree | 3a38eadfc783d9f781cb3a3f1246b6abefef007f /ipy_sh.py | |
parent | 966312ba98d729177238f4663d789a789663eb2e (diff) |
embeded shell, embeded trace, help localization
Diffstat (limited to 'ipy_sh.py')
-rw-r--r-- | ipy_sh.py | 555 |
1 files changed, 534 insertions, 21 deletions
@@ -1,22 +1,535 @@ -#!/usr/bin/env python -#ipy_sh is module which encapsulates IPython.IPShellEmbed to work with gtk gui -import IPython -import inspect -from IPython.frontend.terminal.embed import InteractiveShellEmbed - -class PSE(InteractiveShellEmbed): - def __init__(self): - frame = inspect.currentframe() +# -*- 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: + +http://www.python.org/2.2.3/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> +# +# +#***************************************************************************** + +import bdb +import cmd +import linecache +import os +import sys + +from IPython import PyColorize, ColorANSI, ipapi +from IPython.genutils import Term +from IPython.excolors import exception_colors + +# See if we can use pydb. +has_pydb = False +prompt = 'ipdb> ' +#We have to check this directly from sys.argv, config struct not yet available +if '-pydb' in sys.argv: + try: + import pydb + if hasattr(pydb.pydb, "runl") and pydb.version>'1.17': + # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we + # better protect against it. + has_pydb = True + except ImportError: + print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available" + +if has_pydb: + from pydb import Pdb as OldPdb + #print "Using pydb for %run -d and post-mortem" #dbg + prompt = 'ipydb> ' +else: + 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 BdbQuit_excepthook(et,ev,tb): + if et==bdb.BdbQuit: + print 'Exiting Debugger.' + else: + BdbQuit_excepthook.excepthook_ori(et,ev,tb) + +def BdbQuit_IPython_excepthook(self,et,ev,tb): + print 'Exiting Debugger.' + +class Tracer(object): + """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. + """ + + def __init__(self,colors=None): + """Create a local debugger instance. + + :Parameters: + + - `colors` (None): a string containing 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. + + Usage example: + + from IPython.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. + """ + + global __IPYTHON__ try: - info = inspect.getframeinfo(frame,context=3) - finally: - del frame - print('filename:%s line:%s function:%s index into code %s'%(info[0],info[1],info[2],info[4],)) - for line in info[3]: - print line, - args = ['=pi1','in <\\#>:','-po','Out<\\#>:'] - InteractiveShellEmbed.__init__(self,args,banner='Now in shell at your breakpoint\nTry debug\n') - -if __name__ == '__main__': - test = PSE() -
\ No newline at end of file + __IPYTHON__ + except NameError: + # Outside of ipython, we set our own exception hook manually + __IPYTHON__ = ipapi.get(True,False) + BdbQuit_excepthook.excepthook_ori = sys.excepthook + sys.excepthook = BdbQuit_excepthook + def_colors = 'NoColor' + try: + # Limited tab completion support + import rlcompleter,readline + readline.parse_and_bind('tab: complete') + except ImportError: + pass + else: + # In ipython, we use its custom exception handler mechanism + ip = ipapi.get() + def_colors = ip.options.colors + ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook) + + if colors is None: + colors = def_colors + self.debugger = Pdb(colors) + + #additions for pydebug + from util import Utilities + self.util = Utilities(None) + #save xid so that we can return to it after the pdb sesion + self.xid = self.util.get_xid() + + 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.""" + + #first switch to the terminal window + window = self.util.activate_bundle_id('org.laptop.PyDebug') + + self.debugger.set_trace(sys._getframe(2)) + + #then go back to the gui we came from + self.util.activate_xid(self.xid) + + +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.""" + + if sys.version[:3] >= '2.5' or has_pydb: + def __init__(self,color_scheme='NoColor',completekey=None, + stdin=None, stdout=None): + + # Parent constructor: + if has_pydb and completekey is None: + OldPdb.__init__(self,stdin=stdin,stdout=Term.cout) + else: + OldPdb.__init__(self,completekey,stdin,stdout) + + self.prompt = prompt # The default prompt is '(Pdb)' + + # IPython changes... + self.is_pydb = has_pydb + + if self.is_pydb: + + # iplib.py's ipalias seems to want pdb's checkline + # which located in pydb.fn + import pydb.fns + self.checkline = lambda filename, lineno: \ + pydb.fns.checkline(self, filename, lineno) + + self.curframe = None + self.do_restart = self.new_do_restart + + self.old_all_completions = __IPYTHON__.Completer.all_completions + __IPYTHON__.Completer.all_completions=self.all_completions + + self.do_list = decorate_fn_with_doc(self.list_command_pydb, + OldPdb.do_list) + self.do_l = self.do_list + self.do_frame = decorate_fn_with_doc(self.new_do_frame, + OldPdb.do_frame) + + 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.breakpoint_enabled = C.NoColor + cst['NoColor'].colors.breakpoint_disabled = C.NoColor + + cst['Linux'].colors.breakpoint_enabled = C.LightRed + cst['Linux'].colors.breakpoint_disabled = C.Red + + cst['LightBG'].colors.breakpoint_enabled = C.LightRed + cst['LightBG'].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() + + + else: + # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor, + # because it binds readline and breaks tab-completion. This means we + # have to COPY the constructor here. + def __init__(self,color_scheme='NoColor'): + bdb.Bdb.__init__(self) + cmd.Cmd.__init__(self,completekey=None) # don't load readline + self.prompt = 'ipdb> ' # The default prompt is '(Pdb)' + self.aliases = {} + + # These two lines are part of the py2.4 constructor, let's put them + # unconditionally here as they won't cause any problems in 2.3. + self.mainpyfile = '' + self._wait_for_mainpyfile = 0 + + # Read $HOME/.pdbrc and ./.pdbrc + try: + self.rcLines = _file_lines(os.path.join(os.environ['HOME'], + ".pdbrc")) + except KeyError: + self.rcLines = [] + self.rcLines.extend(_file_lines(".pdbrc")) + + # 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.breakpoint_enabled = C.NoColor + cst['NoColor'].colors.breakpoint_disabled = C.NoColor + + cst['Linux'].colors.breakpoint_enabled = C.LightRed + cst['Linux'].colors.breakpoint_disabled = C.Red + + cst['LightBG'].colors.breakpoint_enabled = C.LightRed + cst['LightBG'].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() + + 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): + __IPYTHON__.set_completer_frame(frame) + OldPdb.interaction(self, frame, traceback) + + def new_do_up(self, arg): + OldPdb.do_up(self, arg) + __IPYTHON__.set_completer_frame(self.curframe) + 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) + __IPYTHON__.set_completer_frame(self.curframe) + + 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) + __IPYTHON__.set_completer_frame(self.curframe) + + def new_do_quit(self, arg): + + if hasattr(self, 'old_all_completions'): + __IPYTHON__.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 postloop(self): + __IPYTHON__.set_completer_frame(None) + + def print_stack_trace(self): + try: + for frame_lineno in self.stack: + self.print_stack_entry(frame_lineno, context = 5) + except KeyboardInterrupt: + pass + + def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ', + context = 3): + #frame, lineno = frame_lineno + print >>Term.cout, self.format_stack_entry(frame_lineno, '', context) + + # vds: >> + frame, lineno = frame_lineno + filename = frame.f_code.co_filename + __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) + # vds: << + + def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3): + import linecache, repr + + ret = [] + + Colors = self.color_scheme_table.active_colors + ColorsNormal = Colors.Normal + tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) + tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) + tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) + tpl_line_em = '%%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 += repr.repr(rv) + '\n' + ret.append(return_value) + + #s = filename + '(' + `lineno` + ')' + filename = self.canonic(frame.f_code.co_filename) + link = tpl_link % 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 = repr.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('%s(%s)%s\n' % (link,lineno,call)) + + start = lineno - 1 - context//2 + lines = linecache.getlines(filename) + start = max(start, 0) + start = min(start, len(lines) - context) + 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) + if pad >= 3: + marker = '-'*(pad-3) + '-> ' + elif pad == 2: + marker = '> ' + elif pad == 1: + marker = '>' + else: + marker = '' + num = '%s%s' % (marker, str(lineno)) + line = tpl_line % (bp_mark_color + bp_mark, num, line) + else: + num = '%*s' % (numbers_width - len(bp_mark), str(lineno)) + line = tpl_line % (bp_mark_color + bp_mark, num, line) + + return line + + def list_command_pydb(self, arg): + """List command to use if we have a newer pydb installed""" + filename, first, last = OldPdb.parse_list_cmd(self, arg) + if filename is not None: + self.print_list_lines(filename, first, last) + + 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 = [] + for lineno in range(first, last+1): + line = linecache.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 >>Term.cout, ''.join(src) + + except KeyboardInterrupt: + pass + + def do_list(self, arg): + 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:', `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 + __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) + # vds: << + + do_l = do_list + + def do_pdef(self, arg): + """The debugger interface to magic_pdef""" + namespaces = [('Locals', self.curframe.f_locals), + ('Globals', self.curframe.f_globals)] + __IPYTHON__.magic_pdef(arg, namespaces=namespaces) + + def do_pdoc(self, arg): + """The debugger interface to magic_pdoc""" + namespaces = [('Locals', self.curframe.f_locals), + ('Globals', self.curframe.f_globals)] + __IPYTHON__.magic_pdoc(arg, namespaces=namespaces) + + def do_pinfo(self, arg): + """The debugger equivalant of ?obj""" + namespaces = [('Locals', self.curframe.f_locals), + ('Globals', self.curframe.f_globals)] + __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces) |