diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py
index 711b2f9..edf5c9c 100644
--- a/IPython/core/interactiveshell.py
+++ b/IPython/core/interactiveshell.py
@@ -29,6 +29,7 @@ import sys
 import tempfile
 from contextlib import nested
 
+from IPython.config.configurable import Configurable
 from IPython.core import debugger, oinspect
 from IPython.core import history as ipcorehist
 from IPython.core import prefilter
@@ -36,8 +37,8 @@ from IPython.core import shadowns
 from IPython.core import ultratb
 from IPython.core.alias import AliasManager
 from IPython.core.builtin_trap import BuiltinTrap
-from IPython.config.configurable import Configurable
 from IPython.core.display_trap import DisplayTrap
+from IPython.core.displayhook import DisplayHook
 from IPython.core.error import UsageError
 from IPython.core.extensions import ExtensionManager
 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
@@ -47,24 +48,22 @@ from IPython.core.magic import Magic
 from IPython.core.payload import PayloadManager
 from IPython.core.plugin import PluginManager
 from IPython.core.prefilter import PrefilterManager
-from IPython.core.displayhook import DisplayHook
-import IPython.core.hooks
 from IPython.external.Itpl import ItplNS
 from IPython.utils import PyColorize
+from IPython.utils import io
 from IPython.utils import pickleshare
 from IPython.utils.doctestreload import doctest_reload
+from IPython.utils.io import ask_yes_no, rprint
 from IPython.utils.ipstruct import Struct
-import IPython.utils.io
-from IPython.utils.io import ask_yes_no
 from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError
 from IPython.utils.process import getoutput, getoutputerror
 from IPython.utils.strdispatch import StrDispatch
 from IPython.utils.syspathcontext import prepended_to_syspath
 from IPython.utils.text import num_ini_spaces
+from IPython.utils.traitlets import (Int, Str, CBool, CaselessStrEnum, Enum,
+                                     List, Unicode, Instance, Type)
 from IPython.utils.warn import warn, error, fatal
-from IPython.utils.traitlets import (
-    Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode, Instance, Type
-)
+import IPython.core.hooks
 
 # from IPython.utils import growl
 # growl.start("IPython")
@@ -430,12 +429,12 @@ class InteractiveShell(Configurable, Magic):
     def init_io(self):
         import IPython.utils.io
         if sys.platform == 'win32' and self.has_readline:
