##// END OF EJS Templates
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
Barry Wark -
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,369 b''
1 # encoding: utf-8
2 # -*- test-case-name: ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
3
4 """PyObjC classes to provide a Cocoa frontend to the ipython1.kernel.engineservice.EngineService.
5
6 The Cocoa frontend is divided into two classes:
7 - IPythonCocoaController
8 - IPythonCLITextViewDelegate
9
10 To add an IPython interpreter to a cocoa app, instantiate both of these classes in an XIB...[FINISH]
11 """
12
13 __docformat__ = "restructuredtext en"
14
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 Barry Wark <barrywark@gmail.com>
17 #
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
21
22 #-------------------------------------------------------------------------------
23 # Imports
24 #-------------------------------------------------------------------------------
25
26 import objc
27 import uuid
28
29 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
30 NSLog, NSNotificationCenter, NSMakeRange,\
31 NSLocalizedString, NSIntersectionRange
32 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
33 NSTextView, NSRulerView, NSVerticalRuler
34
35 from pprint import saferepr
36
37 from IPython.kernel.engineservice import EngineService, ThreadedEngineService
38 from IPython.frontend.frontendbase import FrontEndBase
39
40 from twisted.internet.threads import blockingCallFromThread
41
42 #-------------------------------------------------------------------------------
43 # Classes to implement the Cocoa frontend
44 #-------------------------------------------------------------------------------
45
46 # TODO:
47 # 1. use MultiEngineClient and out-of-process engine rather than ThreadedEngineService?
48 # 2. integrate Xgrid launching of engines
49
50
51
52
53 class IPythonCocoaController(NSObject, FrontEndBase):
54 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
55 waitingForEngine = objc.ivar().bool()
56 textView = objc.IBOutlet()
57
58 def init(self):
59 self = super(IPythonCocoaController, self).init()
60 FrontEndBase.__init__(self, engine=ThreadedEngineService())
61 if(self != None):
62 self._common_init()
63
64 return self
65
66 def _common_init(self):
67 """_common_init"""
68
69 self.userNS = NSMutableDictionary.dictionary()
70 self.waitingForEngine = False
71
72 self.lines = {}
73 self.tabSpaces = 4
74 self.tabUsesSpaces = True
75 self.currentBlockID = self.nextBlockID()
76 self.blockRanges = {} # blockID=>NSRange
77
78
79 def awakeFromNib(self):
80 """awakeFromNib"""
81
82 self._common_init()
83
84 # Start the IPython engine
85 self.engine.startService()
86 NSLog('IPython engine started')
87
88 # Register for app termination
89 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self,
90 'appWillTerminate:',
91 NSApplicationWillTerminateNotification,
92 None)
93
94 self.textView.setDelegate_(self)
95 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
96 self.verticalRulerView = NSRulerView.alloc().initWithScrollView_orientation_(
97 self.textView.enclosingScrollView(),
98 NSVerticalRuler)
99 self.verticalRulerView.setClientView_(self.textView)
100 self.startCLIForTextView()
101
102
103 def appWillTerminate_(self, notification):
104 """appWillTerminate"""
105
106 self.engine.stopService()
107
108
109 def complete(self, token):
110 """Complete token in engine's user_ns
111
112 Parameters
113 ----------
114 token : string
115
116 Result
117 ------
118 Deferred result of ipython1.kernel.engineservice.IEngineInteractive.complete
119 """
120
121 return self.engine.complete(token)
122
123
124 def execute(self, block, blockID=None):
125 self.waitingForEngine = True
126 self.willChangeValueForKey_('commandHistory')
127 d = super(IPythonCocoaController, self).execute(block, blockID)
128 d.addBoth(self._engineDone)
129 d.addCallback(self._updateUserNS)
130
131 return d
132
133
134 def _engineDone(self, x):
135 self.waitingForEngine = False
136 self.didChangeValueForKey_('commandHistory')
137 return x
138
139 def _updateUserNS(self, result):
140 """Update self.userNS from self.engine's namespace"""
141 d = self.engine.keys()
142 d.addCallback(self._getEngineNamepsaceValuesForKeys)
143
144 return result
145
146
147 def _getEngineNamepsaceValuesForKeys(self, keys):
148 d = self.engine.pull(keys)
149 d.addCallback(self._storeEngineNamespaceValues, keys=keys)
150
151
152 def _storeEngineNamespaceValues(self, values, keys=[]):
153 assert(len(values) == len(keys))
154 self.willChangeValueForKey_('userNS')
155 for (k,v) in zip(keys,values):
156 self.userNS[k] = saferepr(v)
157 self.didChangeValueForKey_('userNS')
158
159
160 def startCLIForTextView(self):
161 """Print banner"""
162
163 banner = """IPython1 0.X -- An enhanced Interactive Python."""
164
165 self.insert_text(banner + '\n\n')
166
167 # NSTextView/IPythonTextView delegate methods
168 def textView_doCommandBySelector_(self, textView, selector):
169 assert(textView == self.textView)
170 NSLog("textView_doCommandBySelector_: "+selector)
171
172
173 if(selector == 'insertNewline:'):
174 indent = self.currentIndentString()
175 if(indent):
176 line = indent + self.currentLine()
177 else:
178 line = self.currentLine()
179
180 if(self.is_complete(self.currentBlock())):
181 self.execute(self.currentBlock(),
182 blockID=self.currentBlockID)
183 self.currentBlockID = self.nextBlockID()
184 return True
185
186 return False
187
188 elif(selector == 'moveUp:'):
189 self.replaceCurrentBlockWithString(textView, self.get_history_item_previous(self.currentBlock()))
190 return True
191
192 elif(selector == 'moveDown:'):
193 self.replaceCurrentBlockWithString(textView, self.get_history_item_next(self.currentBlock()))
194 return True
195
196 elif(selector == 'moveToBeginningOfParagraph:'):
197 textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location, 0))
198 return True
199 elif(selector == 'moveToEndOfParagraph:'):
200 textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location + \
201 self.currentBlockRange().length, 0))
202 return True
203 elif(selector == 'deleteToEndOfParagraph:'):
204 if(textView.selectedRange().location <= self.currentBlockRange().location):
205 # Intersect the selected range with the current line range
206 if(self.currentBlockRange().length < 0):
207 self.blockRanges[self.currentBlockID].length = 0
208
209 r = NSIntersectionRange(textView.rangesForUserTextChange()[0],
210 self.currentBlockRange())
211
212 if(r.length > 0): #no intersection
213 textView.setSelectedRange_(r)
214
215 return False # don't actually handle the delete
216
217 elif(selector == 'insertTab:'):
218 if(len(self.currentLine().strip()) == 0): #only white space
219 return False
220 else:
221 self.textView.complete_(self)
222 return True
223
224 elif(selector == 'deleteBackward:'):
225 #if we're at the beginning of the current block, ignore
226 if(textView.selectedRange().location == self.currentBlockRange().location):
227 return True
228 else:
229 self.currentBlockRange().length-=1
230 return False
231 return False
232
233
234 def textView_shouldChangeTextInRanges_replacementStrings_(self, textView, ranges, replacementStrings):
235 """
236 Delegate method for NSTextView.
237
238 Refuse change text in ranges not at end, but make those changes at end.
239 """
240
241 #print 'textView_shouldChangeTextInRanges_replacementStrings_:',ranges,replacementStrings
242 assert(len(ranges) == len(replacementStrings))
243 allow = True
244 for r,s in zip(ranges, replacementStrings):
245 r = r.rangeValue()
246 if(textView.textStorage().length() > 0 and
247 r.location < self.currentBlockRange().location):
248 self.insert_text(s)
249 allow = False
250
251
252 self.blockRanges.setdefault(self.currentBlockID, self.currentBlockRange()).length += len(s)
253
254 return allow
255
256 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, textView, words, charRange, index):
257 try:
258 token = textView.textStorage().string().substringWithRange_(charRange)
259 completions = blockingCallFromThread(self.complete, token)
260 except:
261 completions = objc.nil
262 NSBeep()
263
264 return (completions,0)
265
266
267 def nextBlockID(self):
268
269 return uuid.uuid4()
270
271 def currentBlockRange(self):
272 return self.blockRanges.get(self.currentBlockID, NSMakeRange(self.textView.textStorage().length(), 0))
273
274 def currentBlock(self):
275 """The current block's text"""
276
277 return self.textForRange(self.currentBlockRange())
278
279 def textForRange(self, textRange):
280 """textForRange"""
281
282 return self.textView.textStorage().string().substringWithRange_(textRange)
283
284 def currentLine(self):
285 block = self.textForRange(self.currentBlockRange())
286 block = block.split('\n')
287 return block[-1]
288
289 def update_cell_prompt(self, result):
290 blockID = result['blockID']
291 self.insert_text(self.inputPrompt(result=result),
292 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
293 scrollToVisible=False
294 )
295
296 return result
297
298
299 def render_result(self, result):
300 blockID = result['blockID']
301 inputRange = self.blockRanges[blockID]
302 del self.blockRanges[blockID]
303
304 #print inputRange,self.currentBlockRange()
305 self.insert_text('\n' +
306 self.outputPrompt(result) +
307 result.get('display',{}).get('pprint','') +
308 '\n\n',
309 textRange=NSMakeRange(inputRange.location+inputRange.length, 0))
310 return result
311
312
313 def render_error(self, failure):
314 self.insert_text(str(failure))
315 return failure
316
317
318 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
319 """Insert text into textView at textRange, updating blockRanges as necessary"""
320
321 if(textRange == None):
322 textRange = NSMakeRange(self.textView.textStorage().length(), 0) #range for end of text
323
324 for r in self.blockRanges.itervalues():
325 intersection = NSIntersectionRange(r,textRange)
326 if(intersection.length == 0): #ranges don't intersect
327 if r.location >= textRange.location:
328 r.location += len(string)
329 else: #ranges intersect
330 if(r.location <= textRange.location):
331 assert(intersection.length == textRange.length)
332 r.length += textRange.length
333 else:
334 r.location += intersection.length
335
336 self.textView.replaceCharactersInRange_withString_(textRange, string) #textStorage().string()
337 self.textView.setSelectedRange_(NSMakeRange(textRange.location+len(string), 0))
338 if(scrollToVisible):
339 self.textView.scrollRangeToVisible_(textRange)
340
341
342
343 def replaceCurrentBlockWithString(self, textView, string):
344 textView.replaceCharactersInRange_withString_(self.currentBlockRange(),
345 string)
346 r = NSMakeRange(textView.textStorage().length(), 0)
347 textView.scrollRangeToVisible_(r)
348 textView.setSelectedRange_(r)
349
350
351 def currentIndentString(self):
352 """returns string for indent or None if no indent"""
353
354 if(len(self.currentBlock()) > 0):
355 lines = self.currentBlock().split('\n')
356 currentIndent = len(lines[-1]) - len(lines[-1])
357 if(currentIndent == 0):
358 currentIndent = self.tabSpaces
359
360 if(self.tabUsesSpaces):
361 result = ' ' * currentIndent
362 else:
363 result = '\t' * (currentIndent/self.tabSpaces)
364 else:
365 result = None
366
367 return result
368
369
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -0,0 +1,293 b''
1 // !$*UTF8*$!
2 {
3 archiveVersion = 1;
4 classes = {
5 };
6 objectVersion = 44;
7 objects = {
8
9 /* Begin PBXBuildFile section */
10 77631A270C06C501005415CB /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77631A260C06C501005415CB /* Python.framework */; };
11 77631A3F0C0748CF005415CB /* main.py in Resources */ = {isa = PBXBuildFile; fileRef = 77631A3E0C0748CF005415CB /* main.py */; };
12 7790198F0C07548A00326F66 /* IPython1SandboxAppDelegate.py in Resources */ = {isa = PBXBuildFile; fileRef = 7790198E0C07548A00326F66 /* IPython1SandboxAppDelegate.py */; };
13 77C8C1F90C07829500965286 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77C8C1F70C07829500965286 /* MainMenu.xib */; };
14 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
15 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
16 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
17 /* End PBXBuildFile section */
18
19 /* Begin PBXFileReference section */
20 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
21 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
22 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
23 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
24 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
25 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
26 32CA4F630368D1EE00C91783 /* IPython1Sandbox_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IPython1Sandbox_Prefix.pch; sourceTree = "<group>"; };
27 4CA32F870D8879B100311764 /* IPythonCocoaController Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "IPythonCocoaController Tests-Info.plist"; sourceTree = "<group>"; };
28 77631A260C06C501005415CB /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = /System/Library/Frameworks/Python.framework; sourceTree = "<absolute>"; };
29 77631A3E0C0748CF005415CB /* main.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = main.py; sourceTree = "<group>"; };
30 7790198E0C07548A00326F66 /* IPython1SandboxAppDelegate.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = IPython1SandboxAppDelegate.py; sourceTree = "<group>"; };
31 77C8C1F80C07829500965286 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
32 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
33 8D1107320486CEB800E47090 /* IPython1Sandbox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IPython1Sandbox.app; sourceTree = BUILT_PRODUCTS_DIR; };
34 /* End PBXFileReference section */
35
36 /* Begin PBXFrameworksBuildPhase section */
37 8D11072E0486CEB800E47090 /* Frameworks */ = {
38 isa = PBXFrameworksBuildPhase;
39 buildActionMask = 2147483647;
40 files = (
41 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
42 77631A270C06C501005415CB /* Python.framework in Frameworks */,
43 );
44 runOnlyForDeploymentPostprocessing = 0;
45 };
46 /* End PBXFrameworksBuildPhase section */
47
48 /* Begin PBXGroup section */
49 080E96DDFE201D6D7F000001 /* Classes */ = {
50 isa = PBXGroup;
51 children = (
52 7790198E0C07548A00326F66 /* IPython1SandboxAppDelegate.py */,
53 );
54 name = Classes;
55 sourceTree = "<group>";
56 };
57 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
58 isa = PBXGroup;
59 children = (
60 77631A260C06C501005415CB /* Python.framework */,
61 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
62 );
63 name = "Linked Frameworks";
64 sourceTree = "<group>";
65 };
66 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
67 isa = PBXGroup;
68 children = (
69 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
70 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
71 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
72 );
73 name = "Other Frameworks";
74 sourceTree = "<group>";
75 };
76 19C28FACFE9D520D11CA2CBB /* Products */ = {
77 isa = PBXGroup;
78 children = (
79 8D1107320486CEB800E47090 /* IPython1Sandbox.app */,
80 );
81 name = Products;
82 sourceTree = "<group>";
83 };
84 29B97314FDCFA39411CA2CEA /* IPython1Sandbox */ = {
85 isa = PBXGroup;
86 children = (
87 080E96DDFE201D6D7F000001 /* Classes */,
88 29B97315FDCFA39411CA2CEA /* Other Sources */,
89 29B97317FDCFA39411CA2CEA /* Resources */,
90 29B97323FDCFA39411CA2CEA /* Frameworks */,
91 19C28FACFE9D520D11CA2CBB /* Products */,
92 4CA32F870D8879B100311764 /* IPythonCocoaController Tests-Info.plist */,
93 );
94 name = IPython1Sandbox;
95 sourceTree = "<group>";
96 };
97 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
98 isa = PBXGroup;
99 children = (
100 32CA4F630368D1EE00C91783 /* IPython1Sandbox_Prefix.pch */,
101 29B97316FDCFA39411CA2CEA /* main.m */,
102 77631A3E0C0748CF005415CB /* main.py */,
103 );
104 name = "Other Sources";
105 sourceTree = "<group>";
106 };
107 29B97317FDCFA39411CA2CEA /* Resources */ = {
108 isa = PBXGroup;
109 children = (
110 77C8C1F70C07829500965286 /* MainMenu.xib */,
111 8D1107310486CEB800E47090 /* Info.plist */,
112 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
113 );
114 name = Resources;
115 sourceTree = "<group>";
116 };
117 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
118 isa = PBXGroup;
119 children = (
120 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
121 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
122 );
123 name = Frameworks;
124 sourceTree = "<group>";
125 };
126 /* End PBXGroup section */
127
128 /* Begin PBXNativeTarget section */
129 8D1107260486CEB800E47090 /* IPython1Sandbox */ = {
130 isa = PBXNativeTarget;
131 buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "IPython1Sandbox" */;
132 buildPhases = (
133 8D1107290486CEB800E47090 /* Resources */,
134 8D11072C0486CEB800E47090 /* Sources */,
135 8D11072E0486CEB800E47090 /* Frameworks */,
136 );
137 buildRules = (
138 );
139 dependencies = (
140 );
141 name = IPython1Sandbox;
142 productInstallPath = "$(HOME)/Applications";
143 productName = IPython1Sandbox;
144 productReference = 8D1107320486CEB800E47090 /* IPython1Sandbox.app */;
145 productType = "com.apple.product-type.application";
146 };
147 /* End PBXNativeTarget section */
148
149 /* Begin PBXProject section */
150 29B97313FDCFA39411CA2CEA /* Project object */ = {
151 isa = PBXProject;
152 buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "IPython1Sandbox" */;
153 compatibilityVersion = "Xcode 3.0";
154 hasScannedForEncodings = 1;
155 mainGroup = 29B97314FDCFA39411CA2CEA /* IPython1Sandbox */;
156 projectDirPath = "";
157 projectRoot = "";
158 targets = (
159 8D1107260486CEB800E47090 /* IPython1Sandbox */,
160 );
161 };
162 /* End PBXProject section */
163
164 /* Begin PBXResourcesBuildPhase section */
165 8D1107290486CEB800E47090 /* Resources */ = {
166 isa = PBXResourcesBuildPhase;
167 buildActionMask = 2147483647;
168 files = (
169 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
170 77631A3F0C0748CF005415CB /* main.py in Resources */,
171 7790198F0C07548A00326F66 /* IPython1SandboxAppDelegate.py in Resources */,
172 77C8C1F90C07829500965286 /* MainMenu.xib in Resources */,
173 );
174 runOnlyForDeploymentPostprocessing = 0;
175 };
176 /* End PBXResourcesBuildPhase section */
177
178 /* Begin PBXSourcesBuildPhase section */
179 8D11072C0486CEB800E47090 /* Sources */ = {
180 isa = PBXSourcesBuildPhase;
181 buildActionMask = 2147483647;
182 files = (
183 8D11072D0486CEB800E47090 /* main.m in Sources */,
184 );
185 runOnlyForDeploymentPostprocessing = 0;
186 };
187 /* End PBXSourcesBuildPhase section */
188
189 /* Begin PBXVariantGroup section */
190 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
191 isa = PBXVariantGroup;
192 children = (
193 089C165DFE840E0CC02AAC07 /* English */,
194 );
195 name = InfoPlist.strings;
196 sourceTree = "<group>";
197 };
198 77C8C1F70C07829500965286 /* MainMenu.xib */ = {
199 isa = PBXVariantGroup;
200 children = (
201 77C8C1F80C07829500965286 /* English */,
202 );
203 name = MainMenu.xib;
204 sourceTree = "<group>";
205 };
206 /* End PBXVariantGroup section */
207
208 /* Begin XCBuildConfiguration section */
209 C01FCF4B08A954540054247B /* Debug */ = {
210 isa = XCBuildConfiguration;
211 buildSettings = {
212 COPY_PHASE_STRIP = NO;
213 CURRENT_PROJECT_VERSION = 1;
214 GCC_DYNAMIC_NO_PIC = NO;
215 GCC_ENABLE_FIX_AND_CONTINUE = YES;
216 GCC_MODEL_TUNING = G5;
217 GCC_OPTIMIZATION_LEVEL = 0;
218 GCC_PRECOMPILE_PREFIX_HEADER = YES;
219 GCC_PREFIX_HEADER = IPython1Sandbox_Prefix.pch;
220 INFOPLIST_FILE = Info.plist;
221 INSTALL_PATH = "$(HOME)/Applications";
222 PRODUCT_NAME = IPython1Sandbox;
223 VERSIONING_SYSTEM = "apple-generic";
224 WRAPPER_EXTENSION = app;
225 ZERO_LINK = YES;
226 };
227 name = Debug;
228 };
229 C01FCF4C08A954540054247B /* Release */ = {
230 isa = XCBuildConfiguration;
231 buildSettings = {
232 CURRENT_PROJECT_VERSION = 1;
233 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
234 GCC_MODEL_TUNING = G5;
235 GCC_PRECOMPILE_PREFIX_HEADER = YES;
236 GCC_PREFIX_HEADER = IPython1Sandbox_Prefix.pch;
237 INFOPLIST_FILE = Info.plist;
238 INSTALL_PATH = "$(HOME)/Applications";
239 PRODUCT_NAME = IPython1Sandbox;
240 VERSIONING_SYSTEM = "apple-generic";
241 WRAPPER_EXTENSION = app;
242 };
243 name = Release;
244 };
245 C01FCF4F08A954540054247B /* Debug */ = {
246 isa = XCBuildConfiguration;
247 buildSettings = {
248 GCC_WARN_ABOUT_RETURN_TYPE = YES;
249 GCC_WARN_UNUSED_VARIABLE = YES;
250 PREBINDING = NO;
251 SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
252 };
253 name = Debug;
254 };
255 C01FCF5008A954540054247B /* Release */ = {
256 isa = XCBuildConfiguration;
257 buildSettings = {
258 ARCHS = (
259 ppc,
260 i386,
261 );
262 GCC_WARN_ABOUT_RETURN_TYPE = YES;
263 GCC_WARN_UNUSED_VARIABLE = YES;
264 PREBINDING = NO;
265 SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
266 };
267 name = Release;
268 };
269 /* End XCBuildConfiguration section */
270
271 /* Begin XCConfigurationList section */
272 C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "IPython1Sandbox" */ = {
273 isa = XCConfigurationList;
274 buildConfigurations = (
275 C01FCF4B08A954540054247B /* Debug */,
276 C01FCF4C08A954540054247B /* Release */,
277 );
278 defaultConfigurationIsVisible = 0;
279 defaultConfigurationName = Release;
280 };
281 C01FCF4E08A954540054247B /* Build configuration list for PBXProject "IPython1Sandbox" */ = {
282 isa = XCConfigurationList;
283 buildConfigurations = (
284 C01FCF4F08A954540054247B /* Debug */,
285 C01FCF5008A954540054247B /* Release */,
286 );
287 defaultConfigurationIsVisible = 0;
288 defaultConfigurationName = Release;
289 };
290 /* End XCConfigurationList section */
291 };
292 rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
293 }
@@ -0,0 +1,39 b''
1 #
2 # IPython1SandboxAppDelegate.py
3 # IPython1Sandbox
4 #
5 # Created by Barry Wark on 3/4/08.
6 # Copyright __MyCompanyName__ 2008. All rights reserved.
7 #
8
9 from Foundation import NSObject, NSPredicate
10 import objc
11 import threading
12
13 from PyObjCTools import AppHelper
14
15 from twisted.internet import reactor
16
17 class IPython1SandboxAppDelegate(NSObject):
18 ipythonController = objc.IBOutlet()
19
20 def applicationShouldTerminate_(self, sender):
21 if reactor.running:
22 reactor.addSystemEventTrigger(
23 'after', 'shutdown', AppHelper.stopEventLoop)
24 reactor.stop()
25 return False
26 return True
27
28
29 def applicationDidFinishLaunching_(self, sender):
30 reactor.interleave(AppHelper.callAfter)
31 assert(reactor.running)
32
33
34 def workspaceFilterPredicate(self):
35 return NSPredicate.predicateWithFormat_("NOT (self.value BEGINSWITH '<')")
36
37
38
39
@@ -0,0 +1,7 b''
1 //
2 // Prefix header for all source files of the 'IPython1Sandbox' target in the 'IPython1Sandbox' project
3 //
4
5 #ifdef __OBJC__
6 #import <Cocoa/Cocoa.h>
7 #endif
@@ -0,0 +1,20 b''
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 <plist version="1.0">
4 <dict>
5 <key>CFBundleDevelopmentRegion</key>
6 <string>English</string>
7 <key>CFBundleExecutable</key>
8 <string>${EXECUTABLE_NAME}</string>
9 <key>CFBundleIdentifier</key>
10 <string>com.yourcompany.IPythonCocoaController Tests</string>
11 <key>CFBundleInfoDictionaryVersion</key>
12 <string>6.0</string>
13 <key>CFBundlePackageType</key>
14 <string>BNDL</string>
15 <key>CFBundleSignature</key>
16 <string>????</string>
17 <key>CFBundleVersion</key>
18 <string>1.0</string>
19 </dict>
20 </plist>
@@ -0,0 +1,30 b''
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 <plist version="1.0">
4 <dict>
5 <key>CFBundleDevelopmentRegion</key>
6 <string>English</string>
7 <key>CFBundleExecutable</key>
8 <string>${EXECUTABLE_NAME}</string>
9 <key>CFBundleIconFile</key>
10 <string></string>
11 <key>CFBundleIdentifier</key>
12 <string>com.yourcompany.IPython1Sandbox</string>
13 <key>CFBundleInfoDictionaryVersion</key>
14 <string>6.0</string>
15 <key>CFBundleName</key>
16 <string>${PRODUCT_NAME}</string>
17 <key>CFBundlePackageType</key>
18 <string>APPL</string>
19 <key>CFBundleShortVersionString</key>
20 <string>0.1</string>
21 <key>CFBundleSignature</key>
22 <string>????</string>
23 <key>CFBundleVersion</key>
24 <string>1.0</string>
25 <key>NSMainNibFile</key>
26 <string>MainMenu</string>
27 <key>NSPrincipalClass</key>
28 <string>NSApplication</string>
29 </dict>
30 </plist>
@@ -0,0 +1,49 b''
1 //
2 // main.m
3 // IPython1Sandbox
4 //
5 // Created by Barry Wark on 3/4/08.
6 // Copyright __MyCompanyName__ 2008. All rights reserved.
7 //
8
9 #import <Python/Python.h>
10 #import <Cocoa/Cocoa.h>
11
12 int main(int argc, char *argv[])
13 {
14 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
15
16 NSBundle *mainBundle = [NSBundle mainBundle];
17 NSString *resourcePath = [mainBundle resourcePath];
18 NSArray *pythonPathArray = [NSArray arrayWithObjects: resourcePath, [resourcePath stringByAppendingPathComponent:@"PyObjC"], nil];
19
20 setenv("PYTHONPATH", [[pythonPathArray componentsJoinedByString:@":"] UTF8String], 1);
21
22 NSArray *possibleMainExtensions = [NSArray arrayWithObjects: @"py", @"pyc", @"pyo", nil];
23 NSString *mainFilePath = nil;
24
25 for (NSString *possibleMainExtension in possibleMainExtensions) {
26 mainFilePath = [mainBundle pathForResource: @"main" ofType: possibleMainExtension];
27 if ( mainFilePath != nil ) break;
28 }
29
30 if ( !mainFilePath ) {
31 [NSException raise: NSInternalInconsistencyException format: @"%s:%d main() Failed to find the Main.{py,pyc,pyo} file in the application wrapper's Resources directory.", __FILE__, __LINE__];
32 }
33
34 Py_SetProgramName("/usr/bin/python");
35 Py_Initialize();
36 PySys_SetArgv(argc, (char **)argv);
37
38 const char *mainFilePathPtr = [mainFilePath UTF8String];
39 FILE *mainFile = fopen(mainFilePathPtr, "r");
40 int result = PyRun_SimpleFile(mainFile, (char *)[[mainFilePath lastPathComponent] UTF8String]);
41
42 if ( result != 0 )
43 [NSException raise: NSInternalInconsistencyException
44 format: @"%s:%d main() PyRun_SimpleFile failed with file '%@'. See console for errors.", __FILE__, __LINE__, mainFilePath];
45
46 [pool drain];
47
48 return result;
49 }
@@ -0,0 +1,24 b''
1 #
2 # main.py
3 # IPython1Sandbox
4 #
5 # Created by Barry Wark on 3/4/08.
6 # Copyright __MyCompanyName__ 2008. All rights reserved.
7 #
8
9 #import modules required by application
10 import objc
11 import Foundation
12 import AppKit
13
14 from PyObjCTools import AppHelper
15
16 from twisted.internet import _threadedselect
17 reactor = _threadedselect.install()
18
19 # import modules containing classes required to start application and load MainMenu.nib
20 import IPython1SandboxAppDelegate
21 import IPython.frontend.cocoa.cocoa_frontend
22
23 # pass control to AppKit
24 AppHelper.runEventLoop()
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,27 b''
1 2008-03-14 00:06:49-0700 [-] Log opened.
2 2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerCompletesToken <--
3 2008-03-14 00:06:49-0700 [-] Unhandled error in Deferred:
4 2008-03-14 00:06:49-0700 [-] Unhandled Error
5 Traceback (most recent call last):
6 File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/internet/utils.py", line 144, in runWithWarningsSuppressed
7 result = f(*a, **kw)
8 File "/Users/barry/Desktop/ipython1-cocoa/ipython1/frontend/cocoa/tests/test_cocoa_frontend.py", line 94, in testControllerCompletesToken
9 self.controller.executeRequest([code]).addCallback(testCompletes)
10 File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/internet/defer.py", line 196, in addCallback
11 callbackKeywords=kw)
12 File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/internet/defer.py", line 187, in addCallbacks
13 self._runCallbacks()
14 --- <exception caught here> ---
15 File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/internet/defer.py", line 325, in _runCallbacks
16 self.result = callback(self.result, *args, **kw)
17 File "/Users/barry/Desktop/ipython1-cocoa/ipython1/frontend/cocoa/tests/test_cocoa_frontend.py", line 89, in testCompletes
18 self.assert_("longNameVariable" in result)
19 File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/trial/unittest.py", line 136, in failUnless
20 raise self.failureException(msg)
21 twisted.trial.unittest.FailTest: None
22
23 2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerExecutesCode <--
24 2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerInstantiatesIEngineInteractive <--
25 2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerMirrorsUserNSWithValuesAsStrings <--
26 2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerRaisesCompilerErrorForIllegalCode <--
27 2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerReturnsNoneForIncompleteCode <--
@@ -0,0 +1,97 b''
1 # encoding: utf-8
2 """This file contains unittests for the ipython1.frontend.cocoa.cocoa_frontend module.
3
4 Things that should be tested:
5
6 - IPythonCocoaController instantiates an IEngineInteractive
7 - IPythonCocoaController executes code on the engine
8 - IPythonCocoaController returns continuation for incomplete code
9 - IPythonCocoaController returns failure for exceptions raised in executed code
10 - IPythonCocoaController mirrors engine's user_ns
11 """
12 __docformat__ = "restructuredtext en"
13
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2005 Fernando Perez <fperez@colorado.edu>
16 # Brian E Granger <ellisonbg@gmail.com>
17 # Benjamin Ragan-Kelley <benjaminrk@gmail.com>
18 #
19 # Distributed under the terms of the BSD License. The full license is in
20 # the file COPYING, distributed as part of this software.
21 #-------------------------------------------------------------------------------
22
23 #-------------------------------------------------------------------------------
24 # Imports
25 #-------------------------------------------------------------------------------
26 from ipython1.core.interpreter import Interpreter
27 from ipython1.testutils.parametric import Parametric, parametric
28 from ipython1.core.interpreter import COMPILER_ERROR, INCOMPLETE_INPUT,\
29 COMPLETE_INPUT
30 import ipython1.kernel.engineservice as es
31 from ipython1.testutils.util import DeferredTestCase
32 from twisted.internet.defer import succeed
33 from ipython1.frontend.cocoa.cocoa_frontend import IPythonCocoaController,\
34 IPythonCLITextViewDelegate,\
35 CompilerError
36
37
38 class TestIPythonCocoaControler(DeferredTestCase):
39 """Tests for IPythonCocoaController"""
40
41 def setUp(self):
42 self.controller = IPythonCocoaController.alloc().init()
43 self.controller.awakeFromNib()
44 self.engine = es.EngineService()
45 self.engine.startService()
46
47
48 def tearDown(self):
49 self.controller = None
50 self.engine.stopService()
51
52 def testControllerExecutesCode(self):
53 code ="""5+5"""
54 expected = Interpreter().execute(code)
55 del expected['number']
56 def removeNumberAndID(result):
57 del result['number']
58 del result['id']
59 return result
60 self.assertDeferredEquals(self.controller.executeRequest([code]).addCallback(removeNumberAndID), expected)
61
62 def testControllerReturnsNoneForIncompleteCode(self):
63 code = """def test(a):"""
64 expected = None
65 self.assertDeferredEquals(self.controller.executeRequest([code]), expected)
66
67
68 def testControllerRaisesCompilerErrorForIllegalCode(self):
69 """testControllerRaisesCompilerErrorForIllegalCode"""
70
71 code = """def test() pass"""
72 self.assertDeferredRaises(self.controller.executeRequest([code]), CompilerError)
73
74 def testControllerMirrorsUserNSWithValuesAsStrings(self):
75 code = """userns1=1;userns2=2"""
76 def testControllerUserNS(result):
77 self.assertEquals(self.controller.userNS['userns1'], str(1))
78 self.assertEquals(self.controller.userNS['userns2'], str(2))
79
80 self.controller.executeRequest([code]).addCallback(testControllerUserNS)
81
82
83 def testControllerInstantiatesIEngine(self):
84 self.assert_(es.IEngine.providedBy(self.controller.engine))
85
86 def testControllerCompletesToken(self):
87 code = """longNameVariable=10"""
88 def testCompletes(result):
89 self.assert_("longNameVariable" in result)
90
91 def testCompleteToken(result):
92 self.controller.complete("longNa").addCallback(testCompletes)
93
94 self.controller.executeRequest([code]).addCallback(testCompletes)
95
96
97 Parametric(TestIPythonCocoaControler) No newline at end of file
@@ -0,0 +1,254 b''
1 # encoding: utf-8
2 """
3 FrontEndBase: Base classes for frontends.
4
5 Todo:
6 - synchronous and asynchronous interfaces
7 - adapter to add async to FrontEndBase
8 """
9 __docformat__ = "restructuredtext en"
10
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 Barry Wark <barrywark at gmail _dot_ com>
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
17
18 #-------------------------------------------------------------------------------
19 # Imports
20 #-------------------------------------------------------------------------------
21 import string
22 import uuid
23
24
25 from IPython.kernel.core.history import FrontEndHistory
26 from IPython.kernel.core.util import Bunch
27
28 from IPython.kernel.engineservice import IEngineCore
29
30 import zope.interface as zi
31
32 import _ast
33
34 ##############################################################################
35 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
36 # not
37
38 rc = Bunch()
39 rc.prompt_in1 = r'In [$number]: '
40 rc.prompt_in2 = r'...'
41 rc.prompt_out = r'Out [$number]: '
42
43 ##############################################################################
44
45 class IFrontEnd(zi.Interface):
46 """Interface for frontends. All methods return t.i.d.Deferred"""
47
48 zi.Attribute("input_prompt_template", "string.Template instance substituteable with execute result.")
49 zi.Attribute("output_prompt_template", "string.Template instance substituteable with execute result.")
50 zi.Attribute("continuation_prompt_template", "string.Template instance substituteable with execute result.")
51
52 def __init__(engine=None, history=None):
53 """
54 Parameters:
55 interpreter : IPython.kernel.engineservice.IEngineCore
56 """
57 pass
58
59 def update_cell_prompt(self, result):
60 """Subclass may override to update the input prompt for a block.
61 Since this method will be called as a twisted.internet.defer.Deferred's callback,
62 implementations should return result when finished."""
63
64 return result
65
66 def render_result(self, result):
67 """Render the result of an execute call. Implementors may choose the method of rendering.
68 For example, a notebook-style frontend might render a Chaco plot inline.
69
70 Parameters:
71 result : dict (result of IEngineBase.execute )
72
73 Result:
74 Output of frontend rendering
75 """
76
77 return result
78
79 def render_error(self, failure):
80 """Subclasses must override to render the failure. Since this method will be called as a
81 twisted.internet.defer.Deferred's callback, implementations should return result
82 when finished."""
83
84 return failure
85
86 # TODO: finish interface
87
88 class FrontEndBase(object):
89 """
90 FrontEndBase manages the state tasks for a CLI frontend:
91 - Input and output history management
92 - Input/continuation and output prompt generation
93
94 Some issues (due to possibly unavailable engine):
95 - How do we get the current cell number for the engine?
96 - How do we handle completions?
97 """
98
99 zi.implements(IFrontEnd)
100
101 history_cursor = 0
102
103 current_indent_level = 0
104
105
106 input_prompt_template = string.Template(rc.prompt_in1)
107 output_prompt_template = string.Template(rc.prompt_out)
108 continuation_prompt_template = string.Template(rc.prompt_in2)
109
110 def __init__(self, engine=None, history=None):
111 assert(engine==None or IEngineCore.providedBy(engine))
112 self.engine = IEngineCore(engine)
113 if history is None:
114 self.history = FrontEndHistory(input_cache=[''])
115 else:
116 self.history = history
117
118
119 def inputPrompt(self, result={}):
120 """Returns the current input prompt
121
122 It would be great to use ipython1.core.prompts.Prompt1 here
123 """
124
125 result.setdefault('number','')
126
127 return self.input_prompt_template.safe_substitute(result)
128
129
130 def continuationPrompt(self):
131 """Returns the current continuation prompt"""
132
133 return self.continuation_prompt_template.safe_substitute()
134
135 def outputPrompt(self, result):
136 """Returns the output prompt for result"""
137
138 return self.output_prompt_template.safe_substitute(result)
139
140
141 def is_complete(self, block):
142 """Determine if block is complete.
143
144 Parameters
145 block : string
146
147 Result
148 True if block can be sent to the engine without compile errors.
149 False otherwise.
150 """
151
152 try:
153 self.compile_ast(block)
154 return True
155 except:
156 return False
157
158
159 def compile_ast(self, block):
160 """Compile block to an AST
161
162 Parameters:
163 block : str
164
165 Result:
166 AST
167
168 Throws:
169 Exception if block cannot be compiled
170 """
171
172 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
173
174
175 def execute(self, block, blockID=None):
176 """Execute the block and return result.
177
178 Parameters:
179 block : {str, AST}
180 blockID : any
181 Caller may provide an ID to identify this block. result['blockID'] := blockID
182
183 Result:
184 Deferred result of self.interpreter.execute
185 """
186 # if(not isinstance(block, _ast.AST)):
187 # block = self.compile_ast(block)
188
189 if(blockID == None):
190 blockID = uuid.uuid4() #random UUID
191
192 d = self.engine.execute(block)
193 d.addCallback(self._add_block_id, blockID)
194 d.addCallback(self.update_cell_prompt)
195 d.addCallbacks(self.render_result, errback=self.render_error)
196
197 return d
198
199 def _add_block_id(self, result, blockID):
200 """add_block_id"""
201
202 result['blockID'] = blockID
203
204 return result
205
206
207 def get_history_item_previous(self, current_block):
208 """ Returns previous history string and decrement history cursor.
209 """
210 command = self.history.get_history_item(self.history_cursor - 1)
211 if command is not None:
212 self.history.input_cache[self.history_cursor] = current_block
213 self.history_cursor -= 1
214 return command
215
216
217 def get_history_item_next(self, current_block):
218 """ Returns next history string and increment history cursor.
219 """
220 command = self.history.get_history_item(self.history_cursor + 1)
221 if command is not None:
222 self.history.input_cache[self.history_cursor] = current_block
223 self.history_cursor += 1
224 return command
225
226 ###
227 # Subclasses probably want to override these methods...
228 ###
229
230 def update_cell_prompt(self, result):
231 """Subclass may override to update the input prompt for a block.
232 Since this method will be called as a twisted.internet.defer.Deferred's callback,
233 implementations should return result when finished."""
234
235 return result
236
237
238 def render_result(self, result):
239 """Subclasses must override to render result. Since this method will be called as a
240 twisted.internet.defer.Deferred's callback, implementations should return result
241 when finished."""
242
243 return result
244
245
246 def render_error(self, failure):
247 """Subclasses must override to render the failure. Since this method will be called as a
248 twisted.internet.defer.Deferred's callback, implementations should return result
249 when finished."""
250
251 return failure
252
253
254
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,114 b''
1 # encoding: utf-8
2
3 """This file contains unittests for the frontendbase module."""
4
5 __docformat__ = "restructuredtext en"
6
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
13
14 #-------------------------------------------------------------------------------
15 # Imports
16 #-------------------------------------------------------------------------------
17
18 import unittest
19 from IPython.frontend import frontendbase
20 from IPython.kernel.engineservice import EngineService
21
22 class FrontEndCallbackChecker(frontendbase.FrontEndBase):
23 """FrontEndBase subclass for checking callbacks"""
24 def __init__(self, engine=None, history=None):
25 super(FrontEndCallbackChecker, self).__init__(engine=engine, history=history)
26 self.updateCalled = False
27 self.renderResultCalled = False
28 self.renderErrorCalled = False
29
30 def update_cell_prompt(self, result):
31 self.updateCalled = True
32 return result
33
34 def render_result(self, result):
35 self.renderResultCalled = True
36 return result
37
38
39 def render_error(self, failure):
40 self.renderErrorCalled = True
41 return failure
42
43
44
45
46 class TestFrontendBase(unittest.TestCase):
47 def setUp(self):
48 """Setup the EngineService and FrontEndBase"""
49
50 self.fb = FrontEndCallbackChecker(engine=EngineService())
51
52
53 def test_implementsIFrontEnd(self):
54 assert(frontendbase.IFrontEnd.implementedBy(frontendbase.FrontEndBase))
55
56
57 def test_is_completeReturnsFalseForIncompleteBlock(self):
58 """"""
59
60 block = """def test(a):"""
61
62 assert(self.fb.is_complete(block) == False)
63
64 def test_is_completeReturnsTrueForCompleteBlock(self):
65 """"""
66
67 block = """def test(a): pass"""
68
69 assert(self.fb.is_complete(block))
70
71 block = """a=3"""
72
73 assert(self.fb.is_complete(block))
74
75
76 def test_blockIDAddedToResult(self):
77 block = """3+3"""
78
79 d = self.fb.execute(block, blockID='TEST_ID')
80
81 d.addCallback(self.checkBlockID, expected='TEST_ID')
82
83 def checkBlockID(self, result, expected=""):
84 assert(result['blockID'] == expected)
85
86
87 def test_callbacksAddedToExecuteRequest(self):
88 """test that
89 update_cell_prompt
90 render_result
91
92 are added to execute request
93 """
94
95 d = self.fb.execute("10+10")
96 d.addCallback(self.checkCallbacks)
97
98
99 def checkCallbacks(self, result):
100 assert(self.fb.updateCalled)
101 assert(self.fb.renderResultCalled)
102
103
104 def test_errorCallbackAddedToExecuteRequest(self):
105 """test that render_error called on execution error"""
106
107 d = self.fb.execute("raise Exception()")
108 d.addCallback(self.checkRenderError)
109
110 def checkRenderError(self, result):
111 assert(self.fb.renderErrorCalled)
112
113 # TODO: add tests for history
114
@@ -852,13 +852,16 b' class ThreadedEngineService(EngineService):'
852
852
853 def __init__(self, shellClass=Interpreter, mpi=None):
853 def __init__(self, shellClass=Interpreter, mpi=None):
854 EngineService.__init__(self, shellClass, mpi)
854 EngineService.__init__(self, shellClass, mpi)
855
856
857 def execute(self, lines):
855 # Only import this if we are going to use this class
858 # Only import this if we are going to use this class
856 from twisted.internet import threads
859 from twisted.internet import threads
857
860
858 def execute(self, lines):
861 msg = {'engineid':self.id,
859 msg = """engine: %r
862 'method':'execute',
860 method: execute(lines)
863 'args':[lines]}
861 lines = %s""" % (self.id, lines)
864
862 d = threads.deferToThread(self.executeAndRaise, msg, self.shell.execute, lines)
865 d = threads.deferToThread(self.shell.execute, lines)
863 d.addCallback(self.addIDToResult)
866 d.addCallback(self.addIDToResult)
864 return d
867 return d
General Comments 0
You need to be logged in to leave comments. Login now