diff --git a/IPython/frontend/asyncfrontendbase.py b/IPython/frontend/asyncfrontendbase.py new file mode 100644 index 0000000..c3614b7 --- /dev/null +++ b/IPython/frontend/asyncfrontendbase.py @@ -0,0 +1,93 @@ +""" +Base front end class for all async frontends. +""" +__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. +#------------------------------------------------------------------------------- + + +#------------------------------------------------------------------------------- +# Imports +#------------------------------------------------------------------------------- +import uuid + +try: + from zope.interface import Interface, Attribute, implements, classProvides +except ImportError: + #zope.interface is not available + Interface = object + def Attribute(name, doc): pass + def implements(interface): pass + def classProvides(interface): pass + + + +from frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory + +from IPython.kernel.engineservice import IEngineCore +from IPython.kernel.core.history import FrontEndHistory + +try: + from twisted.python.failure import Failure +except ImportError: + #Twisted not available + Failure = Exception + + + + +class AsyncFrontEndBase(FrontEndBase): + """ + Overrides FrontEndBase to wrap execute in a deferred result. + All callbacks are made as callbacks on the deferred result. + """ + + implements(IFrontEnd) + classProvides(IFrontEndFactory) + + def __init__(self, engine=None, history=None): + assert(engine==None or IEngineCore.providedBy(engine)) + self.engine = IEngineCore(engine) + if history is None: + self.history = FrontEndHistory(input_cache=['']) + else: + self.history = history + + + def execute(self, block, blockID=None): + """Execute the block and return the deferred result. + + Parameters: + block : {str, AST} + blockID : any + Caller may provide an ID to identify this block. + result['blockID'] := blockID + + Result: + Deferred result of self.interpreter.execute + """ + + if(not self.is_complete(block)): + return Failure(Exception("Block is not compilable")) + + if(blockID == None): + blockID = uuid.uuid4() #random UUID + + d = self.engine.execute(block) + d.addCallback(self._add_history, block=block) + d.addCallbacks(self._add_block_id_for_result, + errback=self._add_block_id_for_failure, + callbackArgs=(blockID,), + errbackArgs=(blockID,)) + d.addBoth(self.update_cell_prompt, blockID=blockID) + d.addCallbacks(self.render_result, + errback=self.render_error) + + return d + + diff --git a/IPython/frontend/cocoa/cocoa_frontend.py b/IPython/frontend/cocoa/cocoa_frontend.py index df6cc3f..09e1069 100644 --- a/IPython/frontend/cocoa/cocoa_frontend.py +++ b/IPython/frontend/cocoa/cocoa_frontend.py @@ -40,7 +40,7 @@ from pprint import saferepr import IPython from IPython.kernel.engineservice import ThreadedEngineService -from IPython.frontend.frontendbase import AsyncFrontEndBase +from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase from twisted.internet.threads import blockingCallFromThread from twisted.python.failure import Failure diff --git a/IPython/frontend/frontendbase.py b/IPython/frontend/frontendbase.py index e9c8fbb..37ee772 100644 --- a/IPython/frontend/frontendbase.py +++ b/IPython/frontend/frontendbase.py @@ -37,12 +37,6 @@ from IPython.kernel.core.history import FrontEndHistory from IPython.kernel.core.util import Bunch from IPython.kernel.engineservice import IEngineCore -try: - from twisted.python.failure import Failure -except ImportError: - #Twisted not available - Failure = Exception - ############################################################################## # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or # not @@ -355,53 +349,4 @@ class FrontEndBase(object): -class AsyncFrontEndBase(FrontEndBase): - """ - Overrides FrontEndBase to wrap execute in a deferred result. - All callbacks are made as callbacks on the deferred result. - """ - - implements(IFrontEnd) - classProvides(IFrontEndFactory) - - def __init__(self, engine=None, history=None): - assert(engine==None or IEngineCore.providedBy(engine)) - self.engine = IEngineCore(engine) - if history is None: - self.history = FrontEndHistory(input_cache=['']) - else: - self.history = history - - - def execute(self, block, blockID=None): - """Execute the block and return the deferred result. - - Parameters: - block : {str, AST} - blockID : any - Caller may provide an ID to identify this block. - result['blockID'] := blockID - - Result: - Deferred result of self.interpreter.execute - """ - - if(not self.is_complete(block)): - return Failure(Exception("Block is not compilable")) - - if(blockID == None): - blockID = uuid.uuid4() #random UUID - - d = self.engine.execute(block) - d.addCallback(self._add_history, block=block) - d.addCallbacks(self._add_block_id_for_result, - errback=self._add_block_id_for_failure, - callbackArgs=(blockID,), - errbackArgs=(blockID,)) - d.addBoth(self.update_cell_prompt, blockID=blockID) - d.addCallbacks(self.render_result, - errback=self.render_error) - - return d - diff --git a/IPython/frontend/linefrontendbase.py b/IPython/frontend/linefrontendbase.py new file mode 100644 index 0000000..30847c3 --- /dev/null +++ b/IPython/frontend/linefrontendbase.py @@ -0,0 +1,123 @@ +""" +Base front end class for all line-oriented frontends. + +Currently this focuses on synchronous frontends. +""" +__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. +#------------------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +# Imports +#------------------------------------------------------------------------------- +import re + +import IPython + + +from frontendbase import FrontEndBase +from IPython.kernel.core.interpreter import Interpreter + +#------------------------------------------------------------------------------- +# Base class for the line-oriented front ends +#------------------------------------------------------------------------------- +class LineFrontEndBase(FrontEndBase): + + # Are we entering multi line input? + multi_line_input = False + + # The added tab stop to the string. It may, for instance, come from + # copy and pasting something with tabs. + tab_stop = 0 + # FIXME: We still have to deal with this. + + #-------------------------------------------------------------------------- + # Public API + #-------------------------------------------------------------------------- + + def __init__(self, shell=None, history=None): + if shell is None: + shell = Interpreter() + FrontEndBase.__init__(self, shell=shell, history=history) + + #FIXME: print banner. + banner = """IPython1 %s -- An enhanced Interactive Python.""" \ + % IPython.__version__ + + + def complete(self, token): + """Complete token in engine's user_ns + + Parameters + ---------- + token : string + + Result + ------ + Deferred result of + IPython.kernel.engineservice.IEngineBase.complete + """ + + return self.shell.complete(token) + + + def render_result(self, result): + if 'stdout' in result and result['stdout']: + self.write('\n' + result['stdout']) + if 'display' in result and result['display']: + self.write("%s%s\n" % ( + self.output_prompt % result['number'], + result['display']['pprint'] + ) ) + + + def render_error(self, failure): + self.insert_text('\n\n'+str(failure)+'\n\n') + return failure + + + def on_enter(self): + """ Called when the return key is pressed in a line editing + buffer. + """ + current_buffer = self.get_current_edit_buffer() + current_buffer = current_buffer.replace('\r\n', '\n') + current_buffer = current_buffer.replace('\t', 4*' ') + cleaned_buffer = '\n'.join(l.rstrip() + for l in current_buffer.split('\n')) + if ( not self.multi_line_input + or re.findall(r"\n[\t ]*$", cleaned_buffer)): + if self.is_complete(cleaned_buffer): + self._add_history(None, cleaned_buffer.rstrip()) + result = self.shell.execute(cleaned_buffer) + self.render_result(result) + self.new_prompt(self.prompt % (result['number'] + 1)) + self.multi_line_input = False + else: + if self.multi_line_input: + self.write('\n' + self._get_indent_string(current_buffer)) + else: + self.multi_line_input = True + self.write('\n\t') + else: + self.write('\n'+self._get_indent_string(current_buffer)) + + + #-------------------------------------------------------------------------- + # Private API + #-------------------------------------------------------------------------- + + def _get_indent_string(self, string): + string = string.split('\n')[-1] + indent_chars = len(string) - len(string.lstrip()) + indent_string = '\t'*(indent_chars // 4) + \ + ' '*(indent_chars % 4) + + return indent_string + + diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py index 706e0fe..5bb0229 100644 --- a/IPython/frontend/wx/wx_frontend.py +++ b/IPython/frontend/wx/wx_frontend.py @@ -23,11 +23,8 @@ __docformat__ = "restructuredtext en" import wx from console_widget import ConsoleWidget -import re -import IPython -from IPython.kernel.engineservice import EngineService -from IPython.frontend.frontendbase import FrontEndBase +from IPython.frontend.linefrontendbase import LineFrontEndBase #------------------------------------------------------------------------------- # Classes to implement the Wx frontend @@ -36,19 +33,11 @@ from IPython.frontend.frontendbase import FrontEndBase -class IPythonWxController(FrontEndBase, ConsoleWidget): +class IPythonWxController(LineFrontEndBase, ConsoleWidget): output_prompt = \ '\n\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02' - # Are we entering multi line input? - multi_line_input = False - - # The added tab stop to the string. It may, for instance, come from - # copy and pasting something with tabs. - tab_stop = 0 - # FIXME: We still have to deal with this. - #-------------------------------------------------------------------------- # Public API #-------------------------------------------------------------------------- @@ -59,61 +48,11 @@ class IPythonWxController(FrontEndBase, ConsoleWidget): """ Create Shell instance. """ ConsoleWidget.__init__(self, parent, id, pos, size, style) - FrontEndBase.__init__(self, engine=EngineService(), - ) - - # FIXME: Something is wrong with the history, I instanciate it - # with an empty cache, but this is not the way to do. - self.lines = {} + LineFrontEndBase.__init__(self) - # Start the IPython engine - self.engine.startService() - # Capture Character keys self.Bind(wx.EVT_KEY_DOWN, self._on_key_down) - #FIXME: print banner. - banner = """IPython1 %s -- An enhanced Interactive Python.""" \ - % IPython.__version__ - - - def appWillTerminate_(self, notification): - """appWillTerminate""" - - self.engine.stopService() - - - def complete(self, token): - """Complete token in engine's user_ns - - Parameters - ---------- - token : string - - Result - ------ - Deferred result of - IPython.kernel.engineservice.IEngineBase.complete - """ - - return self.engine.complete(token) - - - def render_result(self, result): - if 'stdout' in result and result['stdout']: - self.write('\n' + result['stdout']) - if 'display' in result and result['display']: - self.write("%s%s\n" % ( - self.output_prompt % result['number'], - result['display']['pprint'] - ) ) - - - def render_error(self, failure): - self.insert_text('\n\n'+str(failure)+'\n\n') - return failure - - #-------------------------------------------------------------------------- # Private API #-------------------------------------------------------------------------- @@ -127,7 +66,7 @@ class IPythonWxController(FrontEndBase, ConsoleWidget): # Capture enter if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \ event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN): - self._on_enter() + self.on_enter() # Up history elif event.KeyCode == wx.WXK_UP and ( ( current_line_number == self.current_prompt_line and @@ -149,42 +88,7 @@ class IPythonWxController(FrontEndBase, ConsoleWidget): ConsoleWidget._on_key_down(self, event, skip=True) - def _on_enter(self): - """ Called when the return key is pressed in a line editing - buffer. - """ - current_buffer = self.get_current_edit_buffer() - current_buffer = current_buffer.replace('\r\n', '\n') - current_buffer = current_buffer.replace('\t', 4*' ') - cleaned_buffer = '\n'.join(l.rstrip() - for l in current_buffer.split('\n')) - if ( not self.multi_line_input - or re.findall(r"\n[\t ]*$", cleaned_buffer)): - if self.is_complete(cleaned_buffer): - self._add_history(None, cleaned_buffer.rstrip()) - result = self.engine.shell.execute(cleaned_buffer) - self.render_result(result) - self.new_prompt(self.prompt % (result['number'] + 1)) - self.multi_line_input = False - else: - if self.multi_line_input: - self.write('\n' + self._get_indent_string(current_buffer)) - else: - self.multi_line_input = True - self.write('\n\t') - else: - self.write('\n'+self._get_indent_string(current_buffer)) - - - def _get_indent_string(self, string): - string = string.split('\n')[-1] - indent_chars = len(string) - len(string.lstrip()) - indent_string = '\t'*(indent_chars // 4) + \ - ' '*(indent_chars % 4) - - return indent_string - - + if __name__ == '__main__': class MainWindow(wx.Frame):