-            Term = IPython.utils.io.IOTerm(
+            Term = io.IOTerm(
                 cout=self.readline._outputfile,cerr=self.readline._outputfile
             )
         else:
-            Term = IPython.utils.io.IOTerm()
-        IPython.utils.io.Term = Term
+            Term = io.IOTerm()
+        io.Term = Term
 
     def init_prompts(self):
         # TODO: This is a pass for now because the prompts are managed inside
@@ -1181,7 +1180,7 @@ class InteractiveShell(Configurable, Magic):
         # Set the exception mode
         self.InteractiveTB.set_mode(mode=self.xmode)
 
-    def set_custom_exc(self,exc_tuple,handler):
+    def set_custom_exc(self, exc_tuple, handler):
         """set_custom_exc(exc_tuple,handler)
 
         Set a custom exception handler, which will be called if any of the
@@ -1198,7 +1197,12 @@ class InteractiveShell(Configurable, Magic):
             exc_tuple == (MyCustomException,)
 
           - handler: this must be defined as a function with the following
-          basic interface: def my_handler(self,etype,value,tb).
+          basic interface::
+
+            def my_handler(self, etype, value, tb, tb_offset=None)
+                ...
+                # The return value must be
+                return structured_traceback
 
           This will be made into an instance method (via new.instancemethod)
           of IPython itself, and it will be called if any of the exceptions
@@ -1272,7 +1276,7 @@ class InteractiveShell(Configurable, Magic):
                     etype, value, tb = sys.last_type, sys.last_value, \
                                        sys.last_traceback
                 else:
-                    self.write('No traceback available to show.\n')
+                    self.write_err('No traceback available to show.\n')
                     return
     
             if etype is SyntaxError:
@@ -1291,22 +1295,39 @@ class InteractiveShell(Configurable, Magic):
                 sys.last_traceback = tb
     
                 if etype in self.custom_exceptions:
-                    self.CustomTB(etype,value,tb)
+                    # FIXME: Old custom traceback objects may just return a
+                    # string, in that case we just put it into a list
+                    stb = self.CustomTB(etype, value, tb, tb_offset)
+                    if isinstance(ctb, basestring):
+                        stb = [stb]
                 else:
                     if exception_only:
-                        m = ('An exception has occurred, use %tb to see the '
-                             'full traceback.')
-                        print m
-                        self.InteractiveTB.show_exception_only(etype, value)
+                        stb = ['An exception has occurred, use %tb to see '
+                               'the full traceback.']
+                        stb.extend(self.InteractiveTB.get_exception_only(etype,
+                                                                         value))
                     else:
-                        self.InteractiveTB(etype,value,tb,tb_offset=tb_offset)
+                        stb = self.InteractiveTB.structured_traceback(etype,
+                                                value, tb, tb_offset=tb_offset)
+                        # FIXME: the pdb calling should be done by us, not by
+                        # the code computing the traceback.
                         if self.InteractiveTB.call_pdb:
                             # pdb mucks up readline, fix it back
                             self.set_completer()
-                        
+
+                # Actually show the traceback
+                self._showtraceback(etype, value, stb)
+                
         except KeyboardInterrupt:
-            self.write("\nKeyboardInterrupt\n")        
-        
+            self.write_err("\nKeyboardInterrupt\n")
+
+    def _showtraceback(self, etype, evalue, stb):
+        """Actually show a traceback.
+
+        Subclasses may override this method to put the traceback on a different
+        place, like a side channel.
+        """
+        self.write_err('\n'.join(stb))
 
     def showsyntaxerror(self, filename=None):
         """Display the syntax error that just occurred.
@@ -1339,7 +1360,8 @@ class InteractiveShell(Configurable, Magic):
                 except:
                     # If that failed, assume SyntaxError is a string
                     value = msg, (filename, lineno, offset, line)
-        self.SyntaxTB(etype,value,[])
+        stb = self.SyntaxTB.structured_traceback(etype, value, [])
+        self._showtraceback(etype, value, stb)
 
     #-------------------------------------------------------------------------
     # Things related to tab completion
@@ -1792,7 +1814,7 @@ class InteractiveShell(Configurable, Magic):
         exposes IPython's processing machinery, the given strings can contain
         magic calls (%magic), special shell access (!cmd), etc.
         """
-
+        
         if isinstance(lines, (list, tuple)):
             lines = '\n'.join(lines)
 
@@ -1912,6 +1934,7 @@ class InteractiveShell(Configurable, Magic):
         try:
             try:
                 self.hooks.pre_runcode_hook()
+                #rprint('Running code') # dbg
                 exec code_obj in self.user_global_ns, self.user_ns
             finally:
                 # Reset our crash handler in place
@@ -2080,12 +2103,12 @@ class InteractiveShell(Configurable, Magic):
     # TODO:  This should be removed when Term is refactored.
     def write(self,data):
         """Write a string to the default output"""
-        IPython.utils.io.Term.cout.write(data)
+        io.Term.cout.write(data)
 
     # TODO:  This should be removed when Term is refactored.
     def write_err(self,data):
         """Write a string to the default error output"""
-        IPython.utils.io.Term.cerr.write(data)
+        io.Term.cerr.write(data)
 
     def ask_yes_no(self,prompt,default=True):
         if self.quiet:
diff --git a/IPython/core/ultratb.py b/IPython/core/ultratb.py
index 7606ab6..8a4cabd 100644
--- a/IPython/core/ultratb.py
+++ b/IPython/core/ultratb.py
@@ -90,12 +90,12 @@ from inspect import getsourcefile, getfile, getmodule,\
 
 # IPython's own modules
 # Modified pdb which doesn't damage IPython's readline handling
-from IPython.utils import PyColorize
 from IPython.core import debugger, ipapi
 from IPython.core.display_trap import DisplayTrap
 from IPython.core.excolors import exception_colors
+from IPython.utils import PyColorize
+from IPython.utils import io
 from IPython.utils.data import uniq_stable
-import IPython.utils.io
 from IPython.utils.warn import info, error
 
 # Globals
@@ -310,7 +310,7 @@ def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
 
 #---------------------------------------------------------------------------
 # Module classes
-class TBTools:
+class TBTools(object):
     """Basic tools used by all traceback printer classes."""
 
     # This attribute us used in globalipapp.py to have stdout used for
@@ -319,6 +319,9 @@ class TBTools:
     # the string 'stdout' which will cause the override to sys.stdout.
     out_stream = None
 
+    # Number of frames to skip when reporting tracebacks
+    tb_offset = 0
+
     def __init__(self,color_scheme = 'NoColor',call_pdb=False):
         # Whether to call the interactive pdb debugger after printing
         # tracebacks or not
@@ -357,6 +360,24 @@ class TBTools:
             self.color_scheme_table.set_active_scheme('NoColor')
             self.Colors = self.color_scheme_table.active_colors
 
+    def text(self, etype, value, tb, tb_offset=None, context=5):
+        """Return formatted traceback.
+
+        Subclasses may override this if they add extra arguments.
+        """
+        tb_list = self.structured_traceback(etype, value, tb,
+                                            tb_offset, context)
+        return '\n'.join(tb_list)
+
+    def structured_traceback(self, etype, evalue, tb, tb_offset=None,
+                             context=5, mode=None):
+        """Return a list of traceback frames.
+
+        Must be implemented by each class.
+        """
+        raise NotImplementedError()
+
+
 #---------------------------------------------------------------------------
 class ListTB(TBTools):
     """Print traceback information from a traceback list, with optional color.
@@ -381,11 +402,12 @@ class ListTB(TBTools):
         TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0)
         
     def __call__(self, etype, value, elist):
