diff --git a/IPython/frontend/cocoa/cocoa_frontend.py b/IPython/frontend/cocoa/cocoa_frontend.py index 77df8ba..1516ed1 100644 --- a/IPython/frontend/cocoa/cocoa_frontend.py +++ b/IPython/frontend/cocoa/cocoa_frontend.py @@ -1,27 +1,28 @@ # encoding: utf-8 -# -*- test-case-name: ipython1.frontend.cocoa.tests.test_cocoa_frontend -*- +# -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*- -"""PyObjC classes to provide a Cocoa frontend to the ipython1.kernel.engineservice.EngineService. +"""PyObjC classes to provide a Cocoa frontend to the +IPython.kernel.engineservice.EngineService. -The Cocoa frontend is divided into two classes: - - IPythonCocoaController - - IPythonCLITextViewDelegate +To add an IPython interpreter to a cocoa app, instantiate an +IPythonCocoaController in a XIB and connect its textView outlet to an +NSTextView instance in your UI. That's it. -To add an IPython interpreter to a cocoa app, instantiate both of these classes in an XIB...[FINISH] +Author: Barry Wark """ __docformat__ = "restructuredtext en" -#------------------------------------------------------------------------------- -# Copyright (C) 2008 Barry Wark +#----------------------------------------------------------------------------- +# 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 objc import uuid @@ -42,12 +43,13 @@ from IPython.frontend.frontendbase import FrontEndBase from twisted.internet.threads import blockingCallFromThread from twisted.python.failure import Failure -#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------ # Classes to implement the Cocoa frontend -#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------ # TODO: -# 1. use MultiEngineClient and out-of-process engine rather than ThreadedEngineService? +# 1. use MultiEngineClient and out-of-process engine rather than +# ThreadedEngineService? # 2. integrate Xgrid launching of engines @@ -89,16 +91,17 @@ class IPythonCocoaController(NSObject, FrontEndBase): NSLog('IPython engine started') # Register for app termination - NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, - 'appWillTerminate:', - NSApplicationWillTerminateNotification, - None) + NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( + self, + 'appWillTerminate:', + NSApplicationWillTerminateNotification, + None) self.textView.setDelegate_(self) self.textView.enclosingScrollView().setHasVerticalRuler_(True) self.verticalRulerView = NSRulerView.alloc().initWithScrollView_orientation_( - self.textView.enclosingScrollView(), - NSVerticalRuler) + self.textView.enclosingScrollView(), + NSVerticalRuler) self.verticalRulerView.setClientView_(self.textView) self.startCLIForTextView() @@ -163,7 +166,8 @@ class IPythonCocoaController(NSObject, FrontEndBase): def startCLIForTextView(self): """Print banner""" - banner = """IPython1 %s -- An enhanced Interactive Python.""" % IPython.__version__ + banner = """IPython1 %s -- An enhanced Interactive Python.""" % \ + IPython.__version__ self.insert_text(banner + '\n\n') @@ -206,14 +210,18 @@ class IPythonCocoaController(NSObject, FrontEndBase): return True elif(selector == 'moveToBeginningOfParagraph:'): - textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location, 0)) + textView.setSelectedRange_(NSMakeRange( + self.currentBlockRange().location, + 0)) return True elif(selector == 'moveToEndOfParagraph:'): - textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location + \ - self.currentBlockRange().length, 0)) + textView.setSelectedRange_(NSMakeRange( + self.currentBlockRange().location + \ + self.currentBlockRange().length, 0)) return True elif(selector == 'deleteToEndOfParagraph:'): - if(textView.selectedRange().location <= self.currentBlockRange().location): + if(textView.selectedRange().location <= \ + self.currentBlockRange().location): # Intersect the selected range with the current line range if(self.currentBlockRange().length < 0): self.blockRanges[self.currentBlockID].length = 0 @@ -235,7 +243,8 @@ class IPythonCocoaController(NSObject, FrontEndBase): elif(selector == 'deleteBackward:'): #if we're at the beginning of the current block, ignore - if(textView.selectedRange().location == self.currentBlockRange().location): + if(textView.selectedRange().location == \ + self.currentBlockRange().location): return True else: self.currentBlockRange().length-=1 @@ -243,14 +252,15 @@ class IPythonCocoaController(NSObject, FrontEndBase): return False - def textView_shouldChangeTextInRanges_replacementStrings_(self, textView, ranges, replacementStrings): + def textView_shouldChangeTextInRanges_replacementStrings_(self, + textView, ranges, replacementStrings): """ Delegate method for NSTextView. - Refuse change text in ranges not at end, but make those changes at end. + Refuse change text in ranges not at end, but make those changes at + end. """ - #print 'textView_shouldChangeTextInRanges_replacementStrings_:',ranges,replacementStrings assert(len(ranges) == len(replacementStrings)) allow = True for r,s in zip(ranges, replacementStrings): @@ -261,13 +271,17 @@ class IPythonCocoaController(NSObject, FrontEndBase): allow = False - self.blockRanges.setdefault(self.currentBlockID, self.currentBlockRange()).length += len(s) + self.blockRanges.setdefault(self.currentBlockID, + self.currentBlockRange()).length +=\ + len(s) return allow - def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, textView, words, charRange, index): + def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, + textView, words, charRange, index): try: - token = textView.textStorage().string().substringWithRange_(charRange) + ts = textView.textStorage() + token = ts.string().substringWithRange_(charRange) completions = blockingCallFromThread(self.complete, token) except: completions = objc.nil @@ -288,7 +302,9 @@ class IPythonCocoaController(NSObject, FrontEndBase): return uuid.uuid4() def currentBlockRange(self): - return self.blockRanges.get(self.currentBlockID, NSMakeRange(self.textView.textStorage().length(), 0)) + return self.blockRanges.get(self.currentBlockID, + NSMakeRange(self.textView.textStorage().length(), + 0)) def currentBlock(self): """The current block's text""" @@ -298,7 +314,8 @@ class IPythonCocoaController(NSObject, FrontEndBase): def textForRange(self, textRange): """textForRange""" - return self.textView.textStorage().string().substringWithRange_(textRange) + ts = self.textView.textStorage() + return ts.string().substringWithRange_(textRange) def currentLine(self): block = self.textForRange(self.currentBlockRange()) @@ -313,9 +330,9 @@ class IPythonCocoaController(NSObject, FrontEndBase): self.insert_text(self.input_prompt(result=result), - textRange=NSMakeRange(self.blockRanges[blockID].location,0), - scrollToVisible=False - ) + textRange=NSMakeRange(self.blockRanges[blockID].location,0), + scrollToVisible=False + ) return result @@ -327,10 +344,11 @@ class IPythonCocoaController(NSObject, FrontEndBase): #print inputRange,self.currentBlockRange() self.insert_text('\n' + - self.output_prompt(result) + - result.get('display',{}).get('pprint','') + - '\n\n', - textRange=NSMakeRange(inputRange.location+inputRange.length, 0)) + self.output_prompt(result) + + result.get('display',{}).get('pprint','') + + '\n\n', + textRange=NSMakeRange(inputRange.location+inputRange.length, + 0)) return result @@ -341,7 +359,9 @@ class IPythonCocoaController(NSObject, FrontEndBase): def insert_text(self, string=None, textRange=None, scrollToVisible=True): - """Insert text into textView at textRange, updating blockRanges as necessary""" + """Insert text into textView at textRange, updating blockRanges + as necessary + """ if(textRange == None): textRange = NSMakeRange(self.textView.textStorage().length(), 0) #range for end of text diff --git a/IPython/frontend/cocoa/tests/test_cocoa_frontend.py b/IPython/frontend/cocoa/tests/test_cocoa_frontend.py index ae1b9d6..eb4ae43 100644 --- a/IPython/frontend/cocoa/tests/test_cocoa_frontend.py +++ b/IPython/frontend/cocoa/tests/test_cocoa_frontend.py @@ -1,27 +1,20 @@ # encoding: utf-8 -"""This file contains unittests for the ipython1.frontend.cocoa.cocoa_frontend module. - -Things that should be tested: - - - IPythonCocoaController instantiates an IEngineInteractive - - IPythonCocoaController executes code on the engine - - IPythonCocoaController mirrors engine's user_ns +"""This file contains unittests for the +IPython.frontend.cocoa.cocoa_frontend module. """ __docformat__ = "restructuredtext en" - -#------------------------------------------------------------------------------- -# Copyright (C) 2005 Fernando Perez -# Brian E Granger -# Benjamin Ragan-Kelley -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports -#------------------------------------------------------------------------------- -from IPython.kernel.core.interpreter import Interpreter + +#--------------------------------------------------------------------------- +# Copyright (C) 2005 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 +#--------------------------------------------------------------------------- +from IPython.kernel.core.interpreter import Interpreter import IPython.kernel.engineservice as es from IPython.testing.util import DeferredTestCase from twisted.internet.defer import succeed @@ -51,7 +44,9 @@ class TestIPythonCocoaControler(DeferredTestCase): del result['number'] del result['id'] return result - self.assertDeferredEquals(self.controller.execute(code).addCallback(removeNumberAndID), expected) + self.assertDeferredEquals( + self.controller.execute(code).addCallback(removeNumberAndID), + expected) def testControllerMirrorsUserNSWithValuesAsStrings(self): code = """userns1=1;userns2=2""" diff --git a/IPython/frontend/frontendbase.py b/IPython/frontend/frontendbase.py index 577bc97..7f403c2 100644 --- a/IPython/frontend/frontendbase.py +++ b/IPython/frontend/frontendbase.py @@ -1,6 +1,8 @@ # encoding: utf-8 +# -*- test-case-name: IPython.frontend.tests.test_frontendbase -*- """ -frontendbase provides an interface and base class for GUI frontends for IPython.kernel/IPython.kernel.core. +frontendbase provides an interface and base class for GUI frontends for +IPython.kernel/IPython.kernel.core. Frontend implementations will likely want to subclass FrontEndBase. @@ -57,20 +59,26 @@ class IFrontEndFactory(zi.Interface): class IFrontEnd(zi.Interface): """Interface for frontends. All methods return t.i.d.Deferred""" - zi.Attribute("input_prompt_template", "string.Template instance substituteable with execute result.") - zi.Attribute("output_prompt_template", "string.Template instance substituteable with execute result.") - zi.Attribute("continuation_prompt_template", "string.Template instance substituteable with execute result.") + zi.Attribute("input_prompt_template", "string.Template instance\ + substituteable with execute result.") + zi.Attribute("output_prompt_template", "string.Template instance\ + substituteable with execute result.") + zi.Attribute("continuation_prompt_template", "string.Template instance\ + substituteable with execute result.") def update_cell_prompt(self, result): """Subclass may override to update the input prompt for a block. - Since this method will be called as a twisted.internet.defer.Deferred's callback, + Since this method will be called as a + twisted.internet.defer.Deferred's callback, implementations should return result when finished.""" pass def render_result(self, result): - """Render the result of an execute call. Implementors may choose the method of rendering. - For example, a notebook-style frontend might render a Chaco plot inline. + """Render the result of an execute call. Implementors may choose the + method of rendering. + For example, a notebook-style frontend might render a Chaco plot + inline. Parameters: result : dict (result of IEngineBase.execute ) @@ -82,24 +90,31 @@ class IFrontEnd(zi.Interface): pass def render_error(self, failure): - """Subclasses must override to render the failure. Since this method will be called as a - twisted.internet.defer.Deferred's callback, implementations should return result - when finished.""" + """Subclasses must override to render the failure. Since this method + ill be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished. + """ pass def input_prompt(result={}): - """Returns the input prompt by subsituting into self.input_prompt_template""" + """Returns the input prompt by subsituting into + self.input_prompt_template + """ pass def output_prompt(result): - """Returns the output prompt by subsituting into self.output_prompt_template""" + """Returns the output prompt by subsituting into + self.output_prompt_template + """ pass def continuation_prompt(): - """Returns the continuation prompt by subsituting into self.continuation_prompt_template""" + """Returns the continuation prompt by subsituting into + self.continuation_prompt_template + """ pass @@ -221,7 +236,8 @@ class FrontEndBase(object): Parameters: block : {str, AST} blockID : any - Caller may provide an ID to identify this block. result['blockID'] := blockID + Caller may provide an ID to identify this block. + result['blockID'] := blockID Result: Deferred result of self.interpreter.execute @@ -243,8 +259,8 @@ class FrontEndBase(object): def _add_block_id(self, result, blockID): - """Add the blockID to result or failure. Unfortunatley, we have to treat failures - differently than result dicts + """Add the blockID to result or failure. Unfortunatley, we have to + treat failures differently than result dicts. """ if(isinstance(result, Failure)): @@ -291,11 +307,12 @@ class FrontEndBase(object): def update_cell_prompt(self, result): """Subclass may override to update the input prompt for a block. - Since this method will be called as a twisted.internet.defer.Deferred's callback, - implementations should return result when finished. + Since this method will be called as a + twisted.internet.defer.Deferred's callback, implementations should + return result when finished. - NP: result is a failure if the execute returned a failre. To get the blockID, you should - do something like:: + NP: result is a failure if the execute returned a failre. + To get the blockID, you should do something like:: if(isinstance(result, twisted.python.failure.Failure)): blockID = result.blockID else: @@ -308,17 +325,18 @@ class FrontEndBase(object): def render_result(self, result): - """Subclasses must override to render result. Since this method will be called as a - twisted.internet.defer.Deferred's callback, implementations should return result - when finished.""" + """Subclasses must override to render result. Since this method will + be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished. + """ return result def render_error(self, failure): - """Subclasses must override to render the failure. Since this method will be called as a - twisted.internet.defer.Deferred's callback, implementations should return result - when finished.""" + """Subclasses must override to render the failure. Since this method + will be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished.""" return failure diff --git a/IPython/frontend/tests/test_frontendbase.py b/IPython/frontend/tests/test_frontendbase.py index d3c265a..451e302 100644 --- a/IPython/frontend/tests/test_frontendbase.py +++ b/IPython/frontend/tests/test_frontendbase.py @@ -4,16 +4,16 @@ __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 -#------------------------------------------------------------------------------- +#--------------------------------------------------------------------------- +# 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 unittest from IPython.frontend import frontendbase @@ -22,7 +22,8 @@ from IPython.kernel.engineservice import EngineService class FrontEndCallbackChecker(frontendbase.FrontEndBase): """FrontEndBase subclass for checking callbacks""" def __init__(self, engine=None, history=None): - super(FrontEndCallbackChecker, self).__init__(engine=engine, history=history) + super(FrontEndCallbackChecker, self).__init__(engine=engine, + history=history) self.updateCalled = False self.renderResultCalled = False self.renderErrorCalled = False @@ -51,7 +52,8 @@ class TestFrontendBase(unittest.TestCase): def test_implements_IFrontEnd(self): - assert(frontendbase.IFrontEnd.implementedBy(frontendbase.FrontEndBase)) + assert(frontendbase.IFrontEnd.implementedBy( + frontendbase.FrontEndBase)) def test_is_complete_returns_False_for_incomplete_block(self): diff --git a/IPython/kernel/engineservice.py b/IPython/kernel/engineservice.py index 90b5f98..c8ec1fc 100644 --- a/IPython/kernel/engineservice.py +++ b/IPython/kernel/engineservice.py @@ -847,11 +847,13 @@ class Command(object): self.deferred.errback(reason) class ThreadedEngineService(EngineService): - """An EngineService subclass that defers execute commands to a separate thread. + """An EngineService subclass that defers execute commands to a separate + thread. - ThreadedEngineService uses twisted.internet.threads.deferToThread to defer execute - requests to a separate thread. GUI frontends may want to use ThreadedEngineService as - the engine in an IPython.frontend.frontendbase.FrontEndBase subclass to prevent + ThreadedEngineService uses twisted.internet.threads.deferToThread to + defer execute requests to a separate thread. GUI frontends may want to + use ThreadedEngineService as the engine in an + IPython.frontend.frontendbase.FrontEndBase subclass to prevent block execution from blocking the GUI thread. """