##// END OF EJS Templates
Delete all references to old IPython.external.guid...
Thomas Spura -
Show More
@@ -1,82 +1,82 b''
1 """
1 """
2 Base front end class for all async frontends.
2 Base front end class for all async frontends.
3 """
3 """
4 __docformat__ = "restructuredtext en"
4 __docformat__ = "restructuredtext en"
5
5
6 # Tell nose to skip this module
6 # Tell nose to skip this module
7 __test__ = {}
7 __test__ = {}
8
8
9 #-------------------------------------------------------------------------------
9 #-------------------------------------------------------------------------------
10 # Copyright (C) 2008-2011 The IPython Development Team
10 # Copyright (C) 2008-2011 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15
15
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 # Third-party
20 # Third-party
21 from twisted.python.failure import Failure
21 from twisted.python.failure import Failure
22 from zope.interface import implements, classProvides
22 from zope.interface import implements, classProvides
23
23
24 # From IPython
24 # From IPython
25 from IPython.external import guid
26
27 from IPython.frontend.frontendbase import (FrontEndBase, IFrontEnd,
25 from IPython.frontend.frontendbase import (FrontEndBase, IFrontEnd,
28 IFrontEndFactory)
26 IFrontEndFactory)
29 from IPython.kernel.core.history import FrontEndHistory
27 from IPython.kernel.core.history import FrontEndHistory
30 from IPython.kernel.engineservice import IEngineCore
28 from IPython.kernel.engineservice import IEngineCore
31
29
30 import uuid
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Classes and functions
33 # Classes and functions
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 class AsyncFrontEndBase(FrontEndBase):
36 class AsyncFrontEndBase(FrontEndBase):
37 """
37 """
38 Overrides FrontEndBase to wrap execute in a deferred result.
38 Overrides FrontEndBase to wrap execute in a deferred result.
39 All callbacks are made as callbacks on the deferred result.
39 All callbacks are made as callbacks on the deferred result.
40 """
40 """
41
41
42 implements(IFrontEnd)
42 implements(IFrontEnd)
43 classProvides(IFrontEndFactory)
43 classProvides(IFrontEndFactory)
44
44
45 def __init__(self, engine=None, history=None):
45 def __init__(self, engine=None, history=None):
46 assert(engine==None or IEngineCore.providedBy(engine))
46 assert(engine==None or IEngineCore.providedBy(engine))
47 self.engine = IEngineCore(engine)
47 self.engine = IEngineCore(engine)
48 if history is None:
48 if history is None:
49 self.history = FrontEndHistory(input_cache=[''])
49 self.history = FrontEndHistory(input_cache=[''])
50 else:
50 else:
51 self.history = history
51 self.history = history
52
52
53 def execute(self, block, blockID=None):
53 def execute(self, block, blockID=None):
54 """Execute the block and return the deferred result.
54 """Execute the block and return the deferred result.
55
55
56 Parameters:
56 Parameters:
57 block : {str, AST}
57 block : {str, AST}
58 blockID : any
58 blockID : any
59 Caller may provide an ID to identify this block.
59 Caller may provide an ID to identify this block.
60 result['blockID'] := blockID
60 result['blockID'] := blockID
61
61
62 Result:
62 Result:
63 Deferred result of self.interpreter.execute
63 Deferred result of self.interpreter.execute
64 """
64 """
65
65
66 if(not self.is_complete(block)):
66 if(not self.is_complete(block)):
67 return Failure(Exception("Block is not compilable"))
67 return Failure(Exception("Block is not compilable"))
68
68
69 if(blockID == None):
69 if(blockID == None):
70 blockID = guid.generate()
70 blockID = uuid.uuid4()
71
71
72 d = self.engine.execute(block)
72 d = self.engine.execute(block)
73 d.addCallback(self._add_history, block=block)
73 d.addCallback(self._add_history, block=block)
74 d.addCallbacks(self._add_block_id_for_result,
74 d.addCallbacks(self._add_block_id_for_result,
75 errback=self._add_block_id_for_failure,
75 errback=self._add_block_id_for_failure,
76 callbackArgs=(blockID,),
76 callbackArgs=(blockID,),
77 errbackArgs=(blockID,))
77 errbackArgs=(blockID,))
78 d.addBoth(self.update_cell_prompt, blockID=blockID)
78 d.addBoth(self.update_cell_prompt, blockID=blockID)
79 d.addCallbacks(self.render_result,
79 d.addCallbacks(self.render_result,
80 errback=self.render_error)
80 errback=self.render_error)
81
81
82 return d
82 return d
@@ -1,560 +1,560 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.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
4 """PyObjC classes to provide a Cocoa frontend to the
5 IPython.kernel.engineservice.IEngineBase.
5 IPython.kernel.engineservice.IEngineBase.
6
6
7 To add an IPython interpreter to a cocoa app, instantiate an
7 To add an IPython interpreter to a cocoa app, instantiate an
8 IPythonCocoaController in a XIB and connect its textView outlet to an
8 IPythonCocoaController in a XIB and connect its textView outlet to an
9 NSTextView instance in your UI. That's it.
9 NSTextView instance in your UI. That's it.
10
10
11 Author: Barry Wark
11 Author: Barry Wark
12 """
12 """
13
13
14 __docformat__ = "restructuredtext en"
14 __docformat__ = "restructuredtext en"
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Copyright (C) 2008-2011 The IPython Development Team
17 # Copyright (C) 2008-2011 The IPython Development Team
18 #
18 #
19 # 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
20 # the file COPYING, distributed as part of this software.
20 # the file COPYING, distributed as part of this software.
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Imports
24 # Imports
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 import sys
27 import sys
28 import objc
28 import objc
29 from IPython.external import guid
29 import uuid
30
30
31 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
31 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
32 NSLog, NSNotificationCenter, NSMakeRange,\
32 NSLog, NSNotificationCenter, NSMakeRange,\
33 NSLocalizedString, NSIntersectionRange,\
33 NSLocalizedString, NSIntersectionRange,\
34 NSString, NSAutoreleasePool
34 NSString, NSAutoreleasePool
35
35
36 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
36 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
37 NSTextView, NSRulerView, NSVerticalRuler
37 NSTextView, NSRulerView, NSVerticalRuler
38
38
39 from pprint import saferepr
39 from pprint import saferepr
40
40
41 import IPython
41 import IPython
42 from IPython.kernel.engineservice import ThreadedEngineService
42 from IPython.kernel.engineservice import ThreadedEngineService
43 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
43 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
44
44
45 from twisted.internet.threads import blockingCallFromThread
45 from twisted.internet.threads import blockingCallFromThread
46 from twisted.python.failure import Failure
46 from twisted.python.failure import Failure
47
47
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49 # Classes to implement the Cocoa frontend
49 # Classes to implement the Cocoa frontend
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51
51
52 # TODO:
52 # TODO:
53 # 1. use MultiEngineClient and out-of-process engine rather than
53 # 1. use MultiEngineClient and out-of-process engine rather than
54 # ThreadedEngineService?
54 # ThreadedEngineService?
55 # 2. integrate Xgrid launching of engines
55 # 2. integrate Xgrid launching of engines
56
56
57 class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):
57 class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):
58 """Wrap all blocks in an NSAutoreleasePool"""
58 """Wrap all blocks in an NSAutoreleasePool"""
59
59
60 def wrapped_execute(self, msg, lines):
60 def wrapped_execute(self, msg, lines):
61 """wrapped_execute"""
61 """wrapped_execute"""
62 try:
62 try:
63 p = NSAutoreleasePool.alloc().init()
63 p = NSAutoreleasePool.alloc().init()
64 result = super(AutoreleasePoolWrappedThreadedEngineService,
64 result = super(AutoreleasePoolWrappedThreadedEngineService,
65 self).wrapped_execute(msg, lines)
65 self).wrapped_execute(msg, lines)
66 finally:
66 finally:
67 p.drain()
67 p.drain()
68
68
69 return result
69 return result
70
70
71
71
72
72
73 class Cell(NSObject):
73 class Cell(NSObject):
74 """
74 """
75 Representation of the prompts, input and output of a cell in the
75 Representation of the prompts, input and output of a cell in the
76 frontend
76 frontend
77 """
77 """
78
78
79 blockNumber = objc.ivar().unsigned_long()
79 blockNumber = objc.ivar().unsigned_long()
80 blockID = objc.ivar()
80 blockID = objc.ivar()
81 inputBlock = objc.ivar()
81 inputBlock = objc.ivar()
82 output = objc.ivar()
82 output = objc.ivar()
83
83
84
84
85
85
86 class CellBlock(object):
86 class CellBlock(object):
87 """
87 """
88 Storage for information about text ranges relating to a single cell
88 Storage for information about text ranges relating to a single cell
89 """
89 """
90
90
91
91
92 def __init__(self, inputPromptRange, inputRange=None, outputPromptRange=None,
92 def __init__(self, inputPromptRange, inputRange=None, outputPromptRange=None,
93 outputRange=None):
93 outputRange=None):
94 super(CellBlock, self).__init__()
94 super(CellBlock, self).__init__()
95 self.inputPromptRange = inputPromptRange
95 self.inputPromptRange = inputPromptRange
96 self.inputRange = inputRange
96 self.inputRange = inputRange
97 self.outputPromptRange = outputPromptRange
97 self.outputPromptRange = outputPromptRange
98 self.outputRange = outputRange
98 self.outputRange = outputRange
99
99
100 def update_ranges_for_insertion(self, text, textRange):
100 def update_ranges_for_insertion(self, text, textRange):
101 """Update ranges for text insertion at textRange"""
101 """Update ranges for text insertion at textRange"""
102
102
103 for r in [self.inputPromptRange,self.inputRange,
103 for r in [self.inputPromptRange,self.inputRange,
104 self.outputPromptRange, self.outputRange]:
104 self.outputPromptRange, self.outputRange]:
105 if(r == None):
105 if(r == None):
106 continue
106 continue
107 intersection = NSIntersectionRange(r,textRange)
107 intersection = NSIntersectionRange(r,textRange)
108 if(intersection.length == 0): #ranges don't intersect
108 if(intersection.length == 0): #ranges don't intersect
109 if r.location >= textRange.location:
109 if r.location >= textRange.location:
110 r.location += len(text)
110 r.location += len(text)
111 else: #ranges intersect
111 else: #ranges intersect
112 if(r.location > textRange.location):
112 if(r.location > textRange.location):
113 offset = len(text) - intersection.length
113 offset = len(text) - intersection.length
114 r.length -= offset
114 r.length -= offset
115 r.location += offset
115 r.location += offset
116 elif(r.location == textRange.location):
116 elif(r.location == textRange.location):
117 r.length += len(text) - intersection.length
117 r.length += len(text) - intersection.length
118 else:
118 else:
119 r.length -= intersection.length
119 r.length -= intersection.length
120
120
121
121
122 def update_ranges_for_deletion(self, textRange):
122 def update_ranges_for_deletion(self, textRange):
123 """Update ranges for text deletion at textRange"""
123 """Update ranges for text deletion at textRange"""
124
124
125 for r in [self.inputPromptRange,self.inputRange,
125 for r in [self.inputPromptRange,self.inputRange,
126 self.outputPromptRange, self.outputRange]:
126 self.outputPromptRange, self.outputRange]:
127 if(r==None):
127 if(r==None):
128 continue
128 continue
129 intersection = NSIntersectionRange(r, textRange)
129 intersection = NSIntersectionRange(r, textRange)
130 if(intersection.length == 0): #ranges don't intersect
130 if(intersection.length == 0): #ranges don't intersect
131 if r.location >= textRange.location:
131 if r.location >= textRange.location:
132 r.location -= textRange.length
132 r.location -= textRange.length
133 else: #ranges intersect
133 else: #ranges intersect
134 if(r.location > textRange.location):
134 if(r.location > textRange.location):
135 offset = intersection.length
135 offset = intersection.length
136 r.length -= offset
136 r.length -= offset
137 r.location += offset
137 r.location += offset
138 elif(r.location == textRange.location):
138 elif(r.location == textRange.location):
139 r.length += intersection.length
139 r.length += intersection.length
140 else:
140 else:
141 r.length -= intersection.length
141 r.length -= intersection.length
142
142
143 def __repr__(self):
143 def __repr__(self):
144 return 'CellBlock('+ str((self.inputPromptRange,
144 return 'CellBlock('+ str((self.inputPromptRange,
145 self.inputRange,
145 self.inputRange,
146 self.outputPromptRange,
146 self.outputPromptRange,
147 self.outputRange)) + ')'
147 self.outputRange)) + ')'
148
148
149
149
150
150
151
151
152 class IPythonCocoaController(NSObject, AsyncFrontEndBase):
152 class IPythonCocoaController(NSObject, AsyncFrontEndBase):
153 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
153 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
154 waitingForEngine = objc.ivar().bool()
154 waitingForEngine = objc.ivar().bool()
155 textView = objc.IBOutlet()
155 textView = objc.IBOutlet()
156
156
157 def init(self):
157 def init(self):
158 self = super(IPythonCocoaController, self).init()
158 self = super(IPythonCocoaController, self).init()
159 AsyncFrontEndBase.__init__(self,
159 AsyncFrontEndBase.__init__(self,
160 engine=AutoreleasePoolWrappedThreadedEngineService())
160 engine=AutoreleasePoolWrappedThreadedEngineService())
161 if(self != None):
161 if(self != None):
162 self._common_init()
162 self._common_init()
163
163
164 return self
164 return self
165
165
166 def _common_init(self):
166 def _common_init(self):
167 """_common_init"""
167 """_common_init"""
168
168
169 self.userNS = NSMutableDictionary.dictionary()
169 self.userNS = NSMutableDictionary.dictionary()
170 self.waitingForEngine = False
170 self.waitingForEngine = False
171
171
172 self.lines = {}
172 self.lines = {}
173 self.tabSpaces = 4
173 self.tabSpaces = 4
174 self.tabUsesSpaces = True
174 self.tabUsesSpaces = True
175 self.currentBlockID = self.next_block_ID()
175 self.currentBlockID = self.next_block_ID()
176 self.blockRanges = {} # blockID=>CellBlock
176 self.blockRanges = {} # blockID=>CellBlock
177
177
178
178
179 def awakeFromNib(self):
179 def awakeFromNib(self):
180 """awakeFromNib"""
180 """awakeFromNib"""
181
181
182 self._common_init()
182 self._common_init()
183
183
184 # Start the IPython engine
184 # Start the IPython engine
185 self.engine.startService()
185 self.engine.startService()
186 NSLog('IPython engine started')
186 NSLog('IPython engine started')
187
187
188 # Register for app termination
188 # Register for app termination
189 nc = NSNotificationCenter.defaultCenter()
189 nc = NSNotificationCenter.defaultCenter()
190 nc.addObserver_selector_name_object_(
190 nc.addObserver_selector_name_object_(
191 self,
191 self,
192 'appWillTerminate:',
192 'appWillTerminate:',
193 NSApplicationWillTerminateNotification,
193 NSApplicationWillTerminateNotification,
194 None)
194 None)
195
195
196 self.textView.setDelegate_(self)
196 self.textView.setDelegate_(self)
197 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
197 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
198 r = NSRulerView.alloc().initWithScrollView_orientation_(
198 r = NSRulerView.alloc().initWithScrollView_orientation_(
199 self.textView.enclosingScrollView(),
199 self.textView.enclosingScrollView(),
200 NSVerticalRuler)
200 NSVerticalRuler)
201 self.verticalRulerView = r
201 self.verticalRulerView = r
202 self.verticalRulerView.setClientView_(self.textView)
202 self.verticalRulerView.setClientView_(self.textView)
203 self._start_cli_banner()
203 self._start_cli_banner()
204 self.start_new_block()
204 self.start_new_block()
205
205
206
206
207 def appWillTerminate_(self, notification):
207 def appWillTerminate_(self, notification):
208 """appWillTerminate"""
208 """appWillTerminate"""
209
209
210 self.engine.stopService()
210 self.engine.stopService()
211
211
212
212
213 def complete(self, token):
213 def complete(self, token):
214 """Complete token in engine's user_ns
214 """Complete token in engine's user_ns
215
215
216 Parameters
216 Parameters
217 ----------
217 ----------
218 token : string
218 token : string
219
219
220 Result
220 Result
221 ------
221 ------
222 Deferred result of
222 Deferred result of
223 IPython.kernel.engineservice.IEngineBase.complete
223 IPython.kernel.engineservice.IEngineBase.complete
224 """
224 """
225
225
226 return self.engine.complete(token)
226 return self.engine.complete(token)
227
227
228
228
229 def execute(self, block, blockID=None):
229 def execute(self, block, blockID=None):
230 self.waitingForEngine = True
230 self.waitingForEngine = True
231 self.willChangeValueForKey_('commandHistory')
231 self.willChangeValueForKey_('commandHistory')
232 d = super(IPythonCocoaController, self).execute(block,
232 d = super(IPythonCocoaController, self).execute(block,
233 blockID)
233 blockID)
234 d.addBoth(self._engine_done)
234 d.addBoth(self._engine_done)
235 d.addCallback(self._update_user_ns)
235 d.addCallback(self._update_user_ns)
236
236
237 return d
237 return d
238
238
239
239
240 def push_(self, namespace):
240 def push_(self, namespace):
241 """Push dictionary of key=>values to python namespace"""
241 """Push dictionary of key=>values to python namespace"""
242
242
243 self.waitingForEngine = True
243 self.waitingForEngine = True
244 self.willChangeValueForKey_('commandHistory')
244 self.willChangeValueForKey_('commandHistory')
245 d = self.engine.push(namespace)
245 d = self.engine.push(namespace)
246 d.addBoth(self._engine_done)
246 d.addBoth(self._engine_done)
247 d.addCallback(self._update_user_ns)
247 d.addCallback(self._update_user_ns)
248
248
249
249
250 def pull_(self, keys):
250 def pull_(self, keys):
251 """Pull keys from python namespace"""
251 """Pull keys from python namespace"""
252
252
253 self.waitingForEngine = True
253 self.waitingForEngine = True
254 result = blockingCallFromThread(self.engine.pull, keys)
254 result = blockingCallFromThread(self.engine.pull, keys)
255 self.waitingForEngine = False
255 self.waitingForEngine = False
256
256
257 @objc.signature('v@:@I')
257 @objc.signature('v@:@I')
258 def executeFileAtPath_encoding_(self, path, encoding):
258 def executeFileAtPath_encoding_(self, path, encoding):
259 """Execute file at path in an empty namespace. Update the engine
259 """Execute file at path in an empty namespace. Update the engine
260 user_ns with the resulting locals."""
260 user_ns with the resulting locals."""
261
261
262 lines,err = NSString.stringWithContentsOfFile_encoding_error_(
262 lines,err = NSString.stringWithContentsOfFile_encoding_error_(
263 path,
263 path,
264 encoding,
264 encoding,
265 None)
265 None)
266 self.engine.execute(lines)
266 self.engine.execute(lines)
267
267
268
268
269 def _engine_done(self, x):
269 def _engine_done(self, x):
270 self.waitingForEngine = False
270 self.waitingForEngine = False
271 self.didChangeValueForKey_('commandHistory')
271 self.didChangeValueForKey_('commandHistory')
272 return x
272 return x
273
273
274 def _update_user_ns(self, result):
274 def _update_user_ns(self, result):
275 """Update self.userNS from self.engine's namespace"""
275 """Update self.userNS from self.engine's namespace"""
276 d = self.engine.keys()
276 d = self.engine.keys()
277 d.addCallback(self._get_engine_namespace_values_for_keys)
277 d.addCallback(self._get_engine_namespace_values_for_keys)
278
278
279 return result
279 return result
280
280
281
281
282 def _get_engine_namespace_values_for_keys(self, keys):
282 def _get_engine_namespace_values_for_keys(self, keys):
283 d = self.engine.pull(keys)
283 d = self.engine.pull(keys)
284 d.addCallback(self._store_engine_namespace_values, keys=keys)
284 d.addCallback(self._store_engine_namespace_values, keys=keys)
285
285
286
286
287 def _store_engine_namespace_values(self, values, keys=[]):
287 def _store_engine_namespace_values(self, values, keys=[]):
288 assert(len(values) == len(keys))
288 assert(len(values) == len(keys))
289 self.willChangeValueForKey_('userNS')
289 self.willChangeValueForKey_('userNS')
290 for (k,v) in zip(keys,values):
290 for (k,v) in zip(keys,values):
291 self.userNS[k] = saferepr(v)
291 self.userNS[k] = saferepr(v)
292 self.didChangeValueForKey_('userNS')
292 self.didChangeValueForKey_('userNS')
293
293
294
294
295 def update_cell_prompt(self, result, blockID=None):
295 def update_cell_prompt(self, result, blockID=None):
296 print self.blockRanges
296 print self.blockRanges
297 if(isinstance(result, Failure)):
297 if(isinstance(result, Failure)):
298 prompt = self.input_prompt()
298 prompt = self.input_prompt()
299
299
300 else:
300 else:
301 prompt = self.input_prompt(number=result['number'])
301 prompt = self.input_prompt(number=result['number'])
302
302
303 r = self.blockRanges[blockID].inputPromptRange
303 r = self.blockRanges[blockID].inputPromptRange
304 self.insert_text(prompt,
304 self.insert_text(prompt,
305 textRange=r,
305 textRange=r,
306 scrollToVisible=False
306 scrollToVisible=False
307 )
307 )
308
308
309 return result
309 return result
310
310
311
311
312 def render_result(self, result):
312 def render_result(self, result):
313 blockID = result['blockID']
313 blockID = result['blockID']
314 inputRange = self.blockRanges[blockID].inputRange
314 inputRange = self.blockRanges[blockID].inputRange
315 del self.blockRanges[blockID]
315 del self.blockRanges[blockID]
316
316
317 #print inputRange,self.current_block_range()
317 #print inputRange,self.current_block_range()
318 self.insert_text('\n' +
318 self.insert_text('\n' +
319 self.output_prompt(number=result['number']) +
319 self.output_prompt(number=result['number']) +
320 result.get('display',{}).get('pprint','') +
320 result.get('display',{}).get('pprint','') +
321 '\n\n',
321 '\n\n',
322 textRange=NSMakeRange(inputRange.location+inputRange.length,
322 textRange=NSMakeRange(inputRange.location+inputRange.length,
323 0))
323 0))
324 return result
324 return result
325
325
326
326
327 def render_error(self, failure):
327 def render_error(self, failure):
328 print failure
328 print failure
329 blockID = failure.blockID
329 blockID = failure.blockID
330 inputRange = self.blockRanges[blockID].inputRange
330 inputRange = self.blockRanges[blockID].inputRange
331 self.insert_text('\n' +
331 self.insert_text('\n' +
332 self.output_prompt() +
332 self.output_prompt() +
333 '\n' +
333 '\n' +
334 failure.getErrorMessage() +
334 failure.getErrorMessage() +
335 '\n\n',
335 '\n\n',
336 textRange=NSMakeRange(inputRange.location +
336 textRange=NSMakeRange(inputRange.location +
337 inputRange.length,
337 inputRange.length,
338 0))
338 0))
339 self.start_new_block()
339 self.start_new_block()
340 return failure
340 return failure
341
341
342
342
343 def _start_cli_banner(self):
343 def _start_cli_banner(self):
344 """Print banner"""
344 """Print banner"""
345
345
346 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
346 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
347 IPython.__version__
347 IPython.__version__
348
348
349 self.insert_text(banner + '\n\n')
349 self.insert_text(banner + '\n\n')
350
350
351
351
352 def start_new_block(self):
352 def start_new_block(self):
353 """"""
353 """"""
354
354
355 self.currentBlockID = self.next_block_ID()
355 self.currentBlockID = self.next_block_ID()
356 self.blockRanges[self.currentBlockID] = self.new_cell_block()
356 self.blockRanges[self.currentBlockID] = self.new_cell_block()
357 self.insert_text(self.input_prompt(),
357 self.insert_text(self.input_prompt(),
358 textRange=self.current_block_range().inputPromptRange)
358 textRange=self.current_block_range().inputPromptRange)
359
359
360
360
361
361
362 def next_block_ID(self):
362 def next_block_ID(self):
363
363
364 return guid.generate()
364 return uuid.uuid4()
365
365
366 def new_cell_block(self):
366 def new_cell_block(self):
367 """A new CellBlock at the end of self.textView.textStorage()"""
367 """A new CellBlock at the end of self.textView.textStorage()"""
368
368
369 return CellBlock(NSMakeRange(self.textView.textStorage().length(),
369 return CellBlock(NSMakeRange(self.textView.textStorage().length(),
370 0), #len(self.input_prompt())),
370 0), #len(self.input_prompt())),
371 NSMakeRange(self.textView.textStorage().length(),# + len(self.input_prompt()),
371 NSMakeRange(self.textView.textStorage().length(),# + len(self.input_prompt()),
372 0))
372 0))
373
373
374
374
375 def current_block_range(self):
375 def current_block_range(self):
376 return self.blockRanges.get(self.currentBlockID,
376 return self.blockRanges.get(self.currentBlockID,
377 self.new_cell_block())
377 self.new_cell_block())
378
378
379 def current_block(self):
379 def current_block(self):
380 """The current block's text"""
380 """The current block's text"""
381
381
382 return self.text_for_range(self.current_block_range().inputRange)
382 return self.text_for_range(self.current_block_range().inputRange)
383
383
384 def text_for_range(self, textRange):
384 def text_for_range(self, textRange):
385 """text_for_range"""
385 """text_for_range"""
386
386
387 ts = self.textView.textStorage()
387 ts = self.textView.textStorage()
388 return ts.string().substringWithRange_(textRange)
388 return ts.string().substringWithRange_(textRange)
389
389
390 def current_line(self):
390 def current_line(self):
391 block = self.text_for_range(self.current_block_range().inputRange)
391 block = self.text_for_range(self.current_block_range().inputRange)
392 block = block.split('\n')
392 block = block.split('\n')
393 return block[-1]
393 return block[-1]
394
394
395
395
396 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
396 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
397 """Insert text into textView at textRange, updating blockRanges
397 """Insert text into textView at textRange, updating blockRanges
398 as necessary
398 as necessary
399 """
399 """
400 if(textRange == None):
400 if(textRange == None):
401 #range for end of text
401 #range for end of text
402 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
402 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
403
403
404
404
405 self.textView.replaceCharactersInRange_withString_(
405 self.textView.replaceCharactersInRange_withString_(
406 textRange, string)
406 textRange, string)
407
407
408 for r in self.blockRanges.itervalues():
408 for r in self.blockRanges.itervalues():
409 r.update_ranges_for_insertion(string, textRange)
409 r.update_ranges_for_insertion(string, textRange)
410
410
411 self.textView.setSelectedRange_(textRange)
411 self.textView.setSelectedRange_(textRange)
412 if(scrollToVisible):
412 if(scrollToVisible):
413 self.textView.scrollRangeToVisible_(textRange)
413 self.textView.scrollRangeToVisible_(textRange)
414
414
415
415
416
416
417 def replace_current_block_with_string(self, textView, string):
417 def replace_current_block_with_string(self, textView, string):
418 textView.replaceCharactersInRange_withString_(
418 textView.replaceCharactersInRange_withString_(
419 self.current_block_range().inputRange,
419 self.current_block_range().inputRange,
420 string)
420 string)
421 self.current_block_range().inputRange.length = len(string)
421 self.current_block_range().inputRange.length = len(string)
422 r = NSMakeRange(textView.textStorage().length(), 0)
422 r = NSMakeRange(textView.textStorage().length(), 0)
423 textView.scrollRangeToVisible_(r)
423 textView.scrollRangeToVisible_(r)
424 textView.setSelectedRange_(r)
424 textView.setSelectedRange_(r)
425
425
426
426
427 def current_indent_string(self):
427 def current_indent_string(self):
428 """returns string for indent or None if no indent"""
428 """returns string for indent or None if no indent"""
429
429
430 return self._indent_for_block(self.current_block())
430 return self._indent_for_block(self.current_block())
431
431
432
432
433 def _indent_for_block(self, block):
433 def _indent_for_block(self, block):
434 lines = block.split('\n')
434 lines = block.split('\n')
435 if(len(lines) > 1):
435 if(len(lines) > 1):
436 currentIndent = len(lines[-1]) - len(lines[-1].lstrip())
436 currentIndent = len(lines[-1]) - len(lines[-1].lstrip())
437 if(currentIndent == 0):
437 if(currentIndent == 0):
438 currentIndent = self.tabSpaces
438 currentIndent = self.tabSpaces
439
439
440 if(self.tabUsesSpaces):
440 if(self.tabUsesSpaces):
441 result = ' ' * currentIndent
441 result = ' ' * currentIndent
442 else:
442 else:
443 result = '\t' * (currentIndent/self.tabSpaces)
443 result = '\t' * (currentIndent/self.tabSpaces)
444 else:
444 else:
445 result = None
445 result = None
446
446
447 return result
447 return result
448
448
449
449
450 # NSTextView delegate methods...
450 # NSTextView delegate methods...
451 def textView_doCommandBySelector_(self, textView, selector):
451 def textView_doCommandBySelector_(self, textView, selector):
452 assert(textView == self.textView)
452 assert(textView == self.textView)
453 NSLog("textView_doCommandBySelector_: "+selector)
453 NSLog("textView_doCommandBySelector_: "+selector)
454
454
455
455
456 if(selector == 'insertNewline:'):
456 if(selector == 'insertNewline:'):
457 indent = self.current_indent_string()
457 indent = self.current_indent_string()
458 if(indent):
458 if(indent):
459 line = indent + self.current_line()
459 line = indent + self.current_line()
460 else:
460 else:
461 line = self.current_line()
461 line = self.current_line()
462
462
463 if(self.is_complete(self.current_block())):
463 if(self.is_complete(self.current_block())):
464 self.execute(self.current_block(),
464 self.execute(self.current_block(),
465 blockID=self.currentBlockID)
465 blockID=self.currentBlockID)
466 self.start_new_block()
466 self.start_new_block()
467
467
468 return True
468 return True
469
469
470 return False
470 return False
471
471
472 elif(selector == 'moveUp:'):
472 elif(selector == 'moveUp:'):
473 prevBlock = self.get_history_previous(self.current_block())
473 prevBlock = self.get_history_previous(self.current_block())
474 if(prevBlock != None):
474 if(prevBlock != None):
475 self.replace_current_block_with_string(textView, prevBlock)
475 self.replace_current_block_with_string(textView, prevBlock)
476 else:
476 else:
477 NSBeep()
477 NSBeep()
478 return True
478 return True
479
479
480 elif(selector == 'moveDown:'):
480 elif(selector == 'moveDown:'):
481 nextBlock = self.get_history_next()
481 nextBlock = self.get_history_next()
482 if(nextBlock != None):
482 if(nextBlock != None):
483 self.replace_current_block_with_string(textView, nextBlock)
483 self.replace_current_block_with_string(textView, nextBlock)
484 else:
484 else:
485 NSBeep()
485 NSBeep()
486 return True
486 return True
487
487
488 elif(selector == 'moveToBeginningOfParagraph:'):
488 elif(selector == 'moveToBeginningOfParagraph:'):
489 textView.setSelectedRange_(NSMakeRange(
489 textView.setSelectedRange_(NSMakeRange(
490 self.current_block_range().inputRange.location,
490 self.current_block_range().inputRange.location,
491 0))
491 0))
492 return True
492 return True
493 elif(selector == 'moveToEndOfParagraph:'):
493 elif(selector == 'moveToEndOfParagraph:'):
494 textView.setSelectedRange_(NSMakeRange(
494 textView.setSelectedRange_(NSMakeRange(
495 self.current_block_range().inputRange.location + \
495 self.current_block_range().inputRange.location + \
496 self.current_block_range().inputRange.length, 0))
496 self.current_block_range().inputRange.length, 0))
497 return True
497 return True
498 elif(selector == 'deleteToEndOfParagraph:'):
498 elif(selector == 'deleteToEndOfParagraph:'):
499 if(textView.selectedRange().location <= \
499 if(textView.selectedRange().location <= \
500 self.current_block_range().location):
500 self.current_block_range().location):
501 raise NotImplemented()
501 raise NotImplemented()
502
502
503 return False # don't actually handle the delete
503 return False # don't actually handle the delete
504
504
505 elif(selector == 'insertTab:'):
505 elif(selector == 'insertTab:'):
506 if(len(self.current_line().strip()) == 0): #only white space
506 if(len(self.current_line().strip()) == 0): #only white space
507 return False
507 return False
508 else:
508 else:
509 self.textView.complete_(self)
509 self.textView.complete_(self)
510 return True
510 return True
511
511
512 elif(selector == 'deleteBackward:'):
512 elif(selector == 'deleteBackward:'):
513 #if we're at the beginning of the current block, ignore
513 #if we're at the beginning of the current block, ignore
514 if(textView.selectedRange().location == \
514 if(textView.selectedRange().location == \
515 self.current_block_range().inputRange.location):
515 self.current_block_range().inputRange.location):
516 return True
516 return True
517 else:
517 else:
518 for r in self.blockRanges.itervalues():
518 for r in self.blockRanges.itervalues():
519 deleteRange = textView.selectedRange
519 deleteRange = textView.selectedRange
520 if(deleteRange.length == 0):
520 if(deleteRange.length == 0):
521 deleteRange.location -= 1
521 deleteRange.location -= 1
522 deleteRange.length = 1
522 deleteRange.length = 1
523 r.update_ranges_for_deletion(deleteRange)
523 r.update_ranges_for_deletion(deleteRange)
524 return False
524 return False
525 return False
525 return False
526
526
527
527
528 def textView_shouldChangeTextInRanges_replacementStrings_(self,
528 def textView_shouldChangeTextInRanges_replacementStrings_(self,
529 textView, ranges, replacementStrings):
529 textView, ranges, replacementStrings):
530 """
530 """
531 Delegate method for NSTextView.
531 Delegate method for NSTextView.
532
532
533 Refuse change text in ranges not at end, but make those changes at
533 Refuse change text in ranges not at end, but make those changes at
534 end.
534 end.
535 """
535 """
536
536
537 assert(len(ranges) == len(replacementStrings))
537 assert(len(ranges) == len(replacementStrings))
538 allow = True
538 allow = True
539 for r,s in zip(ranges, replacementStrings):
539 for r,s in zip(ranges, replacementStrings):
540 r = r.rangeValue()
540 r = r.rangeValue()
541 if(textView.textStorage().length() > 0 and
541 if(textView.textStorage().length() > 0 and
542 r.location < self.current_block_range().inputRange.location):
542 r.location < self.current_block_range().inputRange.location):
543 self.insert_text(s)
543 self.insert_text(s)
544 allow = False
544 allow = False
545
545
546 return allow
546 return allow
547
547
548 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
548 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
549 textView, words, charRange, index):
549 textView, words, charRange, index):
550 try:
550 try:
551 ts = textView.textStorage()
551 ts = textView.textStorage()
552 token = ts.string().substringWithRange_(charRange)
552 token = ts.string().substringWithRange_(charRange)
553 completions = blockingCallFromThread(self.complete, token)
553 completions = blockingCallFromThread(self.complete, token)
554 except:
554 except:
555 completions = objc.nil
555 completions = objc.nil
556 NSBeep()
556 NSBeep()
557
557
558 return (completions,0)
558 return (completions,0)
559
559
560
560
@@ -1,343 +1,343 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
3 """
3 """
4 frontendbase provides an interface and base class for GUI frontends for
4 frontendbase provides an interface and base class for GUI frontends for
5 IPython.kernel/IPython.kernel.core.
5 IPython.kernel/IPython.kernel.core.
6
6
7 Frontend implementations will likely want to subclass FrontEndBase.
7 Frontend implementations will likely want to subclass FrontEndBase.
8
8
9 Author: Barry Wark
9 Author: Barry Wark
10 """
10 """
11 __docformat__ = "restructuredtext en"
11 __docformat__ = "restructuredtext en"
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008-2011 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 import string
23 import string
24 import codeop
24 import codeop
25 from IPython.external import guid
25 import uuid
26
26
27
27
28 from IPython.frontend.zopeinterface import (
28 from IPython.frontend.zopeinterface import (
29 Interface,
29 Interface,
30 Attribute,
30 Attribute,
31 )
31 )
32 from IPython.kernel.core.history import FrontEndHistory
32 from IPython.kernel.core.history import FrontEndHistory
33 from IPython.kernel.core.util import Bunch
33 from IPython.kernel.core.util import Bunch
34
34
35 ##############################################################################
35 ##############################################################################
36 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
36 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
37 # not
37 # not
38
38
39 rc = Bunch()
39 rc = Bunch()
40 rc.prompt_in1 = r'In [$number]: '
40 rc.prompt_in1 = r'In [$number]: '
41 rc.prompt_in2 = r'...'
41 rc.prompt_in2 = r'...'
42 rc.prompt_out = r'Out [$number]: '
42 rc.prompt_out = r'Out [$number]: '
43
43
44 ##############################################################################
44 ##############################################################################
45 # Interface definitions
45 # Interface definitions
46 ##############################################################################
46 ##############################################################################
47
47
48 class IFrontEndFactory(Interface):
48 class IFrontEndFactory(Interface):
49 """Factory interface for frontends."""
49 """Factory interface for frontends."""
50
50
51 def __call__(engine=None, history=None):
51 def __call__(engine=None, history=None):
52 """
52 """
53 Parameters:
53 Parameters:
54 interpreter : IPython.kernel.engineservice.IEngineCore
54 interpreter : IPython.kernel.engineservice.IEngineCore
55 """
55 """
56
56
57 pass
57 pass
58
58
59
59
60 class IFrontEnd(Interface):
60 class IFrontEnd(Interface):
61 """Interface for frontends. All methods return t.i.d.Deferred"""
61 """Interface for frontends. All methods return t.i.d.Deferred"""
62
62
63 Attribute("input_prompt_template", "string.Template instance\
63 Attribute("input_prompt_template", "string.Template instance\
64 substituteable with execute result.")
64 substituteable with execute result.")
65 Attribute("output_prompt_template", "string.Template instance\
65 Attribute("output_prompt_template", "string.Template instance\
66 substituteable with execute result.")
66 substituteable with execute result.")
67 Attribute("continuation_prompt_template", "string.Template instance\
67 Attribute("continuation_prompt_template", "string.Template instance\
68 substituteable with execute result.")
68 substituteable with execute result.")
69
69
70 def update_cell_prompt(result, blockID=None):
70 def update_cell_prompt(result, blockID=None):
71 """Subclass may override to update the input prompt for a block.
71 """Subclass may override to update the input prompt for a block.
72
72
73 In asynchronous frontends, this method will be called as a
73 In asynchronous frontends, this method will be called as a
74 twisted.internet.defer.Deferred's callback/errback.
74 twisted.internet.defer.Deferred's callback/errback.
75 Implementations should thus return result when finished.
75 Implementations should thus return result when finished.
76
76
77 Result is a result dict in case of success, and a
77 Result is a result dict in case of success, and a
78 twisted.python.util.failure.Failure in case of an error
78 twisted.python.util.failure.Failure in case of an error
79 """
79 """
80
80
81 pass
81 pass
82
82
83 def render_result(result):
83 def render_result(result):
84 """Render the result of an execute call. Implementors may choose the
84 """Render the result of an execute call. Implementors may choose the
85 method of rendering.
85 method of rendering.
86 For example, a notebook-style frontend might render a Chaco plot
86 For example, a notebook-style frontend might render a Chaco plot
87 inline.
87 inline.
88
88
89 Parameters:
89 Parameters:
90 result : dict (result of IEngineBase.execute )
90 result : dict (result of IEngineBase.execute )
91 blockID = result['blockID']
91 blockID = result['blockID']
92
92
93 Result:
93 Result:
94 Output of frontend rendering
94 Output of frontend rendering
95 """
95 """
96
96
97 pass
97 pass
98
98
99 def render_error(failure):
99 def render_error(failure):
100 """Subclasses must override to render the failure.
100 """Subclasses must override to render the failure.
101
101
102 In asynchronous frontend, since this method will be called as a
102 In asynchronous frontend, since this method will be called as a
103 twisted.internet.defer.Deferred's callback. Implementations
103 twisted.internet.defer.Deferred's callback. Implementations
104 should thus return result when finished.
104 should thus return result when finished.
105
105
106 blockID = failure.blockID
106 blockID = failure.blockID
107 """
107 """
108
108
109 pass
109 pass
110
110
111 def input_prompt(number=''):
111 def input_prompt(number=''):
112 """Returns the input prompt by subsituting into
112 """Returns the input prompt by subsituting into
113 self.input_prompt_template
113 self.input_prompt_template
114 """
114 """
115 pass
115 pass
116
116
117 def output_prompt(number=''):
117 def output_prompt(number=''):
118 """Returns the output prompt by subsituting into
118 """Returns the output prompt by subsituting into
119 self.output_prompt_template
119 self.output_prompt_template
120 """
120 """
121
121
122 pass
122 pass
123
123
124 def continuation_prompt():
124 def continuation_prompt():
125 """Returns the continuation prompt by subsituting into
125 """Returns the continuation prompt by subsituting into
126 self.continuation_prompt_template
126 self.continuation_prompt_template
127 """
127 """
128
128
129 pass
129 pass
130
130
131 def is_complete(block):
131 def is_complete(block):
132 """Returns True if block is complete, False otherwise."""
132 """Returns True if block is complete, False otherwise."""
133
133
134 pass
134 pass
135
135
136
136
137 def get_history_previous(current_block):
137 def get_history_previous(current_block):
138 """Returns the block previous in the history. Saves currentBlock if
138 """Returns the block previous in the history. Saves currentBlock if
139 the history_cursor is currently at the end of the input history"""
139 the history_cursor is currently at the end of the input history"""
140 pass
140 pass
141
141
142 def get_history_next():
142 def get_history_next():
143 """Returns the next block in the history."""
143 """Returns the next block in the history."""
144
144
145 pass
145 pass
146
146
147 def complete(self, line):
147 def complete(self, line):
148 """Returns the list of possible completions, and the completed
148 """Returns the list of possible completions, and the completed
149 line.
149 line.
150
150
151 The input argument is the full line to be completed. This method
151 The input argument is the full line to be completed. This method
152 returns both the line completed as much as possible, and the list
152 returns both the line completed as much as possible, and the list
153 of further possible completions (full words).
153 of further possible completions (full words).
154 """
154 """
155 pass
155 pass
156
156
157
157
158 ##############################################################################
158 ##############################################################################
159 # Base class for all the frontends.
159 # Base class for all the frontends.
160 ##############################################################################
160 ##############################################################################
161
161
162 class FrontEndBase(object):
162 class FrontEndBase(object):
163 """
163 """
164 FrontEndBase manages the state tasks for a CLI frontend:
164 FrontEndBase manages the state tasks for a CLI frontend:
165 - Input and output history management
165 - Input and output history management
166 - Input/continuation and output prompt generation
166 - Input/continuation and output prompt generation
167
167
168 Some issues (due to possibly unavailable engine):
168 Some issues (due to possibly unavailable engine):
169 - How do we get the current cell number for the engine?
169 - How do we get the current cell number for the engine?
170 - How do we handle completions?
170 - How do we handle completions?
171 """
171 """
172
172
173 history_cursor = 0
173 history_cursor = 0
174
174
175 input_prompt_template = string.Template(rc.prompt_in1)
175 input_prompt_template = string.Template(rc.prompt_in1)
176 output_prompt_template = string.Template(rc.prompt_out)
176 output_prompt_template = string.Template(rc.prompt_out)
177 continuation_prompt_template = string.Template(rc.prompt_in2)
177 continuation_prompt_template = string.Template(rc.prompt_in2)
178
178
179 def __init__(self, shell=None, history=None):
179 def __init__(self, shell=None, history=None):
180 self.shell = shell
180 self.shell = shell
181 if history is None:
181 if history is None:
182 self.history = FrontEndHistory(input_cache=[''])
182 self.history = FrontEndHistory(input_cache=[''])
183 else:
183 else:
184 self.history = history
184 self.history = history
185
185
186
186
187 def input_prompt(self, number=''):
187 def input_prompt(self, number=''):
188 """Returns the current input prompt
188 """Returns the current input prompt
189
189
190 It would be great to use ipython1.core.prompts.Prompt1 here
190 It would be great to use ipython1.core.prompts.Prompt1 here
191 """
191 """
192 return self.input_prompt_template.safe_substitute({'number':number})
192 return self.input_prompt_template.safe_substitute({'number':number})
193
193
194
194
195 def continuation_prompt(self):
195 def continuation_prompt(self):
196 """Returns the current continuation prompt"""
196 """Returns the current continuation prompt"""
197
197
198 return self.continuation_prompt_template.safe_substitute()
198 return self.continuation_prompt_template.safe_substitute()
199
199
200 def output_prompt(self, number=''):
200 def output_prompt(self, number=''):
201 """Returns the output prompt for result"""
201 """Returns the output prompt for result"""
202
202
203 return self.output_prompt_template.safe_substitute({'number':number})
203 return self.output_prompt_template.safe_substitute({'number':number})
204
204
205
205
206 def is_complete(self, block):
206 def is_complete(self, block):
207 """Determine if block is complete.
207 """Determine if block is complete.
208
208
209 Parameters
209 Parameters
210 block : string
210 block : string
211
211
212 Result
212 Result
213 True if block can be sent to the engine without compile errors.
213 True if block can be sent to the engine without compile errors.
214 False otherwise.
214 False otherwise.
215 """
215 """
216
216
217 try:
217 try:
218 is_complete = codeop.compile_command(block.rstrip() + '\n\n',
218 is_complete = codeop.compile_command(block.rstrip() + '\n\n',
219 "<string>", "exec")
219 "<string>", "exec")
220 except:
220 except:
221 return False
221 return False
222
222
223 lines = block.split('\n')
223 lines = block.split('\n')
224 return ((is_complete is not None)
224 return ((is_complete is not None)
225 and (len(lines)==1 or str(lines[-1])==''))
225 and (len(lines)==1 or str(lines[-1])==''))
226
226
227
227
228 def execute(self, block, blockID=None):
228 def execute(self, block, blockID=None):
229 """Execute the block and return the result.
229 """Execute the block and return the result.
230
230
231 Parameters:
231 Parameters:
232 block : {str, AST}
232 block : {str, AST}
233 blockID : any
233 blockID : any
234 Caller may provide an ID to identify this block.
234 Caller may provide an ID to identify this block.
235 result['blockID'] := blockID
235 result['blockID'] := blockID
236
236
237 Result:
237 Result:
238 Deferred result of self.interpreter.execute
238 Deferred result of self.interpreter.execute
239 """
239 """
240
240
241 if(not self.is_complete(block)):
241 if(not self.is_complete(block)):
242 raise Exception("Block is not compilable")
242 raise Exception("Block is not compilable")
243
243
244 if(blockID == None):
244 if(blockID == None):
245 blockID = guid.generate()
245 blockID = uuid.uuid4()
246
246
247 try:
247 try:
248 result = self.shell.execute(block)
248 result = self.shell.execute(block)
249 except Exception,e:
249 except Exception,e:
250 e = self._add_block_id_for_failure(e, blockID=blockID)
250 e = self._add_block_id_for_failure(e, blockID=blockID)
251 e = self.update_cell_prompt(e, blockID=blockID)
251 e = self.update_cell_prompt(e, blockID=blockID)
252 e = self.render_error(e)
252 e = self.render_error(e)
253 else:
253 else:
254 result = self._add_block_id_for_result(result, blockID=blockID)
254 result = self._add_block_id_for_result(result, blockID=blockID)
255 result = self.update_cell_prompt(result, blockID=blockID)
255 result = self.update_cell_prompt(result, blockID=blockID)
256 result = self.render_result(result)
256 result = self.render_result(result)
257
257
258 return result
258 return result
259
259
260
260
261 def _add_block_id_for_result(self, result, blockID):
261 def _add_block_id_for_result(self, result, blockID):
262 """Add the blockID to result or failure. Unfortunatley, we have to
262 """Add the blockID to result or failure. Unfortunatley, we have to
263 treat failures differently than result dicts.
263 treat failures differently than result dicts.
264 """
264 """
265
265
266 result['blockID'] = blockID
266 result['blockID'] = blockID
267
267
268 return result
268 return result
269
269
270 def _add_block_id_for_failure(self, failure, blockID):
270 def _add_block_id_for_failure(self, failure, blockID):
271 """_add_block_id_for_failure"""
271 """_add_block_id_for_failure"""
272 failure.blockID = blockID
272 failure.blockID = blockID
273 return failure
273 return failure
274
274
275
275
276 def _add_history(self, result, block=None):
276 def _add_history(self, result, block=None):
277 """Add block to the history"""
277 """Add block to the history"""
278
278
279 assert(block != None)
279 assert(block != None)
280 self.history.add_items([block])
280 self.history.add_items([block])
281 self.history_cursor += 1
281 self.history_cursor += 1
282
282
283 return result
283 return result
284
284
285
285
286 def get_history_previous(self, current_block):
286 def get_history_previous(self, current_block):
287 """ Returns previous history string and decrement history cursor.
287 """ Returns previous history string and decrement history cursor.
288 """
288 """
289 command = self.history.get_history_item(self.history_cursor - 1)
289 command = self.history.get_history_item(self.history_cursor - 1)
290
290
291 if command is not None:
291 if command is not None:
292 if(self.history_cursor+1 == len(self.history.input_cache)):
292 if(self.history_cursor+1 == len(self.history.input_cache)):
293 self.history.input_cache[self.history_cursor] = current_block
293 self.history.input_cache[self.history_cursor] = current_block
294 self.history_cursor -= 1
294 self.history_cursor -= 1
295 return command
295 return command
296
296
297
297
298 def get_history_next(self):
298 def get_history_next(self):
299 """ Returns next history string and increment history cursor.
299 """ Returns next history string and increment history cursor.
300 """
300 """
301 command = self.history.get_history_item(self.history_cursor+1)
301 command = self.history.get_history_item(self.history_cursor+1)
302
302
303 if command is not None:
303 if command is not None:
304 self.history_cursor += 1
304 self.history_cursor += 1
305 return command
305 return command
306
306
307 ###
307 ###
308 # Subclasses probably want to override these methods...
308 # Subclasses probably want to override these methods...
309 ###
309 ###
310
310
311 def update_cell_prompt(self, result, blockID=None):
311 def update_cell_prompt(self, result, blockID=None):
312 """Subclass may override to update the input prompt for a block.
312 """Subclass may override to update the input prompt for a block.
313
313
314 This method only really makes sens in asyncrhonous frontend.
314 This method only really makes sens in asyncrhonous frontend.
315 Since this method will be called as a
315 Since this method will be called as a
316 twisted.internet.defer.Deferred's callback, implementations should
316 twisted.internet.defer.Deferred's callback, implementations should
317 return result when finished.
317 return result when finished.
318 """
318 """
319
319
320 raise NotImplementedError
320 raise NotImplementedError
321
321
322
322
323 def render_result(self, result):
323 def render_result(self, result):
324 """Subclasses must override to render result.
324 """Subclasses must override to render result.
325
325
326 In asynchronous frontends, this method will be called as a
326 In asynchronous frontends, this method will be called as a
327 twisted.internet.defer.Deferred's callback. Implementations
327 twisted.internet.defer.Deferred's callback. Implementations
328 should thus return result when finished.
328 should thus return result when finished.
329 """
329 """
330
330
331 raise NotImplementedError
331 raise NotImplementedError
332
332
333
333
334 def render_error(self, failure):
334 def render_error(self, failure):
335 """Subclasses must override to render the failure.
335 """Subclasses must override to render the failure.
336
336
337 In asynchronous frontends, this method will be called as a
337 In asynchronous frontends, this method will be called as a
338 twisted.internet.defer.Deferred's callback. Implementations
338 twisted.internet.defer.Deferred's callback. Implementations
339 should thus return result when finished.
339 should thus return result when finished.
340 """
340 """
341
341
342 raise NotImplementedError
342 raise NotImplementedError
343
343
General Comments 0
You need to be logged in to leave comments. Login now