def test_capture_out():
    """ A simple test to see if we can execute a process and get the output.
    """
    s = StringIO()
    p = PipedProcess('echo 1', out_callback=s.write, )
    p.start()
    p.join()
    result = s.getvalue().rstrip()
    assert result == '1'
def test_kill():
    """ Check that we can kill a process, and its subprocess.
    """
    s = StringIO()
    p = PipedProcess(sys.executable + ' -c "a = raw_input();"', 
                            out_callback=s.write, )
    p.start()
    while not hasattr(p, 'process'):
        sleep(0.1)
    p.process.kill() 
    assert p.process.poll() is not None
Beispiel #3
0
def test_kill():
    """ Check that we can kill a process, and its subprocess.
    """
    s = StringIO()
    p = PipedProcess(
        sys.executable + ' -c "a = raw_input();"',
        out_callback=s.write,
    )
    p.start()
    while not hasattr(p, 'process'):
        sleep(0.1)
    p.process.kill()
    assert p.process.poll() is not None
def test_io():
    """ Checks that we can send characters on stdin to the process.
    """
    s = StringIO()
    p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"', 
                            out_callback=s.write, )
    p.start()
    test_string = '12345\n'
    while not hasattr(p, 'process'):
        sleep(0.1)
    p.process.stdin.write(test_string)
    p.join()
    result = s.getvalue()
    assert result == test_string
Beispiel #5
0
    def system_call(self, command_string):
        self._input_state = 'subprocess'
        event_loop = wx.EventLoop()

        def _end_system_call():
            self._input_state = 'buffering'
            self._running_process = False
            event_loop.Exit()

        self._running_process = PipedProcess(command_string,
                                             out_callback=self.buffered_write,
                                             end_callback=_end_system_call)
        self._running_process.start()
        # XXX: Running a separate event_loop. Ugly.
        event_loop.Run()
        # Be sure to flush the buffer.
        self._buffer_flush(event=None)
    def system_call(self, command_string):
        self._input_state = "subprocess"
        event_loop = wx.EventLoop()

        def _end_system_call():
            self._input_state = "buffering"
            self._running_process = False
            event_loop.Exit()

        self._running_process = PipedProcess(
            command_string, out_callback=self.buffered_write, end_callback=_end_system_call
        )
        self._running_process.start()
        # XXX: Running a separate event_loop. Ugly.
        event_loop.Run()
        # Be sure to flush the buffer.
        self._buffer_flush(event=None)
Beispiel #7
0
def test_capture_out():
    """ A simple test to see if we can execute a process and get the output.
    """
    s = StringIO()
    p = PipedProcess(
        'echo 1',
        out_callback=s.write,
    )
    p.start()
    p.join()
    result = s.getvalue().rstrip()
    assert result == '1'
Beispiel #8
0
def test_io():
    """ Checks that we can send characters on stdin to the process.
    """
    s = StringIO()
    p = PipedProcess(
        sys.executable + ' -c "a = raw_input(); print a"',
        out_callback=s.write,
    )
    p.start()
    test_string = '12345\n'
    while not hasattr(p, 'process'):
        sleep(0.1)
    p.process.stdin.write(test_string)
    p.join()
    result = s.getvalue()
    assert result == test_string
