diff --git a/IPython/frontend/frontendbase.py b/IPython/frontend/frontendbase.py index 4fa4ad8..246af59 100644 --- a/IPython/frontend/frontendbase.py +++ b/IPython/frontend/frontendbase.py @@ -173,9 +173,6 @@ class FrontEndBase(object): history_cursor = 0 - current_indent_level = 0 - - input_prompt_template = string.Template(rc.prompt_in1) output_prompt_template = string.Template(rc.prompt_out) continuation_prompt_template = string.Template(rc.prompt_in2) @@ -336,7 +333,7 @@ class FrontEndBase(object): return result when finished. """ - return result + raise NotImplementedError def render_result(self, result): @@ -347,7 +344,7 @@ class FrontEndBase(object): should thus return result when finished. """ - return result + raise NotImplementedError def render_error(self, failure): @@ -358,7 +355,7 @@ class FrontEndBase(object): should thus return result when finished. """ - return failure + raise NotImplementedError diff --git a/IPython/frontend/linefrontendbase.py b/IPython/frontend/linefrontendbase.py index e9d76cd..5d1fbdc 100644 --- a/IPython/frontend/linefrontendbase.py +++ b/IPython/frontend/linefrontendbase.py @@ -19,6 +19,7 @@ __docformat__ = "restructuredtext en" import re import IPython +import sys from frontendbase import FrontEndBase from IPython.kernel.core.interpreter import Interpreter @@ -63,9 +64,7 @@ class LineFrontEndBase(FrontEndBase): shell = Interpreter() FrontEndBase.__init__(self, shell=shell, history=history) - #FIXME: print banner. - banner = """IPython1 %s -- An enhanced Interactive Python.""" \ - % IPython.__version__ + self.new_prompt(self.input_prompt_template.substitute(number=1)) def complete(self, line): @@ -96,7 +95,8 @@ class LineFrontEndBase(FrontEndBase): self.write('\n' + result['stdout']) if 'display' in result and result['display']: self.write("%s%s\n" % ( - self.output_prompt % result['number'], + self.output_prompt_template.substitute( + number=result['number']), result['display']['pprint'] ) ) @@ -104,7 +104,7 @@ class LineFrontEndBase(FrontEndBase): def render_error(self, failure): """ Frontend-specific rendering of error. """ - self.insert_text('\n\n'+str(failure)+'\n\n') + self.write('\n\n'+str(failure)+'\n\n') return failure @@ -129,7 +129,37 @@ class LineFrontEndBase(FrontEndBase): # Add line returns here, to make sure that the statement is # complete. return FrontEndBase.is_complete(self, string.rstrip() + '\n\n') + + + def get_current_edit_buffer(self): + """ Return the current buffer being entered. + """ + raise NotImplementedError + + + def write(self, string): + """ Write some characters to the display. + + Subclass should overide this method. + """ + print >>sys.__stderr__, string + + def add_to_edit_buffer(self, string): + """ Add the given string to the current edit buffer. + """ + raise NotImplementedError + + + def new_prompt(self, prompt): + """ Prints a prompt and starts a new editing buffer. + + Subclasses should use this method to make sure that the + terminal is put in a state favorable for a new line + input. + """ + self.write(prompt) + def execute(self, python_string, raw_string=None): """ Stores the raw_string in the history, and sends the @@ -167,7 +197,8 @@ class LineFrontEndBase(FrontEndBase): terminal back in a shape where it is usable. """ self.prompt_number += 1 - self.new_prompt(self.prompt % (self.last_result['number'] + 1)) + self.new_prompt(self.input_prompt_template.substitute( + number=(self.last_result['number'] + 1))) # Start a new empty history entry self._add_history(None, '') self.history_cursor = len(self.history.input_cache) - 1 @@ -186,10 +217,10 @@ class LineFrontEndBase(FrontEndBase): if self.is_complete(cleaned_buffer): self.execute(cleaned_buffer, raw_string=current_buffer) else: - self.write(self._get_indent_string( + self.add_to_edit_buffer(self._get_indent_string( current_buffer[:-1])) if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'): - self.write('\t') + self.add_to_edit_buffer('\t') def _get_indent_string(self, string): diff --git a/IPython/frontend/prefilterfrontend.py b/IPython/frontend/prefilterfrontend.py index f84c4c0..42f00b8 100644 --- a/IPython/frontend/prefilterfrontend.py +++ b/IPython/frontend/prefilterfrontend.py @@ -175,7 +175,7 @@ class PrefilterFrontEnd(LineFrontEndBase): #-------------------------------------------------------------------------- - # PrefilterLineFrontEnd interface + # PrefilterFrontEnd interface #-------------------------------------------------------------------------- def system_call(self, command_string): diff --git a/IPython/frontend/tests/test_prefilterfrontend.py b/IPython/frontend/tests/test_prefilterfrontend.py new file mode 100644 index 0000000..d9e3dda --- /dev/null +++ b/IPython/frontend/tests/test_prefilterfrontend.py @@ -0,0 +1,90 @@ +# encoding: utf-8 +""" +Test process execution and IO redirection. +""" + +__docformat__ = "restructuredtext en" + +#------------------------------------------------------------------------------- +# Copyright (C) 2008 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is +# in the file COPYING, distributed as part of this software. +#------------------------------------------------------------------------------- + +from IPython.frontend.prefilterfrontend import PrefilterFrontEnd +from cStringIO import StringIO +import string + +class TestPrefilterFrontEnd(PrefilterFrontEnd): + + input_prompt_template = string.Template('') + output_prompt_template = string.Template('') + + + def __init__(self, edit_buffer=''): + self.edit_buffer = edit_buffer + self.out = StringIO() + PrefilterFrontEnd.__init__(self) + + def get_current_edit_buffer(self): + return self.edit_buffer + + def add_to_edit_buffer(self, string): + self.edit_buffer += string + + def write(self, string): + self.out.write(string) + + def _on_enter(self): + self.add_to_edit_buffer('\n') + PrefilterFrontEnd._on_enter(self) + + +def test_execution(): + """ Test execution of a command. + """ + f = TestPrefilterFrontEnd(edit_buffer='print 1\n') + f._on_enter() + assert f.out.getvalue() == '1\n' + + +def test_multiline(): + """ Test execution of a multiline command. + """ + f = TestPrefilterFrontEnd(edit_buffer='if True:') + f._on_enter() + f.add_to_edit_buffer('print 1') + f._on_enter() + assert f.out.getvalue() == '' + f._on_enter() + assert f.out.getvalue() == '1\n' + f = TestPrefilterFrontEnd(edit_buffer='(1 +') + f._on_enter() + f.add_to_edit_buffer('0)') + f._on_enter() + assert f.out.getvalue() == '' + f._on_enter() + assert f.out.getvalue() == '1\n' + + +def test_capture(): + """ Test the capture of output in different channels. + """ + f = TestPrefilterFrontEnd( + edit_buffer='import os; out=os.fdopen(1, "w"); out.write("1")') + f._on_enter() + f._on_enter() + assert f.out.getvalue() == '1' + f = TestPrefilterFrontEnd( + edit_buffer='import os; out=os.fdopen(2, "w"); out.write("1")') + f._on_enter() + f._on_enter() + assert f.out.getvalue() == '1' + + + +if __name__ == '__main__': + test_execution() + test_multiline() + test_capture() diff --git a/IPython/frontend/tests/test_process.py b/IPython/frontend/tests/test_process.py index a879029..b275dff 100644 --- a/IPython/frontend/tests/test_process.py +++ b/IPython/frontend/tests/test_process.py @@ -44,9 +44,6 @@ def test_io(): def test_kill(): - pass - -if True: """ Check that we can kill a process, and its subprocess. """ s = StringIO() diff --git a/IPython/frontend/wx/console_widget.py b/IPython/frontend/wx/console_widget.py index 1091658..2ad60b0 100644 --- a/IPython/frontend/wx/console_widget.py +++ b/IPython/frontend/wx/console_widget.py @@ -106,11 +106,6 @@ class ConsoleWidget(editwindow.EditWindow): editwindow.EditWindow.__init__(self, parent, id, pos, size, style) self._configure_scintilla() - # FIXME: we need to retrieve this from the interpreter. - self.prompt = \ - '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02' - self.new_prompt(self.prompt % 1) - self.Bind(wx.EVT_KEY_DOWN, self._on_key_down) self.Bind(wx.EVT_KEY_UP, self._on_key_up) @@ -229,7 +224,10 @@ class ConsoleWidget(editwindow.EditWindow): buf.append(symbol.rstrip() + '\n') pos = 1 self.write(''.join(buf)) - self.new_prompt(self.prompt % (self.last_result['number'] + 1)) + # FIXME: I have some mixing of interfaces between console_widget + # and wx_frontend, here. + self.new_prompt(self.input_prompt_template.substitute( + number=self.last_result['number'] + 1)) self.replace_current_edit_buffer(current_buffer) #-------------------------------------------------------------------------- diff --git a/IPython/frontend/wx/ipythonx.py b/IPython/frontend/wx/ipythonx.py index 7045e3c..3d48991 100644 --- a/IPython/frontend/wx/ipythonx.py +++ b/IPython/frontend/wx/ipythonx.py @@ -38,7 +38,8 @@ class IPythonXController(WxController): self.release_output() wx.Yield() if not self.ipython0.exit_now: - self.new_prompt(self.prompt % (self.last_result['number'] + 1)) + self.new_prompt(self.input_prompt_template.substitute( + number=self.last_result['number'] + 1)) def do_exit(self): diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py index 6a5f377..39723d0 100644 --- a/IPython/frontend/wx/wx_frontend.py +++ b/IPython/frontend/wx/wx_frontend.py @@ -28,6 +28,7 @@ import __builtin__ from time import sleep import sys from threading import Lock +import string import wx from wx import stc @@ -48,6 +49,12 @@ _ERROR_BG = '#FFF1F1' # Nice red _RUNNING_BUFFER_MARKER = 31 _ERROR_MARKER = 30 +prompt_in1 = \ + '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02' + +prompt_out = \ + '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02' + #------------------------------------------------------------------------------- # Classes to implement the Wx frontend #------------------------------------------------------------------------------- @@ -59,9 +66,9 @@ class WxController(PrefilterFrontEnd, ConsoleWidget): widget to provide a text-rendering widget suitable for a terminal. """ - # FIXME: this shouldn't be there. - output_prompt = \ - '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02' + 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 = True @@ -283,6 +290,13 @@ class WxController(PrefilterFrontEnd, ConsoleWidget): self.MarkerAdd(i, _ERROR_MARKER) + def add_to_edit_buffer(self, string): + """ Add the given string to the current edit buffer. + """ + # XXX: I should check the input_state here. + self.write(string) + + #-------------------------------------------------------------------------- # ConsoleWidget interface #-------------------------------------------------------------------------- @@ -294,6 +308,18 @@ class WxController(PrefilterFrontEnd, ConsoleWidget): ConsoleWidget.new_prompt(self, prompt) + def write(self, *args, **kwargs): + # Avoid multiple inheritence, be explicit about which + # parent method class gets called + ConsoleWidget.write(self, *args, **kwargs) + + + def get_current_edit_buffer(self): + # Avoid multiple inheritence, be explicit about which + # parent method class gets called + return ConsoleWidget.get_current_edit_buffer(self) + + def _on_key_down(self, event, skip=True): """ Capture the character events, let the parent widget handle them, and put our logic afterward. @@ -399,7 +425,6 @@ class WxController(PrefilterFrontEnd, ConsoleWidget): def _end_system_call(self): """ Called at the end of a system call. """ - print >>sys.__stderr__, 'End of system call' self._input_state = 'buffering' self._running_process = False