-        IPython.utils.io.Term.cout.flush()
-        IPython.utils.io.Term.cerr.write(self.text(etype,value,elist))
-        IPython.utils.io.Term.cerr.write('\n')
+        io.Term.cout.flush()
+        io.Term.cerr.write(self.text(etype, value, elist))
+        io.Term.cerr.write('\n')
 
-    def structured_traceback(self, etype, value, elist, context=5):
+    def structured_traceback(self, etype, value, elist, tb_offset=None,
+                             context=5):
         """Return a color formatted string with the traceback info.
 
         Parameters
@@ -399,28 +421,43 @@ class ListTB(TBTools):
         elist : list
           List of frames, see class docstring for details.
 
+        tb_offset : int, optional
+          Number of frames in the traceback to skip.  If not given, the
+          instance value is used (set in constructor).
+          
+        context : int, optional
+          Number of lines of context information to print.
+
         Returns
         -------
         String with formatted exception.
         """
-
+        tb_offset = self.tb_offset if tb_offset is None else tb_offset
         Colors = self.Colors
-        out_string = []
+        out_list = []
         if elist:
-            out_string.append('Traceback %s(most recent call last)%s:' %
+
+            if tb_offset and len(elist) > tb_offset:
+                elist = elist[tb_offset:]
+            
+            out_list.append('Traceback %s(most recent call last)%s:' %
                                 (Colors.normalEm, Colors.Normal) + '\n')
-            out_string.extend(self._format_list(elist))
-        lines = self._format_exception_only(etype, value)
-        for line in lines[:-1]:
-            out_string.append(" "+line)
-        out_string.append(lines[-1])
-        return out_string
-
-    def text(self, etype, value, elist, context=5):
-        out_string = ListTB.structured_traceback(
-            self, etype, value, elist, context
-        )
-        return ''.join(out_string)
+            out_list.extend(self._format_list(elist))
+        # The exception info should be a single entry in the list.
+        lines = ''.join(self._format_exception_only(etype, value))
+        out_list.append(lines)
+
+        # Note: this code originally read:
+        
+        ## for line in lines[:-1]:
+        ##     out_list.append(" "+line)
+        ## out_list.append(lines[-1])
+
+        # This means it was indenting everything but the last line by a little
+        # bit.  I've disabled this for now, but if we see ugliness somewhre we
+        # can restore it.
+        
+        return out_list
 
     def _format_list(self, extracted_list):
         """Format a list of traceback entry tuples for printing.
@@ -457,6 +494,7 @@ class ListTB(TBTools):
             item = item + '%s    %s%s\n' % (Colors.line, line.strip(),
                                             Colors.Normal)
         list.append(item)
+        #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
         return list
         
     def _format_exception_only(self, etype, value):
@@ -528,6 +566,17 @@ class ListTB(TBTools):
 
         return list
 
+    def get_exception_only(self, etype, value):
+        """Only print the exception type and message, without a traceback.
+        
+        Parameters
+        ----------
+        etype : exception type
+        value : exception value
+        """
+        return ListTB.structured_traceback(self, etype, value, [])
+
+
     def show_exception_only(self, etype, value):
         """Only print the exception type and message, without a traceback.
         
@@ -541,9 +590,9 @@ class ListTB(TBTools):
         if self.out_stream == 'stdout':
             ostream = sys.stdout
         else:
-            ostream = IPython.utils.io.Term.cerr
+            ostream = io.Term.cerr
         ostream.flush()
-        ostream.write(ListTB.text(self, etype, value, []))
+        ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
         ostream.flush()
 
     def _some_str(self, value):
@@ -575,9 +624,12 @@ class VerboseTB(TBTools):
         self.long_header = long_header
         self.include_vars = include_vars
 
-    def structured_traceback(self, etype, evalue, etb, context=5):
+    def structured_traceback(self, etype, evalue, etb, tb_offset=None,
+                             context=5):
         """Return a nice text document describing the traceback."""
 
+        tb_offset = self.tb_offset if tb_offset is None else tb_offset
+
         # some locals
         try:
             etype = etype.__name__
@@ -652,9 +704,9 @@ class VerboseTB(TBTools):
             # Try the default getinnerframes and Alex's: Alex's fixes some
             # problems, but it generates empty tracebacks for console errors
             # (5 blanks lines) where none should be returned.
-            #records = inspect.getinnerframes(etb, context)[self.tb_offset:]
+            #records = inspect.getinnerframes(etb, context)[tb_offset:]
             #print 'python records:', records # dbg
-            records = _fixed_getinnerframes(etb, context,self.tb_offset)
+            records = _fixed_getinnerframes(etb, context, tb_offset)
             #print 'alex   records:', records # dbg
         except:
 
@@ -665,7 +717,7 @@ class VerboseTB(TBTools):
             # So far, I haven't been able to find an isolated example to
             # reproduce the problem.
             inspect_error()
-            traceback.print_exc(file=IPython.utils.io.Term.cerr)
+            traceback.print_exc(file=io.Term.cerr)
             info('\nUnfortunately, your original traceback can not be constructed.\n')
             return ''
 
@@ -702,7 +754,7 @@ class VerboseTB(TBTools):
                 # able to remove this try/except when 2.4 becomes a
                 # requirement.  Bug details at http://python.org/sf/1005466
                 inspect_error()
-                traceback.print_exc(file=IPython.utils.io.Term.cerr)
+                traceback.print_exc(file=io.Term.cerr)
                 info("\nIPython's exception reporting continues...\n")
                 
             if func == '?':
@@ -723,7 +775,7 @@ class VerboseTB(TBTools):
                     # and barfs out. At some point I should dig into this one
                     # and file a bug report about it.
                     inspect_error()
-                    traceback.print_exc(file=IPython.utils.io.Term.cerr)
+                    traceback.print_exc(file=io.Term.cerr)
                     info("\nIPython's exception reporting continues...\n")
                     call = tpl_call_fail % func
 
@@ -869,13 +921,7 @@ class VerboseTB(TBTools):
         # return all our info assembled as a single string
         # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
         return [head] + frames + [''.join(exception[0])]
-
-    def text(self, etype, evalue, etb, context=5):
-        tb_list = VerboseTB.structured_traceback(
-            self, etype, evalue, etb, context
-        )
-        return '\n'.join(tb_list)
-
+    
     def debugger(self,force=False):
         """Call up the pdb debugger if desired, always clean up the tb
         reference.
@@ -923,9 +969,9 @@ class VerboseTB(TBTools):
     def handler(self, info=None):
         (etype, evalue, etb) = info or sys.exc_info()
         self.tb = etb
-        IPython.utils.io.Term.cout.flush()
-        IPython.utils.io.Term.cerr.write(self.text(etype, evalue, etb))
-        IPython.utils.io.Term.cerr.write('\n')
+        io.Term.cout.flush()
+        io.Term.cerr.write(self.text(etype, evalue, etb))
+        io.Term.cerr.write('\n')
 
     # Changed so an instance can just be called as VerboseTB_inst() and print
     # out the right info on its own.
@@ -941,7 +987,7 @@ class VerboseTB(TBTools):
             print "\nKeyboardInterrupt"
 
 #----------------------------------------------------------------------------
-class FormattedTB(VerboseTB,ListTB):
+class FormattedTB(VerboseTB, ListTB):
     """Subclass ListTB but allow calling with a traceback.
 
     It can thus be used as a sys.excepthook for Python > 2.1.
@@ -953,8 +999,8 @@ class FormattedTB(VerboseTB,ListTB):
     occurs with python programs that themselves execute other python code,
     like Python shells).  """
     
-    def __init__(self, mode = 'Plain', color_scheme='Linux',
-                 tb_offset = 0,long_header=0,call_pdb=0,include_vars=0):
+    def __init__(self, mode='Plain', color_scheme='Linux',
+                 tb_offset=0, long_header=0, call_pdb=0, include_vars=0):
 
         # NEVER change the order of this list. Put new modes at the end:
         self.valid_modes = ['Plain','Context','Verbose']
@@ -970,12 +1016,14 @@ class FormattedTB(VerboseTB,ListTB):
         else:
             return None
 
-    def structured_traceback(self, etype, value, tb, context=5, mode=None):
+    def structured_traceback(self, etype, value, tb, tb_offset=None,
+                             context=5, mode=None):
+        tb_offset = self.tb_offset if tb_offset is None else tb_offset
         mode = self.mode if mode is None else mode
         if mode in self.verbose_modes:
             # Verbose modes need a full traceback
             return VerboseTB.structured_traceback(
-                self, etype, value, tb, context
+                self, etype, value, tb, tb_offset, context
             )
         else:
             # We must check the source cache because otherwise we can print
@@ -983,21 +1031,21 @@ class FormattedTB(VerboseTB,ListTB):
             linecache.checkcache()
             # Now we can extract and format the exception
             elist = self._extract_tb(tb)
-            if len(elist) > self.tb_offset:
-                del elist[:self.tb_offset]
             return ListTB.structured_traceback(
-                self, etype, value, elist, context
+                self, etype, value, elist, tb_offset, context
             )
 
-    def text(self, etype, value, tb, context=5, mode=None):
+    def text(self, etype, value, tb, tb_offset=None, context=5, mode=None):
         """Return formatted traceback.
 
         If the optional mode parameter is given, it overrides the current
         mode."""
-        tb_list = FormattedTB.structured_traceback(
-            self, etype, value, tb, context, mode
-        )
+
+        mode = self.mode if mode is None else mode
+        tb_list = self.structured_traceback(etype, value, tb, tb_offset,
+                                            context, mode)
         return '\n'.join(tb_list)
+        
 
     def set_mode(self,mode=None):
         """Switch to the desired mode.
