##// END OF EJS Templates
pep8 compliance, first pass
pep8 compliance, first pass

File last commit:

r1291:f18505ed
r1291:f18505ed
Show More
cocoa_frontend.py
415 lines | 14.0 KiB | text/x-python | PythonLexer
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 # encoding: utf-8
Barry Wark
pep8 compliance, first pass
r1291 # -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*-
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
pep8 compliance, first pass
r1291 """PyObjC classes to provide a Cocoa frontend to the
IPython.kernel.engineservice.EngineService.
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
pep8 compliance, first pass
r1291 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.
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
pep8 compliance, first pass
r1291 Author: Barry Wark
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """
__docformat__ = "restructuredtext en"
Barry Wark
pep8 compliance, first pass
r1291 #-----------------------------------------------------------------------------
# Copyright (C) 2008 The IPython Development Team
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 #
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
Barry Wark
pep8 compliance, first pass
r1291 #-----------------------------------------------------------------------------
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
pep8 compliance, first pass
r1291 #-----------------------------------------------------------------------------
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 # Imports
Barry Wark
pep8 compliance, first pass
r1291 #-----------------------------------------------------------------------------
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
import objc
import uuid
from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
NSLog, NSNotificationCenter, NSMakeRange,\
NSLocalizedString, NSIntersectionRange
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
NSTextView, NSRulerView, NSVerticalRuler
from pprint import saferepr
Barry Wark
fixed frontendbase.FrontEndBase.is_complete
r1278 import IPython
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 from IPython.kernel.engineservice import EngineService, ThreadedEngineService
from IPython.frontend.frontendbase import FrontEndBase
from twisted.internet.threads import blockingCallFromThread
Barry Wark
added blockID for failures (special case)
r1283 from twisted.python.failure import Failure
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
pep8 compliance, first pass
r1291 #------------------------------------------------------------------------------
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 # Classes to implement the Cocoa frontend
Barry Wark
pep8 compliance, first pass
r1291 #------------------------------------------------------------------------------
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
# TODO:
Barry Wark
pep8 compliance, first pass
r1291 # 1. use MultiEngineClient and out-of-process engine rather than
# ThreadedEngineService?
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 # 2. integrate Xgrid launching of engines
class IPythonCocoaController(NSObject, FrontEndBase):
userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
waitingForEngine = objc.ivar().bool()
textView = objc.IBOutlet()
def init(self):
self = super(IPythonCocoaController, self).init()
FrontEndBase.__init__(self, engine=ThreadedEngineService())
if(self != None):
self._common_init()
return self
def _common_init(self):
"""_common_init"""
self.userNS = NSMutableDictionary.dictionary()
self.waitingForEngine = False
self.lines = {}
self.tabSpaces = 4
self.tabUsesSpaces = True
self.currentBlockID = self.nextBlockID()
self.blockRanges = {} # blockID=>NSRange
def awakeFromNib(self):
"""awakeFromNib"""
self._common_init()
# Start the IPython engine
self.engine.startService()
NSLog('IPython engine started')
# Register for app termination
Barry Wark
pep8 compliance, first pass
r1291 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self,
'appWillTerminate:',
NSApplicationWillTerminateNotification,
None)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
self.textView.setDelegate_(self)
self.textView.enclosingScrollView().setHasVerticalRuler_(True)
self.verticalRulerView = NSRulerView.alloc().initWithScrollView_orientation_(
Barry Wark
pep8 compliance, first pass
r1291 self.textView.enclosingScrollView(),
NSVerticalRuler)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 self.verticalRulerView.setClientView_(self.textView)
self.startCLIForTextView()
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 ipython1.kernel.engineservice.IEngineInteractive.complete
"""
return self.engine.complete(token)
def execute(self, block, blockID=None):
self.waitingForEngine = True
self.willChangeValueForKey_('commandHistory')
d = super(IPythonCocoaController, self).execute(block, blockID)
d.addBoth(self._engineDone)
d.addCallback(self._updateUserNS)
return d
def _engineDone(self, x):
self.waitingForEngine = False
self.didChangeValueForKey_('commandHistory')
return x
def _updateUserNS(self, result):
"""Update self.userNS from self.engine's namespace"""
d = self.engine.keys()
d.addCallback(self._getEngineNamepsaceValuesForKeys)
return result
def _getEngineNamepsaceValuesForKeys(self, keys):
d = self.engine.pull(keys)
d.addCallback(self._storeEngineNamespaceValues, keys=keys)
def _storeEngineNamespaceValues(self, values, keys=[]):
assert(len(values) == len(keys))
self.willChangeValueForKey_('userNS')
for (k,v) in zip(keys,values):
self.userNS[k] = saferepr(v)
self.didChangeValueForKey_('userNS')
def startCLIForTextView(self):
"""Print banner"""
Barry Wark
pep8 compliance, first pass
r1291 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
IPython.__version__
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
self.insert_text(banner + '\n\n')
# NSTextView/IPythonTextView delegate methods
def textView_doCommandBySelector_(self, textView, selector):
assert(textView == self.textView)
NSLog("textView_doCommandBySelector_: "+selector)
if(selector == 'insertNewline:'):
indent = self.currentIndentString()
if(indent):
line = indent + self.currentLine()
else:
line = self.currentLine()
if(self.is_complete(self.currentBlock())):
self.execute(self.currentBlock(),
blockID=self.currentBlockID)
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 self.startNewBlock()
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 return True
return False
elif(selector == 'moveUp:'):
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 prevBlock = self.get_history_previous(self.currentBlock())
if(prevBlock != None):
self.replaceCurrentBlockWithString(textView, prevBlock)
else:
NSBeep()
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 return True
elif(selector == 'moveDown:'):
Barry Wark
fixes and tests for history
r1281 nextBlock = self.get_history_next()
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 if(nextBlock != None):
self.replaceCurrentBlockWithString(textView, nextBlock)
else:
NSBeep()
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 return True
elif(selector == 'moveToBeginningOfParagraph:'):
Barry Wark
pep8 compliance, first pass
r1291 textView.setSelectedRange_(NSMakeRange(
self.currentBlockRange().location,
0))
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 return True
elif(selector == 'moveToEndOfParagraph:'):
Barry Wark
pep8 compliance, first pass
r1291 textView.setSelectedRange_(NSMakeRange(
self.currentBlockRange().location + \
self.currentBlockRange().length, 0))
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 return True
elif(selector == 'deleteToEndOfParagraph:'):
Barry Wark
pep8 compliance, first pass
r1291 if(textView.selectedRange().location <= \
self.currentBlockRange().location):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 # Intersect the selected range with the current line range
if(self.currentBlockRange().length < 0):
self.blockRanges[self.currentBlockID].length = 0
r = NSIntersectionRange(textView.rangesForUserTextChange()[0],
self.currentBlockRange())
if(r.length > 0): #no intersection
textView.setSelectedRange_(r)
return False # don't actually handle the delete
elif(selector == 'insertTab:'):
if(len(self.currentLine().strip()) == 0): #only white space
return False
else:
self.textView.complete_(self)
return True
elif(selector == 'deleteBackward:'):
#if we're at the beginning of the current block, ignore
Barry Wark
pep8 compliance, first pass
r1291 if(textView.selectedRange().location == \
self.currentBlockRange().location):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 return True
else:
self.currentBlockRange().length-=1
return False
return False
Barry Wark
pep8 compliance, first pass
r1291 def textView_shouldChangeTextInRanges_replacementStrings_(self,
textView, ranges, replacementStrings):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """
Delegate method for NSTextView.
Barry Wark
pep8 compliance, first pass
r1291 Refuse change text in ranges not at end, but make those changes at
end.
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """
assert(len(ranges) == len(replacementStrings))
allow = True
for r,s in zip(ranges, replacementStrings):
r = r.rangeValue()
if(textView.textStorage().length() > 0 and
r.location < self.currentBlockRange().location):
self.insert_text(s)
allow = False
Barry Wark
pep8 compliance, first pass
r1291 self.blockRanges.setdefault(self.currentBlockID,
self.currentBlockRange()).length +=\
len(s)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
return allow
Barry Wark
pep8 compliance, first pass
r1291 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
textView, words, charRange, index):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 try:
Barry Wark
pep8 compliance, first pass
r1291 ts = textView.textStorage()
token = ts.string().substringWithRange_(charRange)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 completions = blockingCallFromThread(self.complete, token)
except:
completions = objc.nil
NSBeep()
return (completions,0)
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 def startNewBlock(self):
""""""
self.currentBlockID = self.nextBlockID()
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 def nextBlockID(self):
return uuid.uuid4()
def currentBlockRange(self):
Barry Wark
pep8 compliance, first pass
r1291 return self.blockRanges.get(self.currentBlockID,
NSMakeRange(self.textView.textStorage().length(),
0))
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
def currentBlock(self):
"""The current block's text"""
return self.textForRange(self.currentBlockRange())
def textForRange(self, textRange):
"""textForRange"""
Barry Wark
pep8 compliance, first pass
r1291 ts = self.textView.textStorage()
return ts.string().substringWithRange_(textRange)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
def currentLine(self):
block = self.textForRange(self.currentBlockRange())
block = block.split('\n')
return block[-1]
def update_cell_prompt(self, result):
Barry Wark
added blockID for failures (special case)
r1283 if(isinstance(result, Failure)):
blockID = result.blockID
else:
blockID = result['blockID']
Barry Wark
CammelCase fixed
r1282 self.insert_text(self.input_prompt(result=result),
Barry Wark
pep8 compliance, first pass
r1291 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
scrollToVisible=False
)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
return result
def render_result(self, result):
blockID = result['blockID']
inputRange = self.blockRanges[blockID]
del self.blockRanges[blockID]
#print inputRange,self.currentBlockRange()
self.insert_text('\n' +
Barry Wark
pep8 compliance, first pass
r1291 self.output_prompt(result) +
result.get('display',{}).get('pprint','') +
'\n\n',
textRange=NSMakeRange(inputRange.location+inputRange.length,
0))
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 return result
def render_error(self, failure):
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 self.insert_text('\n\n'+str(failure)+'\n\n')
self.startNewBlock()
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 return failure
def insert_text(self, string=None, textRange=None, scrollToVisible=True):
Barry Wark
pep8 compliance, first pass
r1291 """Insert text into textView at textRange, updating blockRanges
as necessary
"""
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
if(textRange == None):
textRange = NSMakeRange(self.textView.textStorage().length(), 0) #range for end of text
for r in self.blockRanges.itervalues():
intersection = NSIntersectionRange(r,textRange)
if(intersection.length == 0): #ranges don't intersect
if r.location >= textRange.location:
r.location += len(string)
else: #ranges intersect
if(r.location <= textRange.location):
assert(intersection.length == textRange.length)
r.length += textRange.length
else:
r.location += intersection.length
self.textView.replaceCharactersInRange_withString_(textRange, string) #textStorage().string()
self.textView.setSelectedRange_(NSMakeRange(textRange.location+len(string), 0))
if(scrollToVisible):
self.textView.scrollRangeToVisible_(textRange)
def replaceCurrentBlockWithString(self, textView, string):
textView.replaceCharactersInRange_withString_(self.currentBlockRange(),
string)
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 self.currentBlockRange().length = len(string)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 r = NSMakeRange(textView.textStorage().length(), 0)
textView.scrollRangeToVisible_(r)
textView.setSelectedRange_(r)
def currentIndentString(self):
"""returns string for indent or None if no indent"""
if(len(self.currentBlock()) > 0):
lines = self.currentBlock().split('\n')
currentIndent = len(lines[-1]) - len(lines[-1])
if(currentIndent == 0):
currentIndent = self.tabSpaces
if(self.tabUsesSpaces):
result = ' ' * currentIndent
else:
result = '\t' * (currentIndent/self.tabSpaces)
else:
result = None
return result