Beispiel #9
0
class WxController(ConsoleWidget, PrefilterFrontEnd):
    """Classes to provide a Wx frontend to the
    IPython.kernel.core.interpreter.

    This class inherits from ConsoleWidget, that provides a console-like
    widget to provide a text-rendering widget suitable for a terminal.
    """

    output_prompt_template = string.Template(prompt_out)

    input_prompt_template = string.Template(prompt_in1)

    # Print debug info on what is happening to the console.
    debug = False

    # The title of the terminal, as captured through the ANSI escape
    # sequences.
    def _set_title(self, title):
        return self.Parent.SetTitle(title)

    def _get_title(self):
        return self.Parent.GetTitle()

    title = property(_get_title, _set_title)

    # The buffer being edited.
    # We are duplicating the definition here because of multiple
    # inheritence
    def _set_input_buffer(self, string):
        ConsoleWidget._set_input_buffer(self, string)
        self._colorize_input_buffer()

    def _get_input_buffer(self):
        """ Returns the text in current edit buffer.
        """
        return ConsoleWidget._get_input_buffer(self)

    input_buffer = property(_get_input_buffer, _set_input_buffer)

    #--------------------------------------------------------------------------
    # Private Attributes
    #--------------------------------------------------------------------------

    # A flag governing the behavior of the input. Can be:
    #
    #       'readline' for readline-like behavior with a prompt
    #            and an edit buffer.
    #       'raw_input' similar to readline, but triggered by a raw-input
    #           call. Can be used by subclasses to act differently.
    #       'subprocess' for sending the raw input directly to a
    #           subprocess.
    #       'buffering' for buffering of the input, that will be used
    #           when the input state switches back to another state.
    _input_state = 'readline'

    # Attribute to store reference to the pipes of a subprocess, if we
    # are running any.
    _running_process = False

    # A queue for writing fast streams to the screen without flooding the
    # event loop
    _out_buffer = []

    # A lock to lock the _out_buffer to make sure we don't empty it
    # while it is being swapped
    _out_buffer_lock = Lock()

    # The different line markers used to higlight the prompts.
    _markers = dict()

    #--------------------------------------------------------------------------
    # Public API
    #--------------------------------------------------------------------------

    def __init__(self,
                 parent,
                 id=wx.ID_ANY,
                 pos=wx.DefaultPosition,
                 size=wx.DefaultSize,
                 style=wx.CLIP_CHILDREN | wx.WANTS_CHARS,
                 *args,
                 **kwds):
        """ Create Shell instance.
        """
        ConsoleWidget.__init__(self, parent, id, pos, size, style)
        PrefilterFrontEnd.__init__(self, **kwds)

        # Stick in our own raw_input:
        self.ipython0.raw_input = self.raw_input

        # Marker for complete buffer.
        self.MarkerDefine(_COMPLETE_BUFFER_MARKER,
                          stc.STC_MARK_BACKGROUND,
                          background=_COMPLETE_BUFFER_BG)
        # Marker for current input buffer.
        self.MarkerDefine(_INPUT_MARKER,
                          stc.STC_MARK_BACKGROUND,
                          background=_INPUT_BUFFER_BG)
        # Marker for tracebacks.
        self.MarkerDefine(_ERROR_MARKER,
                          stc.STC_MARK_BACKGROUND,
                          background=_ERROR_BG)

        # A time for flushing the write buffer
        BUFFER_FLUSH_TIMER_ID = 100
        self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
        wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)

        if 'debug' in kwds:
            self.debug = kwds['debug']
            kwds.pop('debug')

        # Inject self in namespace, for debug
        if self.debug:
            self.shell.user_ns['self'] = self
        # Inject our own raw_input in namespace
        self.shell.user_ns['raw_input'] = self.raw_input

    def raw_input(self, prompt=''):
        """ A replacement from python's raw_input.
        """
        self.new_prompt(prompt)
        self._input_state = 'raw_input'
        if hasattr(self, '_cursor'):
            del self._cursor
        self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
        self.__old_on_enter = self._on_enter
        event_loop = wx.EventLoop()

        def my_on_enter():
            event_loop.Exit()

        self._on_enter = my_on_enter
        # XXX: Running a separate event_loop. Ugly.
        event_loop.Run()
        self._on_enter = self.__old_on_enter
        self._input_state = 'buffering'
        self._cursor = wx.BusyCursor()
        return self.input_buffer.rstrip('\n')

    def system_call(self, command_string):
        self._input_state = 'subprocess'
        event_loop = wx.EventLoop()

        def _end_system_call():
            self._input_state = 'buffering'
            self._running_process = False
            event_loop.Exit()

        self._running_process = PipedProcess(command_string,
                                             out_callback=self.buffered_write,
                                             end_callback=_end_system_call)
        self._running_process.start()
        # XXX: Running a separate event_loop. Ugly.
        event_loop.Run()
        # Be sure to flush the buffer.
        self._buffer_flush(event=None)

    def do_calltip(self):
        """ Analyse current and displays useful calltip for it.
        """
        if self.debug:
            print >> sys.__stdout__, "do_calltip"
        separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
        symbol = self.input_buffer
        symbol_string = separators.split(symbol)[-1]
        base_symbol_string = symbol_string.split('.')[0]
        if base_symbol_string in self.shell.user_ns:
            symbol = self.shell.user_ns[base_symbol_string]
        elif base_symbol_string in self.shell.user_global_ns:
            symbol = self.shell.user_global_ns[base_symbol_string]
        elif base_symbol_string in __builtin__.__dict__:
            symbol = __builtin__.__dict__[base_symbol_string]
        else:
            return False
        try:
            for name in symbol_string.split('.')[1:] + ['__doc__']:
                symbol = getattr(symbol, name)
            self.AutoCompCancel()
            # Check that the symbol can indeed be converted to a string:
            symbol += ''
            wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
        except:
            # The retrieve symbol couldn't be converted to a string
            pass

    def _popup_completion(self, create=False):
        """ Updates the popup completion menu if it exists. If create is 
            true, open the menu.
        """
        if self.debug:
            print >> sys.__stdout__, "_popup_completion"
        line = self.input_buffer
        if (self.AutoCompActive() and line and not line[-1] == '.') \
                    or create==True:
            suggestion, completions = self.complete(line)
            offset = 0
            if completions:
                complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
                residual = complete_sep.split(line)[-1]
                offset = len(residual)
                self.pop_completion(completions, offset=offset)
                if self.debug:
                    print >> sys.__stdout__, completions

    def buffered_write(self, text):
        """ A write method for streams, that caches the stream in order
            to avoid flooding the event loop.

            This can be called outside of the main loop, in separate
            threads.
        """
        self._out_buffer_lock.acquire()
        self._out_buffer.append(text)
        self._out_buffer_lock.release()
        if not self._buffer_flush_timer.IsRunning():
            wx.CallAfter(self._buffer_flush_timer.Start,
                         milliseconds=100,
                         oneShot=True)

    #--------------------------------------------------------------------------
    # LineFrontEnd interface
    #--------------------------------------------------------------------------

    def execute(self, python_string, raw_string=None):
        self._input_state = 'buffering'
        self.CallTipCancel()
        self._cursor = wx.BusyCursor()
        if raw_string is None:
            raw_string = python_string
        end_line = self.current_prompt_line \
                        + max(1,  len(raw_string.split('\n'))-1)
        for i in range(self.current_prompt_line, end_line):
            if i in self._markers:
                self.MarkerDeleteHandle(self._markers[i])
            self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
        # Use a callafter to update the display robustly under windows
        def callback():
            self.GotoPos(self.GetLength())
            PrefilterFrontEnd.execute(self,
                                      python_string,
                                      raw_string=raw_string)

        wx.CallAfter(callback)

    def save_output_hooks(self):
        self.__old_raw_input = __builtin__.raw_input
        PrefilterFrontEnd.save_output_hooks(self)

    def capture_output(self):
        self.SetLexer(stc.STC_LEX_NULL)
        PrefilterFrontEnd.capture_output(self)
        __builtin__.raw_input = self.raw_input

    def release_output(self):
        __builtin__.raw_input = self.__old_raw_input
        PrefilterFrontEnd.release_output(self)
        self.SetLexer(stc.STC_LEX_PYTHON)

    def after_execute(self):
        PrefilterFrontEnd.after_execute(self)
        # Clear the wait cursor
        if hasattr(self, '_cursor'):
            del self._cursor
        self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))

    def show_traceback(self):
        start_line = self.GetCurrentLine()
        PrefilterFrontEnd.show_traceback(self)
        self.ProcessEvent(wx.PaintEvent())
        #wx.Yield()
        for i in range(start_line, self.GetCurrentLine()):
            self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)

    #--------------------------------------------------------------------------
    # FrontEndBase interface
    #--------------------------------------------------------------------------

    def render_error(self, e):
        start_line = self.GetCurrentLine()
        self.write('\n' + e + '\n')
        for i in range(start_line, self.GetCurrentLine()):
            self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)

    #--------------------------------------------------------------------------
    # ConsoleWidget interface
    #--------------------------------------------------------------------------

    def new_prompt(self, prompt):
        """ Display a new prompt, and start a new input buffer.
        """
        self._input_state = 'readline'
        ConsoleWidget.new_prompt(self, prompt)
        i = self.current_prompt_line
        self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)

    def write(self, *args, **kwargs):
        # Avoid multiple inheritence, be explicit about which
        # parent method class gets called
        ConsoleWidget.write(self, *args, **kwargs)

    def _on_key_down(self, event, skip=True):
        """ Capture the character events, let the parent
            widget handle them, and put our logic afterward.
        """
        # FIXME: This method needs to be broken down in smaller ones.
        current_line_number = self.GetCurrentLine()
        if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
            # Capture Control-C
            if self._input_state == 'subprocess':
                if self.debug:
                    print >> sys.__stderr__, 'Killing running process'
                if hasattr(self._running_process, 'process'):
                    self._running_process.process.kill()
            elif self._input_state == 'buffering':
                if self.debug:
                    print >> sys.__stderr__, 'Raising KeyboardInterrupt'
                raise KeyboardInterrupt
                # XXX: We need to make really sure we
                # get back to a prompt.
        elif self._input_state == 'subprocess' and (
            (event.KeyCode < 256 and not event.ControlDown()) or
            (event.KeyCode in (ord('d'), ord('D')) and event.ControlDown())):
            #  We are running a process, we redirect keys.
            ConsoleWidget._on_key_down(self, event, skip=skip)
            char = chr(event.KeyCode)
            # Deal with some inconsistency in wx keycodes:
            if char == '\r':
                char = '\n'
            elif not event.ShiftDown():
                char = char.lower()
            if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
                char = '\04'
            self._running_process.process.stdin.write(char)
            self._running_process.process.stdin.flush()
        elif event.KeyCode in (ord('('), 57, 53):
            # Calltips
            event.Skip()
            self.do_calltip()
        elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
            event.Skip()
            if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
                wx.CallAfter(self._popup_completion, create=True)
            elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
                                       wx.WXK_RIGHT, wx.WXK_ESCAPE):
                wx.CallAfter(self._popup_completion)
        else:
            # Up history
            if event.KeyCode == wx.WXK_UP and (
                (current_line_number == self.current_prompt_line
                 and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN))
                    or event.ControlDown()):
                new_buffer = self.get_history_previous(self.input_buffer)
                if new_buffer is not None:
                    self.input_buffer = new_buffer
                    if self.GetCurrentLine() > self.current_prompt_line:
                        # Go to first line, for seemless history up.
                        self.GotoPos(self.current_prompt_pos)
            # Down history
            elif event.KeyCode == wx.WXK_DOWN and (
                (current_line_number == self.LineCount - 1 and event.Modifiers
                 in (wx.MOD_NONE, wx.MOD_WIN)) or event.ControlDown()):
                new_buffer = self.get_history_next()
                if new_buffer is not None:
                    self.input_buffer = new_buffer
            # Tab-completion
            elif event.KeyCode == ord('\t'):
                current_line, current_line_number = self.CurLine
                if not re.match(r'^\s*$', current_line):
                    self.complete_current_input()
                    if self.AutoCompActive():
                        wx.CallAfter(self._popup_completion, create=True)
                else:
                    event.Skip()
            else:
                ConsoleWidget._on_key_down(self, event, skip=skip)

    def _on_key_up(self, event, skip=True):
        """ Called when any key is released.
        """
        if event.KeyCode in (59, ord('.')):
            # Intercepting '.'
            event.Skip()
            wx.CallAfter(self._popup_completion, create=True)
        else:
            ConsoleWidget._on_key_up(self, event, skip=skip)

    def _on_enter(self):
        """ Called on return key down, in readline input_state.
        """
        if self.debug:
            print >> sys.__stdout__, repr(self.input_buffer)
        PrefilterFrontEnd._on_enter(self)

    #--------------------------------------------------------------------------
    # EditWindow API
    #--------------------------------------------------------------------------

    def OnUpdateUI(self, event):
        """ Override the OnUpdateUI of the EditWindow class, to prevent 
            syntax highlighting both for faster redraw, and for more
            consistent look and feel.
        """
        if not self._input_state == 'readline':
            ConsoleWidget.OnUpdateUI(self, event)

    #--------------------------------------------------------------------------
    # Private API
    #--------------------------------------------------------------------------

    def _buffer_flush(self, event):
        """ Called by the timer to flush the write buffer.
            
            This is always called in the mainloop, by the wx timer.
        """
        self._out_buffer_lock.acquire()
        _out_buffer = self._out_buffer
        self._out_buffer = []
        self._out_buffer_lock.release()
        self.write(''.join(_out_buffer), refresh=False)

    def _colorize_input_buffer(self):
        """ Keep the input buffer lines at a bright color.
        """
        if not self._input_state in ('readline', 'raw_input'):
            return
        end_line = self.GetCurrentLine()
        if not sys.platform == 'win32':
            end_line += 1
        for i in range(self.current_prompt_line, end_line):
            if i in self._markers:
                self.MarkerDeleteHandle(self._markers[i])
            self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