@@ -1056,36 +1104,25 @@ class AutoFormattedTB(FormattedTB):
             if self.out_stream == 'stdout':
                 out = sys.stdout
             else:
-                out = IPython.utils.io.Term.cerr
+                out = io.Term.cerr
         out.flush()
-        if tb_offset is not None:
-            tb_offset, self.tb_offset = self.tb_offset, tb_offset
-            out.write(self.text(etype, evalue, etb))
-            out.write('\n')
-            self.tb_offset = tb_offset
-        else:
-            out.write(self.text(etype, evalue, etb))
-            out.write('\n')
+        out.write(self.text(etype, evalue, etb, tb_offset))
+        out.write('\n')
         out.flush()
+        # FIXME: we should remove the auto pdb behavior from here and leave
+        # that to the clients.
         try:
             self.debugger()
         except KeyboardInterrupt:
             print "\nKeyboardInterrupt"
 
     def structured_traceback(self, etype=None, value=None, tb=None,
-                             context=5, mode=None):
+                             tb_offset=None, context=5, mode=None):
         if etype is None:
             etype,value,tb = sys.exc_info()
         self.tb = tb
         return FormattedTB.structured_traceback(
-            self, etype, value, tb, context, mode
-        )
-
-    def text(self, etype=None, value=None, tb=None, context=5, mode=None):
-        tb_list = AutoFormattedTB.structured_traceback(
-            self, etype, value, tb, context, mode
-        )
-        return '\n'.join(tb_list)
+            self, etype, value, tb, tb_offset, context, mode )
 
 #---------------------------------------------------------------------------
 
