Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/ipy_sh.py
diff options
context:
space:
mode:
authorroot <root@ghunt-desktop.(none)>2010-11-10 16:58:38 (GMT)
committer root <root@ghunt-desktop.(none)>2010-11-10 16:58:38 (GMT)
commitfba87852b019e48ab3db508138f3ac35932a6f5a (patch)
tree3a38eadfc783d9f781cb3a3f1246b6abefef007f /ipy_sh.py
parent966312ba98d729177238f4663d789a789663eb2e (diff)
embeded shell, embeded trace, help localization
Diffstat (limited to 'ipy_sh.py')
-rw-r--r--ipy_sh.py555
1 files changed, 534 insertions, 21 deletions
diff --git a/ipy_sh.py b/ipy_sh.py
index 4df8756..55c458a 100644
--- a/ipy_sh.py
+++ b/ipy_sh.py
@@ -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)