##// END OF EJS Templates
Upstream merges.
gvaroquaux -
r1298:92012389 merge
parent child Browse files
Show More
@@ -298,6 +298,8 b' def runlistpy(self, event):'
298 298 return dirs + pys
299 299
300 300
301 greedy_cd_completer = False
302
301 303 def cd_completer(self, event):
302 304 relpath = event.symbol
303 305 #print event # dbg
@@ -353,7 +355,10 b' def cd_completer(self, event):'
353 355 else:
354 356 return matches
355 357
356 return single_dir_expand(found)
358 if greedy_cd_completer:
359 return single_dir_expand(found)
360 else:
361 return found
357 362
358 363 def apt_get_packages(prefix):
359 364 out = os.popen('apt-cache pkgnames')
@@ -117,6 +117,7 b' def main():'
117 117 # and the next best thing to real 'ls -F'
118 118 ip.defalias('d','dir /w /og /on')
119 119
120 ip.set_hook('input_prefilter', dotslash_prefilter_f)
120 121 extend_shell_behavior(ip)
121 122
122 123 class LastArgFinder:
@@ -138,9 +139,15 b' class LastArgFinder:'
138 139 return parts[-1]
139 140 return ""
140 141
141
142
143
142 def dotslash_prefilter_f(self,line):
143 """ ./foo now runs foo as system command
144
145 Removes the need for doing !./foo
146 """
147 import IPython.genutils
148 if line.startswith("./"):
149 return "_ip.system(" + IPython.genutils.make_quoted_expr(line)+")"
150 raise ipapi.TryNext
144 151
145 152 # XXX You do not need to understand the next function!
146 153 # This should probably be moved out of profile
@@ -92,6 +92,15 b' def main():'
92 92 # at your own risk!
93 93 #import ipy_greedycompleter
94 94
95 # If you are on Linux, you may be annoyed by
96 # "Display all N possibilities? (y or n)" on tab completion,
97 # as well as the paging through "more". Uncomment the following
98 # lines to disable that behaviour
99 #import readline
100 #readline.parse_and_bind('set completion-query-items 1000')
101 #readline.parse_and_bind('set page-completions no')
102
103
95 104
96 105
97 106 # some config helper functions you can use
@@ -1,27 +1,28 b''
1 1 # encoding: utf-8
2 # -*- test-case-name: ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
2 # -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*-
3 3
4 """PyObjC classes to provide a Cocoa frontend to the ipython1.kernel.engineservice.EngineService.
4 """PyObjC classes to provide a Cocoa frontend to the
5 IPython.kernel.engineservice.IEngineBase.
5 6
6 The Cocoa frontend is divided into two classes:
7 - IPythonCocoaController
8 - IPythonCLITextViewDelegate
7 To add an IPython interpreter to a cocoa app, instantiate an
8 IPythonCocoaController in a XIB and connect its textView outlet to an
9 NSTextView instance in your UI. That's it.
9 10
10 To add an IPython interpreter to a cocoa app, instantiate both of these classes in an XIB...[FINISH]
11 Author: Barry Wark
11 12 """
12 13
13 14 __docformat__ = "restructuredtext en"
14 15
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 Barry Wark <barrywark@gmail.com>
16 #-----------------------------------------------------------------------------
17 # Copyright (C) 2008 The IPython Development Team
17 18 #
18 19 # Distributed under the terms of the BSD License. The full license is in
19 20 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
21 22
22 #-------------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
23 24 # Imports
24 #-------------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
25 26
26 27 import objc
27 28 import uuid
@@ -36,18 +37,19 b' from AppKit import NSApplicationWillTerminateNotification, NSBeep,\\'
36 37 from pprint import saferepr
37 38
38 39 import IPython
39 from IPython.kernel.engineservice import EngineService, ThreadedEngineService
40 from IPython.kernel.engineservice import ThreadedEngineService
40 41 from IPython.frontend.frontendbase import FrontEndBase
41 42
42 43 from twisted.internet.threads import blockingCallFromThread
43 44 from twisted.python.failure import Failure
44 45
45 #-------------------------------------------------------------------------------
46 #------------------------------------------------------------------------------
46 47 # Classes to implement the Cocoa frontend
47 #-------------------------------------------------------------------------------
48 #------------------------------------------------------------------------------
48 49
49 50 # TODO:
50 # 1. use MultiEngineClient and out-of-process engine rather than ThreadedEngineService?
51 # 1. use MultiEngineClient and out-of-process engine rather than
52 # ThreadedEngineService?
51 53 # 2. integrate Xgrid launching of engines
52 54
53 55
@@ -75,7 +77,7 b' class IPythonCocoaController(NSObject, FrontEndBase):'
75 77 self.lines = {}
76 78 self.tabSpaces = 4
77 79 self.tabUsesSpaces = True
78 self.currentBlockID = self.nextBlockID()
80 self.currentBlockID = self.next_block_ID()
79 81 self.blockRanges = {} # blockID=>NSRange
80 82
81 83
@@ -89,18 +91,21 b' class IPythonCocoaController(NSObject, FrontEndBase):'
89 91 NSLog('IPython engine started')
90 92
91 93 # Register for app termination
92 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self,
93 'appWillTerminate:',
94 NSApplicationWillTerminateNotification,
95 None)
94 nc = NSNotificationCenter.defaultCenter()
95 nc.addObserver_selector_name_object_(
96 self,
97 'appWillTerminate:',
98 NSApplicationWillTerminateNotification,
99 None)
96 100
97 101 self.textView.setDelegate_(self)
98 102 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
99 self.verticalRulerView = NSRulerView.alloc().initWithScrollView_orientation_(
100 self.textView.enclosingScrollView(),
101 NSVerticalRuler)
103 r = NSRulerView.alloc().initWithScrollView_orientation_(
104 self.textView.enclosingScrollView(),
105 NSVerticalRuler)
106 self.verticalRulerView = r
102 107 self.verticalRulerView.setClientView_(self.textView)
103 self.startCLIForTextView()
108 self._start_cli_banner()
104 109
105 110
106 111 def appWillTerminate_(self, notification):
@@ -118,7 +123,8 b' class IPythonCocoaController(NSObject, FrontEndBase):'
118 123
119 124 Result
120 125 ------
121 Deferred result of ipython1.kernel.engineservice.IEngineInteractive.complete
126 Deferred result of
127 IPython.kernel.engineservice.IEngineBase.complete
122 128 """
123 129
124 130 return self.engine.complete(token)
@@ -128,31 +134,31 b' class IPythonCocoaController(NSObject, FrontEndBase):'
128 134 self.waitingForEngine = True
129 135 self.willChangeValueForKey_('commandHistory')
130 136 d = super(IPythonCocoaController, self).execute(block, blockID)
131 d.addBoth(self._engineDone)
132 d.addCallback(self._updateUserNS)
137 d.addBoth(self._engine_done)
138 d.addCallback(self._update_user_ns)
133 139
134 140 return d
135 141
136 142
137 def _engineDone(self, x):
143 def _engine_done(self, x):
138 144 self.waitingForEngine = False
139 145 self.didChangeValueForKey_('commandHistory')
140 146 return x
141 147
142 def _updateUserNS(self, result):
148 def _update_user_ns(self, result):
143 149 """Update self.userNS from self.engine's namespace"""
144 150 d = self.engine.keys()
145 d.addCallback(self._getEngineNamepsaceValuesForKeys)
151 d.addCallback(self._get_engine_namespace_values_for_keys)
146 152
147 153 return result
148 154
149 155
150 def _getEngineNamepsaceValuesForKeys(self, keys):
156 def _get_engine_namespace_values_for_keys(self, keys):
151 157 d = self.engine.pull(keys)
152 d.addCallback(self._storeEngineNamespaceValues, keys=keys)
158 d.addCallback(self._store_engine_namespace_values, keys=keys)
153 159
154 160
155 def _storeEngineNamespaceValues(self, values, keys=[]):
161 def _store_engine_namespace_values(self, values, keys=[]):
156 162 assert(len(values) == len(keys))
157 163 self.willChangeValueForKey_('userNS')
158 164 for (k,v) in zip(keys,values):
@@ -160,39 +166,170 b' class IPythonCocoaController(NSObject, FrontEndBase):'
160 166 self.didChangeValueForKey_('userNS')
161 167
162 168
163 def startCLIForTextView(self):
169 def update_cell_prompt(self, result):
170 if(isinstance(result, Failure)):
171 blockID = result.blockID
172 else:
173 blockID = result['blockID']
174
175
176 self.insert_text(self.input_prompt(result=result),
177 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
178 scrollToVisible=False
179 )
180
181 return result
182
183
184 def render_result(self, result):
185 blockID = result['blockID']
186 inputRange = self.blockRanges[blockID]
187 del self.blockRanges[blockID]
188
189 #print inputRange,self.current_block_range()
190 self.insert_text('\n' +
191 self.output_prompt(result) +
192 result.get('display',{}).get('pprint','') +
193 '\n\n',
194 textRange=NSMakeRange(inputRange.location+inputRange.length,
195 0))
196 return result
197
198
199 def render_error(self, failure):
200 self.insert_text('\n\n'+str(failure)+'\n\n')
201 self.start_new_block()
202 return failure
203
204
205 def _start_cli_banner(self):
164 206 """Print banner"""
165 207
166 banner = """IPython1 %s -- An enhanced Interactive Python.""" % IPython.__version__
208 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
209 IPython.__version__
167 210
168 211 self.insert_text(banner + '\n\n')
169 212
170 # NSTextView/IPythonTextView delegate methods
213
214 def start_new_block(self):
215 """"""
216
217 self.currentBlockID = self.next_block_ID()
218
219
220
221 def next_block_ID(self):
222
223 return uuid.uuid4()
224
225 def current_block_range(self):
226 return self.blockRanges.get(self.currentBlockID,
227 NSMakeRange(self.textView.textStorage().length(),
228 0))
229
230 def current_block(self):
231 """The current block's text"""
232
233 return self.text_for_range(self.current_block_range())
234
235 def text_for_range(self, textRange):
236 """text_for_range"""
237
238 ts = self.textView.textStorage()
239 return ts.string().substringWithRange_(textRange)
240
241 def current_line(self):
242 block = self.text_for_range(self.current_block_range())
243 block = block.split('\n')
244 return block[-1]
245
246
247 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
248 """Insert text into textView at textRange, updating blockRanges
249 as necessary
250 """
251
252 if(textRange == None):
253 #range for end of text
254 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
255
256 for r in self.blockRanges.itervalues():
257 intersection = NSIntersectionRange(r,textRange)
258 if(intersection.length == 0): #ranges don't intersect
259 if r.location >= textRange.location:
260 r.location += len(string)
261 else: #ranges intersect
262 if(r.location <= textRange.location):
263 assert(intersection.length == textRange.length)
264 r.length += textRange.length
265 else:
266 r.location += intersection.length
267
268 self.textView.replaceCharactersInRange_withString_(
269 textRange, string)
270 self.textView.setSelectedRange_(
271 NSMakeRange(textRange.location+len(string), 0))
272 if(scrollToVisible):
273 self.textView.scrollRangeToVisible_(textRange)
274
275
276
277
278 def replace_current_block_with_string(self, textView, string):
279 textView.replaceCharactersInRange_withString_(
280 self.current_block_range(),
281 string)
282 self.current_block_range().length = len(string)
283 r = NSMakeRange(textView.textStorage().length(), 0)
284 textView.scrollRangeToVisible_(r)
285 textView.setSelectedRange_(r)
286
287
288 def current_indent_string(self):
289 """returns string for indent or None if no indent"""
290
291 if(len(self.current_block()) > 0):
292 lines = self.current_block().split('\n')
293 currentIndent = len(lines[-1]) - len(lines[-1])
294 if(currentIndent == 0):
295 currentIndent = self.tabSpaces
296
297 if(self.tabUsesSpaces):
298 result = ' ' * currentIndent
299 else:
300 result = '\t' * (currentIndent/self.tabSpaces)
301 else:
302 result = None
303
304 return result
305
306
307 # NSTextView delegate methods...
171 308 def textView_doCommandBySelector_(self, textView, selector):
172 309 assert(textView == self.textView)
173 310 NSLog("textView_doCommandBySelector_: "+selector)
174 311
175 312
176 313 if(selector == 'insertNewline:'):
177 indent = self.currentIndentString()
314 indent = self.current_indent_string()
178 315 if(indent):
179 line = indent + self.currentLine()
316 line = indent + self.current_line()
180 317 else:
181 line = self.currentLine()
318 line = self.current_line()
182 319
183 if(self.is_complete(self.currentBlock())):
184 self.execute(self.currentBlock(),
320 if(self.is_complete(self.current_block())):
321 self.execute(self.current_block(),
185 322 blockID=self.currentBlockID)
186 self.startNewBlock()
323 self.start_new_block()
187 324
188 325 return True
189 326
190 327 return False
191 328
192 329 elif(selector == 'moveUp:'):
193 prevBlock = self.get_history_previous(self.currentBlock())
330 prevBlock = self.get_history_previous(self.current_block())
194 331 if(prevBlock != None):
195 self.replaceCurrentBlockWithString(textView, prevBlock)
332 self.replace_current_block_with_string(textView, prevBlock)
196 333 else:
197 334 NSBeep()
198 335 return True
@@ -200,26 +337,30 b' class IPythonCocoaController(NSObject, FrontEndBase):'
200 337 elif(selector == 'moveDown:'):
201 338 nextBlock = self.get_history_next()
202 339 if(nextBlock != None):
203 self.replaceCurrentBlockWithString(textView, nextBlock)
340 self.replace_current_block_with_string(textView, nextBlock)
204 341 else:
205 342 NSBeep()
206 343 return True
207 344
208 345 elif(selector == 'moveToBeginningOfParagraph:'):
209 textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location, 0))
346 textView.setSelectedRange_(NSMakeRange(
347 self.current_block_range().location,
348 0))
210 349 return True
211 350 elif(selector == 'moveToEndOfParagraph:'):
212 textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location + \
213 self.currentBlockRange().length, 0))
351 textView.setSelectedRange_(NSMakeRange(
352 self.current_block_range().location + \
353 self.current_block_range().length, 0))
214 354 return True
215 355 elif(selector == 'deleteToEndOfParagraph:'):
216 if(textView.selectedRange().location <= self.currentBlockRange().location):
356 if(textView.selectedRange().location <= \
357 self.current_block_range().location):
217 358 # Intersect the selected range with the current line range
218 if(self.currentBlockRange().length < 0):
359 if(self.current_block_range().length < 0):
219 360 self.blockRanges[self.currentBlockID].length = 0
220 361
221 362 r = NSIntersectionRange(textView.rangesForUserTextChange()[0],
222 self.currentBlockRange())
363 self.current_block_range())
223 364
224 365 if(r.length > 0): #no intersection
225 366 textView.setSelectedRange_(r)
@@ -227,7 +368,7 b' class IPythonCocoaController(NSObject, FrontEndBase):'
227 368 return False # don't actually handle the delete
228 369
229 370 elif(selector == 'insertTab:'):
230 if(len(self.currentLine().strip()) == 0): #only white space
371 if(len(self.current_line().strip()) == 0): #only white space
231 372 return False
232 373 else:
233 374 self.textView.complete_(self)
@@ -235,39 +376,45 b' class IPythonCocoaController(NSObject, FrontEndBase):'
235 376
236 377 elif(selector == 'deleteBackward:'):
237 378 #if we're at the beginning of the current block, ignore
238 if(textView.selectedRange().location == self.currentBlockRange().location):
379 if(textView.selectedRange().location == \
380 self.current_block_range().location):
239 381 return True
240 382 else:
241 self.currentBlockRange().length-=1
383 self.current_block_range().length-=1
242 384 return False
243 385 return False
244 386
245 387
246 def textView_shouldChangeTextInRanges_replacementStrings_(self, textView, ranges, replacementStrings):
388 def textView_shouldChangeTextInRanges_replacementStrings_(self,
389 textView, ranges, replacementStrings):
247 390 """
248 391 Delegate method for NSTextView.
249 392
250 Refuse change text in ranges not at end, but make those changes at end.
393 Refuse change text in ranges not at end, but make those changes at
394 end.
251 395 """
252 396
253 #print 'textView_shouldChangeTextInRanges_replacementStrings_:',ranges,replacementStrings
254 397 assert(len(ranges) == len(replacementStrings))
255 398 allow = True
256 399 for r,s in zip(ranges, replacementStrings):
257 400 r = r.rangeValue()
258 401 if(textView.textStorage().length() > 0 and
259 r.location < self.currentBlockRange().location):
402 r.location < self.current_block_range().location):
260 403 self.insert_text(s)
261 404 allow = False
262 405
263 406
264 self.blockRanges.setdefault(self.currentBlockID, self.currentBlockRange()).length += len(s)
407 self.blockRanges.setdefault(self.currentBlockID,
408 self.current_block_range()).length +=\
409 len(s)
265 410
266 411 return allow
267 412
268 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, textView, words, charRange, index):
413 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
414 textView, words, charRange, index):
269 415 try:
270 token = textView.textStorage().string().substringWithRange_(charRange)
416 ts = textView.textStorage()
417 token = ts.string().substringWithRange_(charRange)
271 418 completions = blockingCallFromThread(self.complete, token)
272 419 except:
273 420 completions = objc.nil
@@ -275,121 +422,4 b' class IPythonCocoaController(NSObject, FrontEndBase):'
275 422
276 423 return (completions,0)
277 424
278
279 def startNewBlock(self):
280 """"""
281
282 self.currentBlockID = self.nextBlockID()
283
284
285
286 def nextBlockID(self):
287
288 return uuid.uuid4()
289
290 def currentBlockRange(self):
291 return self.blockRanges.get(self.currentBlockID, NSMakeRange(self.textView.textStorage().length(), 0))
292
293 def currentBlock(self):
294 """The current block's text"""
295
296 return self.textForRange(self.currentBlockRange())
297
298 def textForRange(self, textRange):
299 """textForRange"""
300
301 return self.textView.textStorage().string().substringWithRange_(textRange)
302
303 def currentLine(self):
304 block = self.textForRange(self.currentBlockRange())
305 block = block.split('\n')
306 return block[-1]
307
308 def update_cell_prompt(self, result):
309 if(isinstance(result, Failure)):
310 blockID = result.blockID
311 else:
312 blockID = result['blockID']
313
314
315 self.insert_text(self.input_prompt(result=result),
316 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
317 scrollToVisible=False
318 )
319
320 return result
321
322
323 def render_result(self, result):
324 blockID = result['blockID']
325 inputRange = self.blockRanges[blockID]
326 del self.blockRanges[blockID]
327
328 #print inputRange,self.currentBlockRange()
329 self.insert_text('\n' +
330 self.output_prompt(result) +
331 result.get('display',{}).get('pprint','') +
332 '\n\n',
333 textRange=NSMakeRange(inputRange.location+inputRange.length, 0))
334 return result
335
336
337 def render_error(self, failure):
338 self.insert_text('\n\n'+str(failure)+'\n\n')
339 self.startNewBlock()
340 return failure
341
342
343 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
344 """Insert text into textView at textRange, updating blockRanges as necessary"""
345
346 if(textRange == None):
347 textRange = NSMakeRange(self.textView.textStorage().length(), 0) #range for end of text
348
349 for r in self.blockRanges.itervalues():
350 intersection = NSIntersectionRange(r,textRange)
351 if(intersection.length == 0): #ranges don't intersect
352 if r.location >= textRange.location:
353 r.location += len(string)
354 else: #ranges intersect
355 if(r.location <= textRange.location):
356 assert(intersection.length == textRange.length)
357 r.length += textRange.length
358 else:
359 r.location += intersection.length
360
361 self.textView.replaceCharactersInRange_withString_(textRange, string) #textStorage().string()
362 self.textView.setSelectedRange_(NSMakeRange(textRange.location+len(string), 0))
363 if(scrollToVisible):
364 self.textView.scrollRangeToVisible_(textRange)
365
366
367
368 def replaceCurrentBlockWithString(self, textView, string):
369 textView.replaceCharactersInRange_withString_(self.currentBlockRange(),
370 string)
371 self.currentBlockRange().length = len(string)
372 r = NSMakeRange(textView.textStorage().length(), 0)
373 textView.scrollRangeToVisible_(r)
374 textView.setSelectedRange_(r)
375
376
377 def currentIndentString(self):
378 """returns string for indent or None if no indent"""
379
380 if(len(self.currentBlock()) > 0):
381 lines = self.currentBlock().split('\n')
382 currentIndent = len(lines[-1]) - len(lines[-1])
383 if(currentIndent == 0):
384 currentIndent = self.tabSpaces
385
386 if(self.tabUsesSpaces):
387 result = ' ' * currentIndent
388 else:
389 result = '\t' * (currentIndent/self.tabSpaces)
390 else:
391 result = None
392
393 return result
394
395 425
@@ -1,27 +1,20 b''
1 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 mirrors engine's user_ns
2 """This file contains unittests for the
3 IPython.frontend.cocoa.cocoa_frontend module.
9 4 """
10 5 __docformat__ = "restructuredtext en"
11
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2005 Fernando Perez <fperez@colorado.edu>
14 # Brian E Granger <ellisonbg@gmail.com>
15 # Benjamin Ragan-Kelley <benjaminrk@gmail.com>
16 #
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
20
21 #-------------------------------------------------------------------------------
22 # Imports
23 #-------------------------------------------------------------------------------
24 from IPython.kernel.core.interpreter import Interpreter
6
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2005 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 from IPython.kernel.core.interpreter import Interpreter
25 18 import IPython.kernel.engineservice as es
26 19 from IPython.testing.util import DeferredTestCase
27 20 from twisted.internet.defer import succeed
@@ -51,7 +44,9 b' class TestIPythonCocoaControler(DeferredTestCase):'
51 44 del result['number']
52 45 del result['id']
53 46 return result
54 self.assertDeferredEquals(self.controller.execute(code).addCallback(removeNumberAndID), expected)
47 self.assertDeferredEquals(
48 self.controller.execute(code).addCallback(removeNumberAndID),
49 expected)
55 50
56 51 def testControllerMirrorsUserNSWithValuesAsStrings(self):
57 52 code = """userns1=1;userns2=2"""
@@ -1,6 +1,8 b''
1 1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
2 3 """
3 frontendbase provides an interface and base class for GUI frontends for IPython.kernel/IPython.kernel.core.
4 frontendbase provides an interface and base class for GUI frontends for
5 IPython.kernel/IPython.kernel.core.
4 6
5 7 Frontend implementations will likely want to subclass FrontEndBase.
6 8
@@ -57,20 +59,34 b' class IFrontEndFactory(zi.Interface):'
57 59 class IFrontEnd(zi.Interface):
58 60 """Interface for frontends. All methods return t.i.d.Deferred"""
59 61
60 zi.Attribute("input_prompt_template", "string.Template instance substituteable with execute result.")
61 zi.Attribute("output_prompt_template", "string.Template instance substituteable with execute result.")
62 zi.Attribute("continuation_prompt_template", "string.Template instance substituteable with execute result.")
62 zi.Attribute("input_prompt_template", "string.Template instance\
63 substituteable with execute result.")
64 zi.Attribute("output_prompt_template", "string.Template instance\
65 substituteable with execute result.")
66 zi.Attribute("continuation_prompt_template", "string.Template instance\
67 substituteable with execute result.")
63 68
64 69 def update_cell_prompt(self, result):
65 70 """Subclass may override to update the input prompt for a block.
66 Since this method will be called as a twisted.internet.defer.Deferred's callback,
67 implementations should return result when finished."""
71 Since this method will be called as a
72 twisted.internet.defer.Deferred's callback,
73 implementations should return result when finished.
74
75 NB: result is a failure if the execute returned a failre.
76 To get the blockID, you should do something like::
77 if(isinstance(result, twisted.python.failure.Failure)):
78 blockID = result.blockID
79 else:
80 blockID = result['blockID']
81 """
68 82
69 83 pass
70 84
71 85 def render_result(self, result):
72 """Render the result of an execute call. Implementors may choose the method of rendering.
73 For example, a notebook-style frontend might render a Chaco plot inline.
86 """Render the result of an execute call. Implementors may choose the
87 method of rendering.
88 For example, a notebook-style frontend might render a Chaco plot
89 inline.
74 90
75 91 Parameters:
76 92 result : dict (result of IEngineBase.execute )
@@ -82,24 +98,31 b' class IFrontEnd(zi.Interface):'
82 98 pass
83 99
84 100 def render_error(self, failure):
85 """Subclasses must override to render the failure. Since this method will be called as a
86 twisted.internet.defer.Deferred's callback, implementations should return result
87 when finished."""
101 """Subclasses must override to render the failure. Since this method
102 ill be called as a twisted.internet.defer.Deferred's callback,
103 implementations should return result when finished.
104 """
88 105
89 106 pass
90 107
91 108
92 109 def input_prompt(result={}):
93 """Returns the input prompt by subsituting into self.input_prompt_template"""
110 """Returns the input prompt by subsituting into
111 self.input_prompt_template
112 """
94 113 pass
95 114
96 115 def output_prompt(result):
97 """Returns the output prompt by subsituting into self.output_prompt_template"""
116 """Returns the output prompt by subsituting into
117 self.output_prompt_template
118 """
98 119
99 120 pass
100 121
101 122 def continuation_prompt():
102 """Returns the continuation prompt by subsituting into self.continuation_prompt_template"""
123 """Returns the continuation prompt by subsituting into
124 self.continuation_prompt_template
125 """
103 126
104 127 pass
105 128
@@ -221,7 +244,8 b' class FrontEndBase(object):'
221 244 Parameters:
222 245 block : {str, AST}
223 246 blockID : any
224 Caller may provide an ID to identify this block. result['blockID'] := blockID
247 Caller may provide an ID to identify this block.
248 result['blockID'] := blockID
225 249
226 250 Result:
227 251 Deferred result of self.interpreter.execute
@@ -243,8 +267,8 b' class FrontEndBase(object):'
243 267
244 268
245 269 def _add_block_id(self, result, blockID):
246 """Add the blockID to result or failure. Unfortunatley, we have to treat failures
247 differently than result dicts
270 """Add the blockID to result or failure. Unfortunatley, we have to
271 treat failures differently than result dicts.
248 272 """
249 273
250 274 if(isinstance(result, Failure)):
@@ -291,11 +315,12 b' class FrontEndBase(object):'
291 315
292 316 def update_cell_prompt(self, result):
293 317 """Subclass may override to update the input prompt for a block.
294 Since this method will be called as a twisted.internet.defer.Deferred's callback,
295 implementations should return result when finished.
318 Since this method will be called as a
319 twisted.internet.defer.Deferred's callback, implementations should
320 return result when finished.
296 321
297 NP: result is a failure if the execute returned a failre. To get the blockID, you should
298 do something like::
322 NB: result is a failure if the execute returned a failre.
323 To get the blockID, you should do something like::
299 324 if(isinstance(result, twisted.python.failure.Failure)):
300 325 blockID = result.blockID
301 326 else:
@@ -308,17 +333,18 b' class FrontEndBase(object):'
308 333
309 334
310 335 def render_result(self, result):
311 """Subclasses must override to render result. Since this method will be called as a
312 twisted.internet.defer.Deferred's callback, implementations should return result
313 when finished."""
336 """Subclasses must override to render result. Since this method will
337 be called as a twisted.internet.defer.Deferred's callback,
338 implementations should return result when finished.
339 """
314 340
315 341 return result
316 342
317 343
318 344 def render_error(self, failure):
319 """Subclasses must override to render the failure. Since this method will be called as a
320 twisted.internet.defer.Deferred's callback, implementations should return result
321 when finished."""
345 """Subclasses must override to render the failure. Since this method
346 will be called as a twisted.internet.defer.Deferred's callback,
347 implementations should return result when finished."""
322 348
323 349 return failure
324 350
@@ -4,16 +4,16 b''
4 4
5 5 __docformat__ = "restructuredtext en"
6 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 #-------------------------------------------------------------------------------
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 17
18 18 import unittest
19 19 from IPython.frontend import frontendbase
@@ -22,7 +22,8 b' from IPython.kernel.engineservice import EngineService'
22 22 class FrontEndCallbackChecker(frontendbase.FrontEndBase):
23 23 """FrontEndBase subclass for checking callbacks"""
24 24 def __init__(self, engine=None, history=None):
25 super(FrontEndCallbackChecker, self).__init__(engine=engine, history=history)
25 super(FrontEndCallbackChecker, self).__init__(engine=engine,
26 history=history)
26 27 self.updateCalled = False
27 28 self.renderResultCalled = False
28 29 self.renderErrorCalled = False
@@ -51,7 +52,8 b' class TestFrontendBase(unittest.TestCase):'
51 52
52 53
53 54 def test_implements_IFrontEnd(self):
54 assert(frontendbase.IFrontEnd.implementedBy(frontendbase.FrontEndBase))
55 assert(frontendbase.IFrontEnd.implementedBy(
56 frontendbase.FrontEndBase))
55 57
56 58
57 59 def test_is_complete_returns_False_for_incomplete_block(self):
@@ -27,9 +27,11 b' try:'
27 27 except ImportError:
28 28 pass
29 29 import os
30 import platform
30 31 import re
31 32 import shlex
32 33 import shutil
34 import subprocess
33 35 import sys
34 36 import tempfile
35 37 import time
@@ -2041,6 +2043,55 b" def wrap_deprecated(func, suggest = '<nothing>'):"
2041 2043 stacklevel = 2)
2042 2044 return func(*args, **kwargs)
2043 2045 return newFunc
2044
2045 #*************************** end of file <genutils.py> **********************
2046 2046
2047
2048 def _num_cpus_unix():
2049 """Return the number of active CPUs on a Unix system."""
2050 return os.sysconf("SC_NPROCESSORS_ONLN")
2051
2052
2053 def _num_cpus_darwin():
2054 """Return the number of active CPUs on a Darwin system."""
2055 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
2056 return p.stdout.read()
2057
2058
2059 def _num_cpus_windows():
2060 """Return the number of active CPUs on a Windows system."""
2061 return os.environ.get("NUMBER_OF_PROCESSORS")
2062
2063
2064 def num_cpus():
2065 """Return the effective number of CPUs in the system as an integer.
2066
2067 This cross-platform function makes an attempt at finding the total number of
2068 available CPUs in the system, as returned by various underlying system and
2069 python calls.
2070
2071 If it can't find a sensible answer, it returns 1 (though an error *may* make
2072 it return a large positive number that's actually incorrect).
2073 """
2074
2075 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
2076 # for the names of the keys we needed to look up for this function. This
2077 # code was inspired by their equivalent function.
2078
2079 ncpufuncs = {'Linux':_num_cpus_unix,
2080 'Darwin':_num_cpus_darwin,
2081 'Windows':_num_cpus_windows,
2082 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
2083 # See http://bugs.python.org/issue1082 for details.
2084 'Microsoft':_num_cpus_windows,
2085 }
2086
2087 ncpufunc = ncpufuncs.get(platform.system(),
2088 # default to unix version (Solaris, AIX, etc)
2089 _num_cpus_unix)
2090
2091 try:
2092 ncpus = max(1,int(ncpufunc()))
2093 except:
2094 ncpus = 1
2095 return ncpus
2096
2097 #*************************** end of file <genutils.py> **********************
@@ -55,24 +55,32 b' class Struct:'
55 55 Define a dictionary and initialize both with dict and k=v pairs:
56 56 >>> d={'a':1,'b':2}
57 57 >>> s=Struct(d,hi=10,ho=20)
58
58 59 The return of __repr__ can be used to create a new instance:
59 60 >>> s
60 Struct({'ho': 20, 'b': 2, 'hi': 10, 'a': 1})
61 Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20})
62
63 Note: the special '__allownew' key is used for internal purposes.
64
61 65 __str__ (called by print) shows it's not quite a regular dictionary:
62 66 >>> print s
63 Struct {a: 1, b: 2, hi: 10, ho: 20}
67 Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20})
68
64 69 Access by explicitly named key with dot notation:
65 70 >>> s.a
66 71 1
72
67 73 Or like a dictionary:
68 74 >>> s['a']
69 75 1
76
70 77 If you want a variable to hold the key value, only dictionary access works:
71 78 >>> key='hi'
72 79 >>> s.key
73 80 Traceback (most recent call last):
74 81 File "<stdin>", line 1, in ?
75 82 AttributeError: Struct instance has no attribute 'key'
83
76 84 >>> s[key]
77 85 10
78 86
@@ -81,13 +89,16 b' class Struct:'
81 89 accessed using the dictionary syntax. Again, an example:
82 90
83 91 This doesn't work:
84 >>> s=Struct(4='hi')
92 >>> s=Struct(4='hi') #doctest: +IGNORE_EXCEPTION_DETAIL
93 Traceback (most recent call last):
94 ...
85 95 SyntaxError: keyword can't be an expression
96
86 97 But this does:
87 98 >>> s=Struct()
88 99 >>> s[4]='hi'
89 100 >>> s
90 Struct({4: 'hi'})
101 Struct({4: 'hi', '__allownew': True})
91 102 >>> s[4]
92 103 'hi'
93 104 """
@@ -318,7 +329,8 b' class Struct:'
318 329 if __conflict_solve:
319 330 inv_conflict_solve_user = __conflict_solve.copy()
320 331 for name, func in [('preserve',preserve), ('update',update),
321 ('add',add), ('add_flip',add_flip), ('add_s',add_s)]:
332 ('add',add), ('add_flip',add_flip),
333 ('add_s',add_s)]:
322 334 if name in inv_conflict_solve_user.keys():
323 335 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
324 336 del inv_conflict_solve_user[name]
@@ -369,14 +381,14 b' class Struct:'
369 381 return ret
370 382
371 383 def get(self,attr,val=None):
372 """S.get(k[,d]) -> S[k] if S.has_key(k), else d. d defaults to None."""
384 """S.get(k[,d]) -> S[k] if k in S, else d. d defaults to None."""
373 385 try:
374 386 return self[attr]
375 387 except KeyError:
376 388 return val
377 389
378 390 def setdefault(self,attr,val=None):
379 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if not S.has_key(k)"""
391 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if k not in S"""
380 392 if not self.has_key(attr):
381 393 self[attr] = val
382 394 return self.get(attr,val)
@@ -384,8 +396,8 b' class Struct:'
384 396 def allow_new_attr(self, allow = True):
385 397 """ Set whether new attributes can be created inside struct
386 398
387 This can be used to catch typos by verifying that the attribute user tries to
388 change already exists in this Struct.
399 This can be used to catch typos by verifying that the attribute user
400 tries to change already exists in this Struct.
389 401 """
390 402 self['__allownew'] = allow
391 403
@@ -847,11 +847,13 b' class Command(object):'
847 847 self.deferred.errback(reason)
848 848
849 849 class ThreadedEngineService(EngineService):
850 """An EngineService subclass that defers execute commands to a separate thread.
850 """An EngineService subclass that defers execute commands to a separate
851 thread.
851 852
852 ThreadedEngineService uses twisted.internet.threads.deferToThread to defer execute
853 requests to a separate thread. GUI frontends may want to use ThreadedEngineService as
854 the engine in an IPython.frontend.frontendbase.FrontEndBase subclass to prevent
853 ThreadedEngineService uses twisted.internet.threads.deferToThread to
854 defer execute requests to a separate thread. GUI frontends may want to
855 use ThreadedEngineService as the engine in an
856 IPython.frontend.frontendbase.FrontEndBase subclass to prevent
855 857 block execution from blocking the GUI thread.
856 858 """
857 859
1 NO CONTENT: file renamed from docs/ChangeLog to docs/attic/ChangeLog
@@ -29,6 +29,7 b' New features'
29 29 Development Team" as the copyright holder. We give more details about exactly
30 30 what this means in this file. All developer should read this and use the new
31 31 banner in all IPython source code files.
32 * sh profile: ./foo runs foo as system command, no need to do !./foo anymore
32 33
33 34 Bug fixes
34 35 ---------
@@ -5,22 +5,6 b' IPython development guidelines'
5 5 ==================================
6 6
7 7 .. contents::
8 ..
9 1 Overview
10 2 Project organization
11 2.1 Subpackages
12 2.2 Installation and dependencies
13 2.3 Specific subpackages
14 3 Version control
15 4 Documentation
16 4.1 Standalone documentation
17 4.2 Docstring format
18 5 Coding conventions
19 5.1 General
20 5.2 Naming conventions
21 6 Testing
22 7 Configuration
23 ..
24 8
25 9
26 10 Overview
@@ -136,11 +120,72 b' Specific subpackages'
136 120 Version control
137 121 ===============
138 122
139 In the past, IPython development has been done using `Subversion`__. We are currently trying out `Bazaar`__ and `Launchpad`__.
123 In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people
124 to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython
125 development. First, you should install Bazaar. After you have done that, make
126 sure that it is working by getting the latest main branch of IPython::
127
128 $ bzr branch lp:ipython
129
130 Now you can create a new branch for you to do your work in::
131
132 $ bzr branch ipython ipython-mybranch
133
134 The typical work cycle in this branch will be to make changes in `ipython-mybranch`
135 and then commit those changes using the commit command::
136
137 $ ...do work in ipython-mybranch...
138 $ bzr ci -m "the commit message goes here"
139
140 Please note that since we now don't use an old-style linear ChangeLog
141 (that tends to cause problems with distributed version control
142 systems), you should ensure that your log messages are reasonably
143 detailed. Use a docstring-like approach in the commit messages
144 (including the second line being left *blank*)::
145
146 Single line summary of changes being committed.
147
148 - more details when warranted ...
149 - including crediting outside contributors if they sent the
150 code/bug/idea!
151
152 If we couple this with a policy of making single commits for each
153 reasonably atomic change, the bzr log should give an excellent view of
154 the project, and the `--short` log option becomes a nice summary.
155
156 While working with this branch, it is a good idea to merge in changes that have been
157 made upstream in the parent branch. This can be done by doing::
158
159 $ bzr pull
160
161 If this command shows that the branches have diverged, then you should do a merge
162 instead::
163
164 $ bzr merge lp:ipython
165
166 If you want others to be able to see your branch, you can create an account with
167 launchpad and push the branch to your own workspace::
168
169 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
170
171 Finally, once the work in your branch is done, you can merge your changes back into
172 the `ipython` branch by using merge::
173
174 $ cd ipython
175 $ merge ../ipython-mybranch
176 [resolve any conflicts]
177 $ bzr ci -m "Fixing that bug"
178 $ bzr push
179
180 But this will require you to have write permissions to the `ipython` branch. It you don't
181 you can tell one of the IPython devs about your branch and they can do the merge for you.
182
183 More information about Bazaar workflows can be found `here`__.
140 184
141 185 .. __: http://subversion.tigris.org/
142 186 .. __: http://bazaar-vcs.org/
143 187 .. __: http://www.launchpad.net/ipython
188 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
144 189
145 190 Documentation
146 191 =============
@@ -160,7 +160,9 b' def find_data_files():'
160 160 ('data', manpagebase, manpages),
161 161 ('data',pjoin(docdirbase, 'extensions'),igridhelpfiles),
162 162 ]
163 return data_files
163 # import pprint
164 # pprint.pprint(data_files)
165 return []
164 166
165 167 #---------------------------------------------------------------------------
166 168 # Find scripts
General Comments 0
You need to be logged in to leave comments. Login now