@@ -1114,6 +1151,15 @@ class SyntaxTB(ListTB):
         self.last_syntax_error = None
         return e
 
+    def text(self, etype, value, tb, tb_offset=None, context=5):
+        """Return formatted traceback.
+
+        Subclasses may override this if they add extra arguments.
+        """
+        tb_list = self.structured_traceback(etype, value, tb,
+                                            tb_offset, context)
+        return ''.join(tb_list)
+
 #----------------------------------------------------------------------------
 # module testing (minimal)
 if __name__ == "__main__":
diff --git a/IPython/frontend/qt/console/ipython_widget.py b/IPython/frontend/qt/console/ipython_widget.py
index b140e7b..e6e5334 100644
--- a/IPython/frontend/qt/console/ipython_widget.py
+++ b/IPython/frontend/qt/console/ipython_widget.py
@@ -94,7 +94,7 @@ class IPythonWidget(FrontendWidget):
     def execute_file(self, path, hidden=False):
         """ Reimplemented to use the 'run' magic.
         """
-        self.execute('run %s' % path, hidden=hidden)
+        self.execute('%%run %s' % path, hidden=hidden)
 
     #---------------------------------------------------------------------------
     # 'FrontendWidget' protected interface
@@ -109,16 +109,24 @@ class IPythonWidget(FrontendWidget):
         """ Reimplemented for IPython-style traceback formatting.
         """
         content = msg['content']