class WxController(ConsoleWidget, PrefilterFrontEnd):
    """Classes to provide a Wx frontend to the
    IPython.kernel.core.interpreter.

    This class inherits from ConsoleWidget, that provides a console-like
    widget to provide a text-rendering widget suitable for a terminal.
    """

    output_prompt_template = string.Template(prompt_out)

    input_prompt_template = string.Template(prompt_in1)

    # Print debug info on what is happening to the console.
    debug = False

    # The title of the terminal, as captured through the ANSI escape
    # sequences.
    def _set_title(self, title):
        return self.Parent.SetTitle(title)

    def _get_title(self):
        return self.Parent.GetTitle()

    title = property(_get_title, _set_title)

    # The buffer being edited.
    # We are duplicating the definition here because of multiple
    # inheritence
    def _set_input_buffer(self, string):
        ConsoleWidget._set_input_buffer(self, string)
        self._colorize_input_buffer()

    def _get_input_buffer(self):
        """ Returns the text in current edit buffer.
        """
        return ConsoleWidget._get_input_buffer(self)

    input_buffer = property(_get_input_buffer, _set_input_buffer)

    # --------------------------------------------------------------------------
    # Private Attributes
    # --------------------------------------------------------------------------

    # A flag governing the behavior of the input. Can be:
    #
    #       'readline' for readline-like behavior with a prompt
    #            and an edit buffer.
    #       'raw_input' similar to readline, but triggered by a raw-input
    #           call. Can be used by subclasses to act differently.
    #       'subprocess' for sending the raw input directly to a
    #           subprocess.
    #       'buffering' for buffering of the input, that will be used
    #           when the input state switches back to another state.
    _input_state = "readline"

    # Attribute to store reference to the pipes of a subprocess, if we
    # are running any.
    _running_process = False

    # A queue for writing fast streams to the screen without flooding the
    # event loop
    _out_buffer = []

    # A lock to lock the _out_buffer to make sure we don't empty it
    # while it is being swapped
    _out_buffer_lock = Lock()

    # The different line markers used to higlight the prompts.
    _markers = dict()

    # --------------------------------------------------------------------------
    # Public API
    # --------------------------------------------------------------------------

    def __init__(
        self,
        parent,
        id=wx.ID_ANY,
        pos=wx.DefaultPosition,
        size=wx.DefaultSize,
        style=wx.CLIP_CHILDREN | wx.WANTS_CHARS,
        *args,
        **kwds
    ):
        """ Create Shell instance.
        """
        ConsoleWidget.__init__(self, parent, id, pos, size, style)
        PrefilterFrontEnd.__init__(self, **kwds)

        # Stick in our own raw_input:
        self.ipython0.raw_input = self.raw_input

        # Marker for complete buffer.
        self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND, background=_COMPLETE_BUFFER_BG)
        # Marker for current input buffer.
        self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND, background=_INPUT_BUFFER_BG)
        # Marker for tracebacks.
        self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND, background=_ERROR_BG)

        # A time for flushing the write buffer
        BUFFER_FLUSH_TIMER_ID = 100
        self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
        wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)

        if "debug" in kwds:
            self.debug = kwds["debug"]
            kwds.pop("debug")

        # Inject self in namespace, for debug
        if self.debug:
            self.shell.user_ns["self"] = self
        # Inject our own raw_input in namespace
        self.shell.user_ns["raw_input"] = self.raw_input

    def raw_input(self, prompt=""):
        """ A replacement from python's raw_input.
        """
        self.new_prompt(prompt)
        self._input_state = "raw_input"
        if hasattr(self, "_cursor"):
            del self._cursor
        self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
        self.__old_on_enter = self._on_enter
        event_loop = wx.EventLoop()

        def my_on_enter():
            event_loop.Exit()

        self._on_enter = my_on_enter
        # XXX: Running a separate event_loop. Ugly.
        event_loop.Run()
        self._on_enter = self.__old_on_enter
        self._input_state = "buffering"
        self._cursor = wx.BusyCursor()
        return self.input_buffer.rstrip("\n")

    def system_call(self, command_string):
        self._input_state = "subprocess"
        event_loop = wx.EventLoop()

        def _end_system_call():
            self._input_state = "buffering"
            self._running_process = False
            event_loop.Exit()

        self._running_process = PipedProcess(
            command_string, out_callback=self.buffered_write, end_callback=_end_system_call
        )
        self._running_process.start()
        # XXX: Running a separate event_loop. Ugly.
        event_loop.Run()
        # Be sure to flush the buffer.
        self._buffer_flush(event=None)

    def do_calltip(self):
        """ Analyse current and displays useful calltip for it.
        """
        if self.debug:
            print >>sys.__stdout__, "do_calltip"
        separators = re.compile("[\s\{\}\[\]\(\)\= ,:]")
        symbol = self.input_buffer
        symbol_string = separators.split(symbol)[-1]
        base_symbol_string = symbol_string.split(".")[0]
        if base_symbol_string in self.shell.user_ns:
            symbol = self.shell.user_ns[base_symbol_string]
        elif base_symbol_string in self.shell.user_global_ns:
            symbol = self.shell.user_global_ns[base_symbol_string]
        elif base_symbol_string in __builtin__.__dict__:
            symbol = __builtin__.__dict__[base_symbol_string]
        else:
            return False
        try:
            for name in symbol_string.split(".")[1:] + ["__doc__"]:
                symbol = getattr(symbol, name)
            self.AutoCompCancel()
            # Check that the symbol can indeed be converted to a string:
            symbol += ""
            wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
        except:
            # The retrieve symbol couldn't be converted to a string
            pass

    def _popup_completion(self, create=False):
        """ Updates the popup completion menu if it exists. If create is 
            true, open the menu.
        """
        if self.debug:
            print >>sys.__stdout__, "_popup_completion"
        line = self.input_buffer
        if (self.AutoCompActive() and line and not line[-1] == ".") or create == True:
            suggestion, completions = self.complete(line)
            offset = 0
            if completions:
                complete_sep = re.compile("[\s\{\}\[\]\(\)\= ,:]")
                residual = complete_sep.split(line)[-1]
                offset = len(residual)
                self.pop_completion(completions, offset=offset)
                if self.debug:
                    print >>sys.__stdout__, completions

    def buffered_write(self, text):
        """ A write method for streams, that caches the stream in order
            to avoid flooding the event loop.

            This can be called outside of the main loop, in separate
            threads.
        """
        self._out_buffer_lock.acquire()
        self._out_buffer.append(text)
        self._out_buffer_lock.release()
        if not self._buffer_flush_timer.IsRunning():
            wx.CallAfter(self._buffer_flush_timer.Start, milliseconds=100, oneShot=True)

    # --------------------------------------------------------------------------
    # LineFrontEnd interface
    # --------------------------------------------------------------------------

    def execute(self, python_string, raw_string=None):
        self._input_state = "buffering"
        self.CallTipCancel()
        self._cursor = wx.BusyCursor()
        if raw_string is None:
            raw_string = python_string
        end_line = self.current_prompt_line + max(1, len(raw_string.split("\n")) - 1)
        for i in range(self.current_prompt_line, end_line):
            if i in self._markers:
                self.MarkerDeleteHandle(self._markers[i])
            self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
        # Use a callafter to update the display robustly under windows
        def callback():
            self.GotoPos(self.GetLength())
            PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)

        wx.CallAfter(callback)

    def save_output_hooks(self):
        self.__old_raw_input = __builtin__.raw_input
        PrefilterFrontEnd.save_output_hooks(self)

    def capture_output(self):
        self.SetLexer(stc.STC_LEX_NULL)
        PrefilterFrontEnd.capture_output(self)
        __builtin__.raw_input = self.raw_input

    def release_output(self):
        __builtin__.raw_input = self.__old_raw_input
        PrefilterFrontEnd.release_output(self)
        self.SetLexer(stc.STC_LEX_PYTHON)

    def after_execute(self):
        PrefilterFrontEnd.after_execute(self)
        # Clear the wait cursor
        if hasattr(self, "_cursor"):
            del self._cursor
        self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))

    def show_traceback(self):
        start_line = self.GetCurrentLine()
        PrefilterFrontEnd.show_traceback(self)
        self.ProcessEvent(wx.PaintEvent())
        # wx.Yield()
        for i in range(start_line, self.GetCurrentLine()):
            self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)

    # --------------------------------------------------------------------------
    # FrontEndBase interface
    # --------------------------------------------------------------------------

    def render_error(self, e):
        start_line = self.GetCurrentLine()
        self.write("\n" + e + "\n")
        for i in range(start_line, self.GetCurrentLine()):
            self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)

    # --------------------------------------------------------------------------
    # ConsoleWidget interface
    # --------------------------------------------------------------------------

    def new_prompt(self, prompt):
        """ Display a new prompt, and start a new input buffer.
        """
        self._input_state = "readline"
        ConsoleWidget.new_prompt(self, prompt)
        i = self.current_prompt_line
        self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)

    def write(self, *args, **kwargs):
        # Avoid multiple inheritence, be explicit about which
        # parent method class gets called
        ConsoleWidget.write(self, *args, **kwargs)

    def _on_key_down(self, event, skip=True):
        """ Capture the character events, let the parent
            widget handle them, and put our logic afterward.
        """
        # FIXME: This method needs to be broken down in smaller ones.
        current_line_number = self.GetCurrentLine()
        if event.KeyCode in (ord("c"), ord("C")) and event.ControlDown():
            # Capture Control-C
            if self._input_state == "subprocess":
                if self.debug:
                    print >>sys.__stderr__, "Killing running process"
                if hasattr(self._running_process, "process"):
                    self._running_process.process.kill()
            elif self._input_state == "buffering":
                if self.debug:
                    print >>sys.__stderr__, "Raising KeyboardInterrupt"
                raise KeyboardInterrupt
                # XXX: We need to make really sure we
                # get back to a prompt.
        elif self._input_state == "subprocess" and (
            (event.KeyCode < 256 and not event.ControlDown())
            or (event.KeyCode in (ord("d"), ord("D")) and event.ControlDown())
        ):
            #  We are running a process, we redirect keys.
            ConsoleWidget._on_key_down(self, event, skip=skip)
            char = chr(event.KeyCode)
            # Deal with some inconsistency in wx keycodes:
            if char == "\r":
                char = "\n"
            elif not event.ShiftDown():
                char = char.lower()
            if event.ControlDown() and event.KeyCode in (ord("d"), ord("D")):
                char = "\04"
            self._running_process.process.stdin.write(char)
            self._running_process.process.stdin.flush()
        elif event.KeyCode in (ord("("), 57, 53):
            # Calltips
            event.Skip()
            self.do_calltip()
        elif self.AutoCompActive() and not event.KeyCode == ord("\t"):
            event.Skip()
            if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
                wx.CallAfter(self._popup_completion, create=True)
            elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_ESCAPE):
                wx.CallAfter(self._popup_completion)
        else:
            # Up history
            if event.KeyCode == wx.WXK_UP and (
                (current_line_number == self.current_prompt_line and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN))
                or event.ControlDown()
            ):
                new_buffer = self.get_history_previous(self.input_buffer)
                if new_buffer is not None:
                    self.input_buffer = new_buffer
                    if self.GetCurrentLine() > self.current_prompt_line:
                        # Go to first line, for seemless history up.
                        self.GotoPos(self.current_prompt_pos)
            # Down history
            elif event.KeyCode == wx.WXK_DOWN and (
                (current_line_number == self.LineCount - 1 and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN))
                or event.ControlDown()
            ):
                new_buffer = self.get_history_next()
                if new_buffer is not None:
                    self.input_buffer = new_buffer
            # Tab-completion
            elif event.KeyCode == ord("\t"):
                current_line, current_line_number = self.CurLine
                if not re.match(r"^\s*$", current_line):
                    self.complete_current_input()
                    if self.AutoCompActive():
                        wx.CallAfter(self._popup_completion, create=True)
                else:
                    event.Skip()
            else:
                ConsoleWidget._on_key_down(self, event, skip=skip)

    def _on_key_up(self, event, skip=True):
        """ Called when any key is released.
        """
        if event.KeyCode in (59, ord(".")):
            # Intercepting '.'
            event.Skip()
            wx.CallAfter(self._popup_completion, create=True)
        else:
            ConsoleWidget._on_key_up(self, event, skip=skip)

    def _on_enter(self):
        """ Called on return key down, in readline input_state.
        """
        if self.debug:
            print >>sys.__stdout__, repr(self.input_buffer)
        PrefilterFrontEnd._on_enter(self)

    # --------------------------------------------------------------------------
    # EditWindow API
    # --------------------------------------------------------------------------

    def OnUpdateUI(self, event):
        """ Override the OnUpdateUI of the EditWindow class, to prevent 
            syntax highlighting both for faster redraw, and for more
            consistent look and feel.
        """
        if not self._input_state == "readline":
            ConsoleWidget.OnUpdateUI(self, event)

    # --------------------------------------------------------------------------
    # Private API
    # --------------------------------------------------------------------------

    def _buffer_flush(self, event):
        """ Called by the timer to flush the write buffer.
            
            This is always called in the mainloop, by the wx timer.
        """
        self._out_buffer_lock.acquire()
        _out_buffer = self._out_buffer
        self._out_buffer = []
        self._out_buffer_lock.release()
        self.write("".join(_out_buffer), refresh=False)

    def _colorize_input_buffer(self):
        """ Keep the input buffer lines at a bright color.
        """
        if not self._input_state in ("readline", "raw_input"):
            return
        end_line = self.GetCurrentLine()
        if not sys.platform == "win32":
            end_line += 1
        for i in range(self.current_prompt_line, end_line):
            if i in self._markers:
                self.MarkerDeleteHandle(self._markers[i])
            self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)