##// END OF EJS Templates
pep8 compliance, first pass
Barry Wark -
Show More
@@ -1,27 +1,28 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
2 # -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*-
3
3
4 """PyObjC classes to provide a Cocoa frontend to the ipython1.kernel.engineservice.EngineService.
4 """PyObjC classes to provide a Cocoa frontend to the
5 IPython.kernel.engineservice.EngineService.
5
6
6 The Cocoa frontend is divided into two classes:
7 To add an IPython interpreter to a cocoa app, instantiate an
7 - IPythonCocoaController
8 IPythonCocoaController in a XIB and connect its textView outlet to an
8 - IPythonCLITextViewDelegate
9 NSTextView instance in your UI. That's it.
9
10
10 To add an IPython interpreter to a cocoa app, instantiate both of these classes in an XIB...[FINISH]
11 Author: Barry Wark
11 """
12 """
12
13
13 __docformat__ = "restructuredtext en"
14 __docformat__ = "restructuredtext en"
14
15
15 #-------------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
16 # Copyright (C) 2008 Barry Wark <barrywark@gmail.com>
17 # Copyright (C) 2008 The IPython Development Team
17 #
18 #
18 # Distributed under the terms of the BSD License. The full license is in
19 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
20 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
21
22
22 #-------------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
23 # Imports
24 # Imports
24 #-------------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
25
26
26 import objc
27 import objc
27 import uuid
28 import uuid
@@ -42,12 +43,13 b' from IPython.frontend.frontendbase import FrontEndBase'
42 from twisted.internet.threads import blockingCallFromThread
43 from twisted.internet.threads import blockingCallFromThread
43 from twisted.python.failure import Failure
44 from twisted.python.failure import Failure
44
45
45 #-------------------------------------------------------------------------------
46 #------------------------------------------------------------------------------
46 # Classes to implement the Cocoa frontend
47 # Classes to implement the Cocoa frontend
47 #-------------------------------------------------------------------------------
48 #------------------------------------------------------------------------------
48
49
49 # TODO:
50 # TODO:
50 # 1. use MultiEngineClient and out-of-process engine rather than ThreadedEngineService?
51 # 1. use MultiEngineClient and out-of-process engine rather than
52 # ThreadedEngineService?
51 # 2. integrate Xgrid launching of engines
53 # 2. integrate Xgrid launching of engines
52
54
53
55
@@ -89,16 +91,17 b' class IPythonCocoaController(NSObject, FrontEndBase):'
89 NSLog('IPython engine started')
91 NSLog('IPython engine started')
90
92
91 # Register for app termination
93 # Register for app termination
92 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self,
94 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
93 'appWillTerminate:',
95 self,
94 NSApplicationWillTerminateNotification,
96 'appWillTerminate:',
95 None)
97 NSApplicationWillTerminateNotification,
98 None)
96
99
97 self.textView.setDelegate_(self)
100 self.textView.setDelegate_(self)
98 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
101 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
99 self.verticalRulerView = NSRulerView.alloc().initWithScrollView_orientation_(
102 self.verticalRulerView = NSRulerView.alloc().initWithScrollView_orientation_(
100 self.textView.enclosingScrollView(),
103 self.textView.enclosingScrollView(),
101 NSVerticalRuler)
104 NSVerticalRuler)
102 self.verticalRulerView.setClientView_(self.textView)
105 self.verticalRulerView.setClientView_(self.textView)
103 self.startCLIForTextView()
106 self.startCLIForTextView()
104
107
@@ -163,7 +166,8 b' class IPythonCocoaController(NSObject, FrontEndBase):'
163 def startCLIForTextView(self):
166 def startCLIForTextView(self):
164 """Print banner"""
167 """Print banner"""
165
168
166 banner = """IPython1 %s -- An enhanced Interactive Python.""" % IPython.__version__
169 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
170 IPython.__version__
167
171
168 self.insert_text(banner + '\n\n')
172 self.insert_text(banner + '\n\n')
169
173
@@ -206,14 +210,18 b' class IPythonCocoaController(NSObject, FrontEndBase):'
206 return True
210 return True
207
211
208 elif(selector == 'moveToBeginningOfParagraph:'):
212 elif(selector == 'moveToBeginningOfParagraph:'):
209 textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location, 0))
213 textView.setSelectedRange_(NSMakeRange(
214 self.currentBlockRange().location,
215 0))
210 return True
216 return True
211 elif(selector == 'moveToEndOfParagraph:'):
217 elif(selector == 'moveToEndOfParagraph:'):
212 textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location + \
218 textView.setSelectedRange_(NSMakeRange(
213 self.currentBlockRange().length, 0))
219 self.currentBlockRange().location + \
220 self.currentBlockRange().length, 0))
214 return True
221 return True
215 elif(selector == 'deleteToEndOfParagraph:'):
222 elif(selector == 'deleteToEndOfParagraph:'):
216 if(textView.selectedRange().location <= self.currentBlockRange().location):
223 if(textView.selectedRange().location <= \
224 self.currentBlockRange().location):
217 # Intersect the selected range with the current line range
225 # Intersect the selected range with the current line range
218 if(self.currentBlockRange().length < 0):
226 if(self.currentBlockRange().length < 0):
219 self.blockRanges[self.currentBlockID].length = 0
227 self.blockRanges[self.currentBlockID].length = 0
@@ -235,7 +243,8 b' class IPythonCocoaController(NSObject, FrontEndBase):'
235
243
236 elif(selector == 'deleteBackward:'):
244 elif(selector == 'deleteBackward:'):
237 #if we're at the beginning of the current block, ignore
245 #if we're at the beginning of the current block, ignore
238 if(textView.selectedRange().location == self.currentBlockRange().location):
246 if(textView.selectedRange().location == \
247 self.currentBlockRange().location):
239 return True
248 return True
240 else:
249 else:
241 self.currentBlockRange().length-=1
250 self.currentBlockRange().length-=1
@@ -243,14 +252,15 b' class IPythonCocoaController(NSObject, FrontEndBase):'
243 return False
252 return False
244
253
245
254
246 def textView_shouldChangeTextInRanges_replacementStrings_(self, textView, ranges, replacementStrings):
255 def textView_shouldChangeTextInRanges_replacementStrings_(self,
256 textView, ranges, replacementStrings):
247 """
257 """
248 Delegate method for NSTextView.
258 Delegate method for NSTextView.
249
259
250 Refuse change text in ranges not at end, but make those changes at end.
260 Refuse change text in ranges not at end, but make those changes at
261 end.
251 """
262 """
252
263
253 #print 'textView_shouldChangeTextInRanges_replacementStrings_:',ranges,replacementStrings
254 assert(len(ranges) == len(replacementStrings))
264 assert(len(ranges) == len(replacementStrings))
255 allow = True
265 allow = True
256 for r,s in zip(ranges, replacementStrings):
266 for r,s in zip(ranges, replacementStrings):
@@ -261,13 +271,17 b' class IPythonCocoaController(NSObject, FrontEndBase):'
261 allow = False
271 allow = False
262
272
263
273
264 self.blockRanges.setdefault(self.currentBlockID, self.currentBlockRange()).length += len(s)
274 self.blockRanges.setdefault(self.currentBlockID,
275 self.currentBlockRange()).length +=\
276 len(s)
265
277
266 return allow
278 return allow
267
279
268 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, textView, words, charRange, index):
280 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
281 textView, words, charRange, index):
269 try:
282 try:
270 token = textView.textStorage().string().substringWithRange_(charRange)
283 ts = textView.textStorage()
284 token = ts.string().substringWithRange_(charRange)
271 completions = blockingCallFromThread(self.complete, token)
285 completions = blockingCallFromThread(self.complete, token)
272 except:
286 except:
273 completions = objc.nil
287 completions = objc.nil
@@ -288,7 +302,9 b' class IPythonCocoaController(NSObject, FrontEndBase):'
288 return uuid.uuid4()
302 return uuid.uuid4()
289
303
290 def currentBlockRange(self):
304 def currentBlockRange(self):
291 return self.blockRanges.get(self.currentBlockID, NSMakeRange(self.textView.textStorage().length(), 0))
305 return self.blockRanges.get(self.currentBlockID,
306 NSMakeRange(self.textView.textStorage().length(),
307 0))
292
308
293 def currentBlock(self):
309 def currentBlock(self):
294 """The current block's text"""
310 """The current block's text"""
@@ -298,7 +314,8 b' class IPythonCocoaController(NSObject, FrontEndBase):'
298 def textForRange(self, textRange):
314 def textForRange(self, textRange):
299 """textForRange"""
315 """textForRange"""
300
316
301 return self.textView.textStorage().string().substringWithRange_(textRange)
317 ts = self.textView.textStorage()
318 return ts.string().substringWithRange_(textRange)
302
319
303 def currentLine(self):
320 def currentLine(self):
304 block = self.textForRange(self.currentBlockRange())
321 block = self.textForRange(self.currentBlockRange())
@@ -313,9 +330,9 b' class IPythonCocoaController(NSObject, FrontEndBase):'
313
330
314
331
315 self.insert_text(self.input_prompt(result=result),
332 self.insert_text(self.input_prompt(result=result),
316 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
333 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
317 scrollToVisible=False
334 scrollToVisible=False
318 )
335 )
319
336
320 return result
337 return result
321
338
@@ -327,10 +344,11 b' class IPythonCocoaController(NSObject, FrontEndBase):'
327
344
328 #print inputRange,self.currentBlockRange()
345 #print inputRange,self.currentBlockRange()
329 self.insert_text('\n' +
346 self.insert_text('\n' +
330 self.output_prompt(result) +
347 self.output_prompt(result) +
331 result.get('display',{}).get('pprint','') +
348 result.get('display',{}).get('pprint','') +
332 '\n\n',
349 '\n\n',
333 textRange=NSMakeRange(inputRange.location+inputRange.length, 0))
350 textRange=NSMakeRange(inputRange.location+inputRange.length,
351 0))
334 return result
352 return result
335
353
336
354
@@ -341,7 +359,9 b' class IPythonCocoaController(NSObject, FrontEndBase):'
341
359
342
360
343 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
361 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
344 """Insert text into textView at textRange, updating blockRanges as necessary"""
362 """Insert text into textView at textRange, updating blockRanges
363 as necessary
364 """
345
365
346 if(textRange == None):
366 if(textRange == None):
347 textRange = NSMakeRange(self.textView.textStorage().length(), 0) #range for end of text
367 textRange = NSMakeRange(self.textView.textStorage().length(), 0) #range for end of text
@@ -1,27 +1,20 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """This file contains unittests for the ipython1.frontend.cocoa.cocoa_frontend module.
2 """This file contains unittests for the
3
3 IPython.frontend.cocoa.cocoa_frontend module.
4 Things that should be tested:
5
6 - IPythonCocoaController instantiates an IEngineInteractive
7 - IPythonCocoaController executes code on the engine
8 - IPythonCocoaController mirrors engine's user_ns
9 """
4 """
10 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
11
6
12 #-------------------------------------------------------------------------------
7 #---------------------------------------------------------------------------
13 # Copyright (C) 2005 Fernando Perez <fperez@colorado.edu>
8 # Copyright (C) 2005 The IPython Development Team
14 # Brian E Granger <ellisonbg@gmail.com>
9 #
15 # Benjamin Ragan-Kelley <benjaminrk@gmail.com>
10 # Distributed under the terms of the BSD License. The full license is in
16 #
11 # the file COPYING, distributed as part of this software.
17 # Distributed under the terms of the BSD License. The full license is in
12 #---------------------------------------------------------------------------
18 # the file COPYING, distributed as part of this software.
13
19 #-------------------------------------------------------------------------------
14 #---------------------------------------------------------------------------
20
15 # Imports
21 #-------------------------------------------------------------------------------
16 #---------------------------------------------------------------------------
22 # Imports
17 from IPython.kernel.core.interpreter import Interpreter
23 #-------------------------------------------------------------------------------
24 from IPython.kernel.core.interpreter import Interpreter
25 import IPython.kernel.engineservice as es
18 import IPython.kernel.engineservice as es
26 from IPython.testing.util import DeferredTestCase
19 from IPython.testing.util import DeferredTestCase
27 from twisted.internet.defer import succeed
20 from twisted.internet.defer import succeed
@@ -51,7 +44,9 b' class TestIPythonCocoaControler(DeferredTestCase):'
51 del result['number']
44 del result['number']
52 del result['id']
45 del result['id']
53 return result
46 return result
54 self.assertDeferredEquals(self.controller.execute(code).addCallback(removeNumberAndID), expected)
47 self.assertDeferredEquals(
48 self.controller.execute(code).addCallback(removeNumberAndID),
49 expected)
55
50
56 def testControllerMirrorsUserNSWithValuesAsStrings(self):
51 def testControllerMirrorsUserNSWithValuesAsStrings(self):
57 code = """userns1=1;userns2=2"""
52 code = """userns1=1;userns2=2"""
@@ -1,6 +1,8 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
2 """
3 """
3 frontendbase provides an interface and base class for GUI frontends for IPython.kernel/IPython.kernel.core.
4 frontendbase provides an interface and base class for GUI frontends for
5 IPython.kernel/IPython.kernel.core.
4
6
5 Frontend implementations will likely want to subclass FrontEndBase.
7 Frontend implementations will likely want to subclass FrontEndBase.
6
8
@@ -57,20 +59,26 b' class IFrontEndFactory(zi.Interface):'
57 class IFrontEnd(zi.Interface):
59 class IFrontEnd(zi.Interface):
58 """Interface for frontends. All methods return t.i.d.Deferred"""
60 """Interface for frontends. All methods return t.i.d.Deferred"""
59
61
60 zi.Attribute("input_prompt_template", "string.Template instance substituteable with execute result.")
62 zi.Attribute("input_prompt_template", "string.Template instance\
61 zi.Attribute("output_prompt_template", "string.Template instance substituteable with execute result.")
63 substituteable with execute result.")
62 zi.Attribute("continuation_prompt_template", "string.Template instance substituteable with execute result.")
64 zi.Attribute("output_prompt_template", "string.Template instance\
65 substituteable with execute result.")
66 zi.Attribute("continuation_prompt_template", "string.Template instance\
67 substituteable with execute result.")
63
68
64 def update_cell_prompt(self, result):
69 def update_cell_prompt(self, result):
65 """Subclass may override to update the input prompt for a block.
70 """Subclass may override to update the input prompt for a block.
66 Since this method will be called as a twisted.internet.defer.Deferred's callback,
71 Since this method will be called as a
72 twisted.internet.defer.Deferred's callback,
67 implementations should return result when finished."""
73 implementations should return result when finished."""
68
74
69 pass
75 pass
70
76
71 def render_result(self, result):
77 def render_result(self, result):
72 """Render the result of an execute call. Implementors may choose the method of rendering.
78 """Render the result of an execute call. Implementors may choose the
73 For example, a notebook-style frontend might render a Chaco plot inline.
79 method of rendering.
80 For example, a notebook-style frontend might render a Chaco plot
81 inline.
74
82
75 Parameters:
83 Parameters:
76 result : dict (result of IEngineBase.execute )
84 result : dict (result of IEngineBase.execute )
@@ -82,24 +90,31 b' class IFrontEnd(zi.Interface):'
82 pass
90 pass
83
91
84 def render_error(self, failure):
92 def render_error(self, failure):
85 """Subclasses must override to render the failure. Since this method will be called as a
93 """Subclasses must override to render the failure. Since this method
86 twisted.internet.defer.Deferred's callback, implementations should return result
94 ill be called as a twisted.internet.defer.Deferred's callback,
87 when finished."""
95 implementations should return result when finished.
96 """
88
97
89 pass
98 pass
90
99
91
100
92 def input_prompt(result={}):
101 def input_prompt(result={}):
93 """Returns the input prompt by subsituting into self.input_prompt_template"""
102 """Returns the input prompt by subsituting into
103 self.input_prompt_template
104 """
94 pass
105 pass
95
106
96 def output_prompt(result):
107 def output_prompt(result):
97 """Returns the output prompt by subsituting into self.output_prompt_template"""
108 """Returns the output prompt by subsituting into
109 self.output_prompt_template
110 """
98
111
99 pass
112 pass
100
113
101 def continuation_prompt():
114 def continuation_prompt():
102 """Returns the continuation prompt by subsituting into self.continuation_prompt_template"""
115 """Returns the continuation prompt by subsituting into
116 self.continuation_prompt_template
117 """
103
118
104 pass
119 pass
105
120
@@ -221,7 +236,8 b' class FrontEndBase(object):'
221 Parameters:
236 Parameters:
222 block : {str, AST}
237 block : {str, AST}
223 blockID : any
238 blockID : any
224 Caller may provide an ID to identify this block. result['blockID'] := blockID
239 Caller may provide an ID to identify this block.
240 result['blockID'] := blockID
225
241
226 Result:
242 Result:
227 Deferred result of self.interpreter.execute
243 Deferred result of self.interpreter.execute
@@ -243,8 +259,8 b' class FrontEndBase(object):'
243
259
244
260
245 def _add_block_id(self, result, blockID):
261 def _add_block_id(self, result, blockID):
246 """Add the blockID to result or failure. Unfortunatley, we have to treat failures
262 """Add the blockID to result or failure. Unfortunatley, we have to
247 differently than result dicts
263 treat failures differently than result dicts.
248 """
264 """
249
265
250 if(isinstance(result, Failure)):
266 if(isinstance(result, Failure)):
@@ -291,11 +307,12 b' class FrontEndBase(object):'
291
307
292 def update_cell_prompt(self, result):
308 def update_cell_prompt(self, result):
293 """Subclass may override to update the input prompt for a block.
309 """Subclass may override to update the input prompt for a block.
294 Since this method will be called as a twisted.internet.defer.Deferred's callback,
310 Since this method will be called as a
295 implementations should return result when finished.
311 twisted.internet.defer.Deferred's callback, implementations should
312 return result when finished.
296
313
297 NP: result is a failure if the execute returned a failre. To get the blockID, you should
314 NP: result is a failure if the execute returned a failre.
298 do something like::
315 To get the blockID, you should do something like::
299 if(isinstance(result, twisted.python.failure.Failure)):
316 if(isinstance(result, twisted.python.failure.Failure)):
300 blockID = result.blockID
317 blockID = result.blockID
301 else:
318 else:
@@ -308,17 +325,18 b' class FrontEndBase(object):'
308
325
309
326
310 def render_result(self, result):
327 def render_result(self, result):
311 """Subclasses must override to render result. Since this method will be called as a
328 """Subclasses must override to render result. Since this method will
312 twisted.internet.defer.Deferred's callback, implementations should return result
329 be called as a twisted.internet.defer.Deferred's callback,
313 when finished."""
330 implementations should return result when finished.
331 """
314
332
315 return result
333 return result
316
334
317
335
318 def render_error(self, failure):
336 def render_error(self, failure):
319 """Subclasses must override to render the failure. Since this method will be called as a
337 """Subclasses must override to render the failure. Since this method
320 twisted.internet.defer.Deferred's callback, implementations should return result
338 will be called as a twisted.internet.defer.Deferred's callback,
321 when finished."""
339 implementations should return result when finished."""
322
340
323 return failure
341 return failure
324
342
@@ -4,16 +4,16 b''
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #---------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #---------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #---------------------------------------------------------------------------
17
17
18 import unittest
18 import unittest
19 from IPython.frontend import frontendbase
19 from IPython.frontend import frontendbase
@@ -22,7 +22,8 b' from IPython.kernel.engineservice import EngineService'
22 class FrontEndCallbackChecker(frontendbase.FrontEndBase):
22 class FrontEndCallbackChecker(frontendbase.FrontEndBase):
23 """FrontEndBase subclass for checking callbacks"""
23 """FrontEndBase subclass for checking callbacks"""
24 def __init__(self, engine=None, history=None):
24 def __init__(self, engine=None, history=None):
25 super(FrontEndCallbackChecker, self).__init__(engine=engine, history=history)
25 super(FrontEndCallbackChecker, self).__init__(engine=engine,
26 history=history)
26 self.updateCalled = False
27 self.updateCalled = False
27 self.renderResultCalled = False
28 self.renderResultCalled = False
28 self.renderErrorCalled = False
29 self.renderErrorCalled = False
@@ -51,7 +52,8 b' class TestFrontendBase(unittest.TestCase):'
51
52
52
53
53 def test_implements_IFrontEnd(self):
54 def test_implements_IFrontEnd(self):
54 assert(frontendbase.IFrontEnd.implementedBy(frontendbase.FrontEndBase))
55 assert(frontendbase.IFrontEnd.implementedBy(
56 frontendbase.FrontEndBase))
55
57
56
58
57 def test_is_complete_returns_False_for_incomplete_block(self):
59 def test_is_complete_returns_False_for_incomplete_block(self):
@@ -847,11 +847,13 b' class Command(object):'
847 self.deferred.errback(reason)
847 self.deferred.errback(reason)
848
848
849 class ThreadedEngineService(EngineService):
849 class ThreadedEngineService(EngineService):
850 """An EngineService subclass that defers execute commands to a separate thread.
850 """An EngineService subclass that defers execute commands to a separate
851 thread.
851
852
852 ThreadedEngineService uses twisted.internet.threads.deferToThread to defer execute
853 ThreadedEngineService uses twisted.internet.threads.deferToThread to
853 requests to a separate thread. GUI frontends may want to use ThreadedEngineService as
854 defer execute requests to a separate thread. GUI frontends may want to
854 the engine in an IPython.frontend.frontendbase.FrontEndBase subclass to prevent
855 use ThreadedEngineService as the engine in an
856 IPython.frontend.frontendbase.FrontEndBase subclass to prevent
855 block execution from blocking the GUI thread.
857 block execution from blocking the GUI thread.
856 """
858 """
857
859
General Comments 0
You need to be logged in to leave comments. Login now