-        traceback_lines = content['traceback'][:]
-        traceback = ''.join(traceback_lines)
-        traceback = traceback.replace(' ', ' ')
-        traceback = traceback.replace('\n', '<br/>')
 
-        ename = content['ename']
-        ename_styled = '<span class="error">%s</span>' % ename
-        traceback = traceback.replace(ename, ename_styled)
+        traceback = '\n'.join(content['traceback'])
 
-        self._append_html(traceback)
+        if 0:
+            # FIXME: for now, tracebacks come as plain text, so we can't use
+            # the html renderer yet.  Once we refactor ultratb to produce
+            # properly styled tracebacks, this branch should be the default
+            traceback = traceback.replace(' ', '&nbsp;')
+            traceback = traceback.replace('\n', '<br/>')
+
+            ename = content['ename']
+            ename_styled = '<span class="error">%s</span>' % ename
+            traceback = traceback.replace(ename, ename_styled)
+
+            self._append_html(traceback)
+        else:
+            # This is the fallback for now, using plain text with ansi escapes
+            self._append_plain_text(traceback)
 
     def _process_execute_payload(self, item):
         """ Reimplemented to handle %edit and paging payloads.
diff --git a/IPython/frontend/qt/console/scripts/ipythonqt.py b/IPython/frontend/qt/console/scripts/ipythonqt.py
index cdf50a2..1a8033c 100755
--- a/IPython/frontend/qt/console/scripts/ipythonqt.py
+++ b/IPython/frontend/qt/console/scripts/ipythonqt.py
@@ -61,6 +61,10 @@ def main():
             kernel_manager.start_kernel()
     kernel_manager.start_channels()
 
+    # FIXME: this is a hack, set colors to lightbg by default in qt terminal
+    # unconditionally, regardless of user settings in config files.
+    kernel_manager.xreq_channel.execute("%colors lightbg")
+
     # Launch the application.
     app = QtGui.QApplication([])
     if args.pure:
diff --git a/IPython/utils/io.py b/IPython/utils/io.py
index e38445b..e32c49f 100644
--- a/IPython/utils/io.py
+++ b/IPython/utils/io.py
@@ -13,7 +13,6 @@ IO related utilities.
 #-----------------------------------------------------------------------------
 # Imports
 #-----------------------------------------------------------------------------
-
 import sys
 import tempfile
 
@@ -278,4 +277,10 @@ def temp_pyfile(src, ext='.py'):
     return fname, f
 
 
-
+def rprint(*info):
+    """Raw print to sys.__stderr__"""
+    
+    for item in info:
+        print >> sys.__stderr__, item,
+    print >> sys.__stderr__
+    sys.__stderr__.flush()
diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py
index bcdf342..4839785 100755
--- a/IPython/zmq/ipkernel.py
+++ b/IPython/zmq/ipkernel.py
@@ -66,6 +66,9 @@ class Kernel(Configurable):
         self.shell.displayhook.session = self.session
         self.shell.displayhook.pub_socket = self.pub_socket
 
+        # TMP - hack while developing
+        self.shell._reply_content = None
+
         # Build dict of handlers for message types
         msg_types = [ 'execute_request', 'complete_request', 
                       'object_info_request', 'prompt_request',
@@ -156,19 +159,20 @@ class Kernel(Configurable):
             sys.stdout.set_parent(parent)
             sys.stderr.set_parent(parent)
 
+            # FIXME: runlines calls the exception handler itself.  We should
+            # clean this up.
+            self.shell._reply_content = None
             self.shell.runlines(code)
         except:
+            # FIXME: this code right now isn't being used yet by default,
+            # because the runlines() call above directly fires off exception
+            # reporting.  This code, therefore, is only active in the scenario
+            # where runlines itself has an unhandled exception.  We need to
+            # uniformize this, for all exception construction to come from a
+            # single location in the codbase.
             etype, evalue, tb = sys.exc_info()
-            tb = traceback.format_exception(etype, evalue, tb)
-            exc_content = {
-                u'status' : u'error',
-                u'traceback' : tb,
-                u'ename' : unicode(etype.__name__),
-                u'evalue' : unicode(evalue)
-            }
-            exc_msg = self.session.msg(u'pyerr', exc_content, parent)
-            self.pub_socket.send_json(exc_msg)
-            reply_content = exc_content
+            tb_list = traceback.format_exception(etype, evalue, tb)
+            reply_content = self.shell._showtraceback(etype, evalue, tb_list)
         else:
             payload = self.shell.payload_manager.read_payload()
             # Be agressive about clearing the payload because we don't want
@@ -185,6 +189,11 @@ class Kernel(Configurable):
                        'input_sep'     : self.shell.displayhook.input_sep}
         reply_content['next_prompt'] = next_prompt
 
+        # TMP - fish exception info out of shell, possibly left there by
+        # runlines
+        if self.shell._reply_content is not None:
+            reply_content.update(self.shell._reply_content)
+
         # Flush output before sending the reply.
         sys.stderr.flush()
         sys.stdout.flush()
diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py
index ecbf85e..0e2c1a8 100644
--- a/IPython/zmq/zmqshell.py
+++ b/IPython/zmq/zmqshell.py
@@ -8,6 +8,7 @@ from IPython.core.interactiveshell import (
 )
 from IPython.core.displayhook import DisplayHook
 from IPython.core.macro import Macro
+from IPython.utils.io import rprint
 from IPython.utils.path import get_py_filename
 from IPython.utils.text import StringTypes
 from IPython.utils.traitlets import Instance, Type, Dict
@@ -359,7 +360,30 @@ class ZMQInteractiveShell(InteractiveShell):
         self.payload_manager.write_payload(payload)
 
 
-InteractiveShellABC.register(ZMQInteractiveShell)
+    def _showtraceback(self, etype, evalue, stb):
 
+        exc_content = {
+            u'status' : u'error',
+            u'traceback' : stb,
+            u'ename' : unicode(etype.__name__),
+            u'evalue' : unicode(evalue)
+        }
 
+        dh = self.displayhook
+        exc_msg = dh.session.msg(u'pyerr', exc_content, dh.parent_header)
+        # Send exception info over pub socket for other clients than the caller
+        # to pick up
+        dh.pub_socket.send_json(exc_msg)
+
+        # FIXME - Hack: store exception info in shell object.  Right now, the
+        # caller is reading this info after the fact, we need to fix this logic
+        # to remove this hack.
+        self._reply_content = exc_content
+        # /FIXME
+        
+        return exc_content
 
+    def runlines(self, lines, clean=False):
+        return InteractiveShell.runlines(self, lines, clean)
+
+InteractiveShellABC.register(ZMQInteractiveShell)