##// END OF EJS Templates
updates for frontendbase API. Cocoa plugin functional in Objective-C app
Barry Wark -
Show More
@@ -29,7 +29,8 b' import uuid'
29 29
30 30 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
31 31 NSLog, NSNotificationCenter, NSMakeRange,\
32 NSLocalizedString, NSIntersectionRange
32 NSLocalizedString, NSIntersectionRange,\
33 NSString, NSAutoreleasePool
33 34
34 35 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
35 36 NSTextView, NSRulerView, NSVerticalRuler
@@ -52,7 +53,29 b' from twisted.python.failure import Failure'
52 53 # ThreadedEngineService?
53 54 # 2. integrate Xgrid launching of engines
54 55
56 class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):
57 """Wrap all blocks in an NSAutoreleasePool"""
55 58
59 def wrapped_execute(self, lines):
60 """wrapped_execute"""
61
62 p = NSAutoreleasePool.alloc().init()
63 result = self.shell.execute(lines)
64 p.drain()
65
66 return result
67
68 def execute(self, lines):
69 # Only import this if we are going to use this class
70 from twisted.internet import threads
71
72 msg = {'engineid':self.id,
73 'method':'execute',
74 'args':[lines]}
75
76 d = threads.deferToThread(self.wrapped_execute, lines)
77 d.addCallback(self.addIDToResult)
78 return d
56 79
57 80
58 81 class IPythonCocoaController(NSObject, FrontEndBase):
@@ -62,7 +85,8 b' class IPythonCocoaController(NSObject, FrontEndBase):'
62 85
63 86 def init(self):
64 87 self = super(IPythonCocoaController, self).init()
65 FrontEndBase.__init__(self, engine=ThreadedEngineService())
88 FrontEndBase.__init__(self,
89 engine=AutoreleasePoolWrappedThreadedEngineService())
66 90 if(self != None):
67 91 self._common_init()
68 92
@@ -133,13 +157,42 b' class IPythonCocoaController(NSObject, FrontEndBase):'
133 157 def execute(self, block, blockID=None):
134 158 self.waitingForEngine = True
135 159 self.willChangeValueForKey_('commandHistory')
136 d = super(IPythonCocoaController, self).execute(block, blockID)
160 d = super(IPythonCocoaController, self).execute(block,
161 blockID)
137 162 d.addBoth(self._engine_done)
138 163 d.addCallback(self._update_user_ns)
139 164
140 165 return d
141 166
167
168 def push_(self, namespace):
169 """Push dictionary of key=>values to python namespace"""
170
171 self.waitingForEngine = True
172 self.willChangeValueForKey_('commandHistory')
173 d = self.engine.push(namespace)
174 d.addBoth(self._engine_done)
175 d.addCallback(self._update_user_ns)
176
177
178 def pull_(self, keys):
179 """Pull keys from python namespace"""
142 180
181 self.waitingForEngine = True
182 result = blockingCallFromThread(self.engine.pull, keys)
183 self.waitingForEngine = False
184
185 def executeFileAtPath_(self, path):
186 """Execute file at path in an empty namespace. Update the engine
187 user_ns with the resulting locals."""
188
189 lines,err = NSString.stringWithContentsOfFile_encoding_error_(
190 path,
191 NSString.defaultCStringEncoding(),
192 None)
193 self.engine.execute(lines)
194
195
143 196 def _engine_done(self, x):
144 197 self.waitingForEngine = False
145 198 self.didChangeValueForKey_('commandHistory')
@@ -166,14 +219,14 b' class IPythonCocoaController(NSObject, FrontEndBase):'
166 219 self.didChangeValueForKey_('userNS')
167 220
168 221
169 def update_cell_prompt(self, result):
222 def update_cell_prompt(self, result, blockID=None):
170 223 if(isinstance(result, Failure)):
171 blockID = result.blockID
224 self.insert_text(self.input_prompt(),
225 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
226 scrollToVisible=False
227 )
172 228 else:
173 blockID = result['blockID']
174
175
176 self.insert_text(self.input_prompt(result=result),
229 self.insert_text(self.input_prompt(number=result['number']),
177 230 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
178 231 scrollToVisible=False
179 232 )
@@ -2,4 +2,5 b' include ./plugins.mk'
2 2
3 3 all : dist/IPythonCocoaController.plugin
4 4
5 dist/IPythonCocoaController.plugin : ./FrontendLoader.py ./setup.py No newline at end of file
5 dist/IPythonCocoaController.plugin : ./IPythonCocoaFrontendLoader.py\
6 ./setup.py No newline at end of file
@@ -1,5 +1,4 b''
1 1 %.plugin::
2 mkdir -p plugin
3 2 rm -rf dist/$(notdir $@)
4 3 rm -rf build dist && \
5 4 python setup.py py2app -s
@@ -25,10 +25,10 b' infoPlist = dict('
25 25 )
26 26
27 27 setup(
28 plugin=['FrontendLoader.py'],
28 plugin=['IPythonCocoaFrontendLoader.py'],
29 29 setup_requires=['py2app'],
30 30 options=dict(py2app=dict(
31 31 plist=infoPlist,
32 excludes=['IPython']
32 excludes=['IPython','twisted']
33 33 )),
34 34 ) No newline at end of file
@@ -66,23 +66,20 b' class IFrontEnd(zi.Interface):'
66 66 zi.Attribute("continuation_prompt_template", "string.Template instance\
67 67 substituteable with execute result.")
68 68
69 def update_cell_prompt(self, result):
69 def update_cell_prompt(result, blockID=None):
70 70 """Subclass may override to update the input prompt for a block.
71 71 Since this method will be called as a
72 twisted.internet.defer.Deferred's callback,
72 twisted.internet.defer.Deferred's callback/errback,
73 73 implementations should return result when finished.
74 74
75 NB: result is a failure if the execute returned a failre.
76 To get the blockID, you should do something like::
77 if(isinstance(result, twisted.python.failure.Failure)):
78 blockID = result.blockID
79 else:
80 blockID = result['blockID']
75 Result is a result dict in case of success, and a
76 twisted.python.util.failure.Failure in case of an error
81 77 """
82 78
83 79 pass
84 80
85 def render_result(self, result):
81
82 def render_result(result):
86 83 """Render the result of an execute call. Implementors may choose the
87 84 method of rendering.
88 85 For example, a notebook-style frontend might render a Chaco plot
@@ -90,6 +87,7 b' class IFrontEnd(zi.Interface):'
90 87
91 88 Parameters:
92 89 result : dict (result of IEngineBase.execute )
90 blockID = result['blockID']
93 91
94 92 Result:
95 93 Output of frontend rendering
@@ -97,22 +95,24 b' class IFrontEnd(zi.Interface):'
97 95
98 96 pass
99 97
100 def render_error(self, failure):
98 def render_error(failure):
101 99 """Subclasses must override to render the failure. Since this method
102 ill be called as a twisted.internet.defer.Deferred's callback,
100 will be called as a twisted.internet.defer.Deferred's callback,
103 101 implementations should return result when finished.
102
103 blockID = failure.blockID
104 104 """
105 105
106 106 pass
107 107
108 108
109 def input_prompt(result={}):
109 def input_prompt(number=None):
110 110 """Returns the input prompt by subsituting into
111 111 self.input_prompt_template
112 112 """
113 113 pass
114 114
115 def output_prompt(result):
115 def output_prompt(number=None):
116 116 """Returns the output prompt by subsituting into
117 117 self.output_prompt_template
118 118 """
@@ -180,15 +180,12 b' class FrontEndBase(object):'
180 180 self.history = history
181 181
182 182
183 def input_prompt(self, result={}):
183 def input_prompt(self, number=None):
184 184 """Returns the current input prompt
185 185
186 186 It would be great to use ipython1.core.prompts.Prompt1 here
187 187 """
188
189 result.setdefault('number','')
190
191 return self.input_prompt_template.safe_substitute(result)
188 return self.input_prompt_template.safe_substitute({'number':number})
192 189
193 190
194 191 def continuation_prompt(self):
@@ -196,10 +193,10 b' class FrontEndBase(object):'
196 193
197 194 return self.continuation_prompt_template.safe_substitute()
198 195
199 def output_prompt(self, result):
196 def output_prompt(self, number=None):
200 197 """Returns the output prompt for result"""
201 198
202 return self.output_prompt_template.safe_substitute(result)
199 return self.output_prompt_template.safe_substitute({'number':number})
203 200
204 201
205 202 def is_complete(self, block):
@@ -259,25 +256,33 b' class FrontEndBase(object):'
259 256
260 257 d = self.engine.execute(block)
261 258 d.addCallback(self._add_history, block=block)
262 d.addBoth(self._add_block_id, blockID)
263 d.addBoth(self.update_cell_prompt)
264 d.addCallbacks(self.render_result, errback=self.render_error)
259 d.addCallbacks(self._add_block_id_for_result,
260 errback=self._add_block_id_for_failure,
261 callbackArgs=(blockID,),
262 errbackArgs=(blockID,))
263 d.addBoth(self.update_cell_prompt, blockID=blockID)
264 d.addCallbacks(self.render_result,
265 errback=self.render_error)
265 266
266 267 return d
267 268
268 269
269 def _add_block_id(self, result, blockID):
270 def _add_block_id_for_result(self, result, blockID):
270 271 """Add the blockID to result or failure. Unfortunatley, we have to
271 272 treat failures differently than result dicts.
272 273 """
273 274
274 if(isinstance(result, Failure)):
275 result.blockID = blockID
276 else:
277 result['blockID'] = blockID
275 result['blockID'] = blockID
278 276
279 277 return result
280 278
279 def _add_block_id_for_failure(self, failure, blockID):
280 """_add_block_id_for_failure"""
281
282 failure.blockID = blockID
283 return failure
284
285
281 286 def _add_history(self, result, block=None):
282 287 """Add block to the history"""
283 288
@@ -313,20 +318,11 b' class FrontEndBase(object):'
313 318 # Subclasses probably want to override these methods...
314 319 ###
315 320
316 def update_cell_prompt(self, result):
321 def update_cell_prompt(self, result, blockID=None):
317 322 """Subclass may override to update the input prompt for a block.
318 323 Since this method will be called as a
319 324 twisted.internet.defer.Deferred's callback, implementations should
320 325 return result when finished.
321
322 NB: result is a failure if the execute returned a failre.
323 To get the blockID, you should do something like::
324 if(isinstance(result, twisted.python.failure.Failure)):
325 blockID = result.blockID
326 else:
327 blockID = result['blockID']
328
329
330 326 """
331 327
332 328 return result
@@ -344,7 +340,8 b' class FrontEndBase(object):'
344 340 def render_error(self, failure):
345 341 """Subclasses must override to render the failure. Since this method
346 342 will be called as a twisted.internet.defer.Deferred's callback,
347 implementations should return result when finished."""
343 implementations should return result when finished.
344 """
348 345
349 346 return failure
350 347
@@ -28,7 +28,7 b' class FrontEndCallbackChecker(frontendbase.FrontEndBase):'
28 28 self.renderResultCalled = False
29 29 self.renderErrorCalled = False
30 30
31 def update_cell_prompt(self, result):
31 def update_cell_prompt(self, result, blockID=None):
32 32 self.updateCalled = True
33 33 return result
34 34
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now