From 92012389d0aa8e17ee6b2ff4bedce7fd58ed729f 2008-06-24 20:09:18 From: gvaroquaux Date: 2008-06-24 20:09:18 Subject: [PATCH] Upstream merges. --- diff --git a/IPython/Extensions/ipy_completers.py b/IPython/Extensions/ipy_completers.py index 238a101..27139d3 100644 --- a/IPython/Extensions/ipy_completers.py +++ b/IPython/Extensions/ipy_completers.py @@ -298,6 +298,8 @@ def runlistpy(self, event): return dirs + pys +greedy_cd_completer = False + def cd_completer(self, event): relpath = event.symbol #print event # dbg @@ -353,7 +355,10 @@ def cd_completer(self, event): else: return matches - return single_dir_expand(found) + if greedy_cd_completer: + return single_dir_expand(found) + else: + return found def apt_get_packages(prefix): out = os.popen('apt-cache pkgnames') diff --git a/IPython/Extensions/ipy_profile_sh.py b/IPython/Extensions/ipy_profile_sh.py index c25afef..ef4ae8e 100644 --- a/IPython/Extensions/ipy_profile_sh.py +++ b/IPython/Extensions/ipy_profile_sh.py @@ -117,6 +117,7 @@ def main(): # and the next best thing to real 'ls -F' ip.defalias('d','dir /w /og /on') + ip.set_hook('input_prefilter', dotslash_prefilter_f) extend_shell_behavior(ip) class LastArgFinder: @@ -138,9 +139,15 @@ class LastArgFinder: return parts[-1] return "" - - - +def dotslash_prefilter_f(self,line): + """ ./foo now runs foo as system command + + Removes the need for doing !./foo + """ + import IPython.genutils + if line.startswith("./"): + return "_ip.system(" + IPython.genutils.make_quoted_expr(line)+")" + raise ipapi.TryNext # XXX You do not need to understand the next function! # This should probably be moved out of profile diff --git a/IPython/UserConfig/ipy_user_conf.py b/IPython/UserConfig/ipy_user_conf.py index 2500a18..b0184ef 100644 --- a/IPython/UserConfig/ipy_user_conf.py +++ b/IPython/UserConfig/ipy_user_conf.py @@ -92,6 +92,15 @@ def main(): # at your own risk! #import ipy_greedycompleter + # If you are on Linux, you may be annoyed by + # "Display all N possibilities? (y or n)" on tab completion, + # as well as the paging through "more". Uncomment the following + # lines to disable that behaviour + #import readline + #readline.parse_and_bind('set completion-query-items 1000') + #readline.parse_and_bind('set page-completions no') + + # some config helper functions you can use diff --git a/IPython/frontend/cocoa/cocoa_frontend.py b/IPython/frontend/cocoa/cocoa_frontend.py index 77df8ba..9f9aa55 100644 --- a/IPython/frontend/cocoa/cocoa_frontend.py +++ b/IPython/frontend/cocoa/cocoa_frontend.py @@ -1,27 +1,28 @@ # encoding: utf-8 -# -*- test-case-name: ipython1.frontend.cocoa.tests.test_cocoa_frontend -*- +# -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*- -"""PyObjC classes to provide a Cocoa frontend to the ipython1.kernel.engineservice.EngineService. +"""PyObjC classes to provide a Cocoa frontend to the +IPython.kernel.engineservice.IEngineBase. -The Cocoa frontend is divided into two classes: - - IPythonCocoaController - - IPythonCLITextViewDelegate +To add an IPython interpreter to a cocoa app, instantiate an +IPythonCocoaController in a XIB and connect its textView outlet to an +NSTextView instance in your UI. That's it. -To add an IPython interpreter to a cocoa app, instantiate both of these classes in an XIB...[FINISH] +Author: Barry Wark """ __docformat__ = "restructuredtext en" -#------------------------------------------------------------------------------- -# Copyright (C) 2008 Barry Wark +#----------------------------------------------------------------------------- +# Copyright (C) 2008 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. -#------------------------------------------------------------------------------- +#----------------------------------------------------------------------------- -#------------------------------------------------------------------------------- +#----------------------------------------------------------------------------- # Imports -#------------------------------------------------------------------------------- +#----------------------------------------------------------------------------- import objc import uuid @@ -36,18 +37,19 @@ from AppKit import NSApplicationWillTerminateNotification, NSBeep,\ from pprint import saferepr import IPython -from IPython.kernel.engineservice import EngineService, ThreadedEngineService +from IPython.kernel.engineservice import ThreadedEngineService from IPython.frontend.frontendbase import FrontEndBase from twisted.internet.threads import blockingCallFromThread from twisted.python.failure import Failure -#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------ # Classes to implement the Cocoa frontend -#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------ # TODO: -# 1. use MultiEngineClient and out-of-process engine rather than ThreadedEngineService? +# 1. use MultiEngineClient and out-of-process engine rather than +# ThreadedEngineService? # 2. integrate Xgrid launching of engines @@ -75,7 +77,7 @@ class IPythonCocoaController(NSObject, FrontEndBase): self.lines = {} self.tabSpaces = 4 self.tabUsesSpaces = True - self.currentBlockID = self.nextBlockID() + self.currentBlockID = self.next_block_ID() self.blockRanges = {} # blockID=>NSRange @@ -89,18 +91,21 @@ class IPythonCocoaController(NSObject, FrontEndBase): NSLog('IPython engine started') # Register for app termination - NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, - 'appWillTerminate:', - NSApplicationWillTerminateNotification, - None) + nc = NSNotificationCenter.defaultCenter() + nc.addObserver_selector_name_object_( + self, + 'appWillTerminate:', + NSApplicationWillTerminateNotification, + None) self.textView.setDelegate_(self) self.textView.enclosingScrollView().setHasVerticalRuler_(True) - self.verticalRulerView = NSRulerView.alloc().initWithScrollView_orientation_( - self.textView.enclosingScrollView(), - NSVerticalRuler) + r = NSRulerView.alloc().initWithScrollView_orientation_( + self.textView.enclosingScrollView(), + NSVerticalRuler) + self.verticalRulerView = r self.verticalRulerView.setClientView_(self.textView) - self.startCLIForTextView() + self._start_cli_banner() def appWillTerminate_(self, notification): @@ -118,7 +123,8 @@ class IPythonCocoaController(NSObject, FrontEndBase): Result ------ - Deferred result of ipython1.kernel.engineservice.IEngineInteractive.complete + Deferred result of + IPython.kernel.engineservice.IEngineBase.complete """ return self.engine.complete(token) @@ -128,31 +134,31 @@ class IPythonCocoaController(NSObject, FrontEndBase): self.waitingForEngine = True self.willChangeValueForKey_('commandHistory') d = super(IPythonCocoaController, self).execute(block, blockID) - d.addBoth(self._engineDone) - d.addCallback(self._updateUserNS) + d.addBoth(self._engine_done) + d.addCallback(self._update_user_ns) return d - def _engineDone(self, x): + def _engine_done(self, x): self.waitingForEngine = False self.didChangeValueForKey_('commandHistory') return x - def _updateUserNS(self, result): + def _update_user_ns(self, result): """Update self.userNS from self.engine's namespace""" d = self.engine.keys() - d.addCallback(self._getEngineNamepsaceValuesForKeys) + d.addCallback(self._get_engine_namespace_values_for_keys) return result - def _getEngineNamepsaceValuesForKeys(self, keys): + def _get_engine_namespace_values_for_keys(self, keys): d = self.engine.pull(keys) - d.addCallback(self._storeEngineNamespaceValues, keys=keys) + d.addCallback(self._store_engine_namespace_values, keys=keys) - def _storeEngineNamespaceValues(self, values, keys=[]): + def _store_engine_namespace_values(self, values, keys=[]): assert(len(values) == len(keys)) self.willChangeValueForKey_('userNS') for (k,v) in zip(keys,values): @@ -160,39 +166,170 @@ class IPythonCocoaController(NSObject, FrontEndBase): self.didChangeValueForKey_('userNS') - def startCLIForTextView(self): + def update_cell_prompt(self, result): + if(isinstance(result, Failure)): + blockID = result.blockID + else: + blockID = result['blockID'] + + + self.insert_text(self.input_prompt(result=result), + textRange=NSMakeRange(self.blockRanges[blockID].location,0), + scrollToVisible=False + ) + + return result + + + def render_result(self, result): + blockID = result['blockID'] + inputRange = self.blockRanges[blockID] + del self.blockRanges[blockID] + + #print inputRange,self.current_block_range() + self.insert_text('\n' + + self.output_prompt(result) + + result.get('display',{}).get('pprint','') + + '\n\n', + textRange=NSMakeRange(inputRange.location+inputRange.length, + 0)) + return result + + + def render_error(self, failure): + self.insert_text('\n\n'+str(failure)+'\n\n') + self.start_new_block() + return failure + + + def _start_cli_banner(self): """Print banner""" - banner = """IPython1 %s -- An enhanced Interactive Python.""" % IPython.__version__ + banner = """IPython1 %s -- An enhanced Interactive Python.""" % \ + IPython.__version__ self.insert_text(banner + '\n\n') - # NSTextView/IPythonTextView delegate methods + + def start_new_block(self): + """""" + + self.currentBlockID = self.next_block_ID() + + + + def next_block_ID(self): + + return uuid.uuid4() + + def current_block_range(self): + return self.blockRanges.get(self.currentBlockID, + NSMakeRange(self.textView.textStorage().length(), + 0)) + + def current_block(self): + """The current block's text""" + + return self.text_for_range(self.current_block_range()) + + def text_for_range(self, textRange): + """text_for_range""" + + ts = self.textView.textStorage() + return ts.string().substringWithRange_(textRange) + + def current_line(self): + block = self.text_for_range(self.current_block_range()) + block = block.split('\n') + return block[-1] + + + def insert_text(self, string=None, textRange=None, scrollToVisible=True): + """Insert text into textView at textRange, updating blockRanges + as necessary + """ + + if(textRange == None): + #range for end of text + textRange = NSMakeRange(self.textView.textStorage().length(), 0) + + for r in self.blockRanges.itervalues(): + intersection = NSIntersectionRange(r,textRange) + if(intersection.length == 0): #ranges don't intersect + if r.location >= textRange.location: + r.location += len(string) + else: #ranges intersect + if(r.location <= textRange.location): + assert(intersection.length == textRange.length) + r.length += textRange.length + else: + r.location += intersection.length + + self.textView.replaceCharactersInRange_withString_( + textRange, string) + self.textView.setSelectedRange_( + NSMakeRange(textRange.location+len(string), 0)) + if(scrollToVisible): + self.textView.scrollRangeToVisible_(textRange) + + + + + def replace_current_block_with_string(self, textView, string): + textView.replaceCharactersInRange_withString_( + self.current_block_range(), + string) + self.current_block_range().length = len(string) + r = NSMakeRange(textView.textStorage().length(), 0) + textView.scrollRangeToVisible_(r) + textView.setSelectedRange_(r) + + + def current_indent_string(self): + """returns string for indent or None if no indent""" + + if(len(self.current_block()) > 0): + lines = self.current_block().split('\n') + currentIndent = len(lines[-1]) - len(lines[-1]) + if(currentIndent == 0): + currentIndent = self.tabSpaces + + if(self.tabUsesSpaces): + result = ' ' * currentIndent + else: + result = '\t' * (currentIndent/self.tabSpaces) + else: + result = None + + return result + + + # NSTextView delegate methods... def textView_doCommandBySelector_(self, textView, selector): assert(textView == self.textView) NSLog("textView_doCommandBySelector_: "+selector) if(selector == 'insertNewline:'): - indent = self.currentIndentString() + indent = self.current_indent_string() if(indent): - line = indent + self.currentLine() + line = indent + self.current_line() else: - line = self.currentLine() + line = self.current_line() - if(self.is_complete(self.currentBlock())): - self.execute(self.currentBlock(), + if(self.is_complete(self.current_block())): + self.execute(self.current_block(), blockID=self.currentBlockID) - self.startNewBlock() + self.start_new_block() return True return False elif(selector == 'moveUp:'): - prevBlock = self.get_history_previous(self.currentBlock()) + prevBlock = self.get_history_previous(self.current_block()) if(prevBlock != None): - self.replaceCurrentBlockWithString(textView, prevBlock) + self.replace_current_block_with_string(textView, prevBlock) else: NSBeep() return True @@ -200,26 +337,30 @@ class IPythonCocoaController(NSObject, FrontEndBase): elif(selector == 'moveDown:'): nextBlock = self.get_history_next() if(nextBlock != None): - self.replaceCurrentBlockWithString(textView, nextBlock) + self.replace_current_block_with_string(textView, nextBlock) else: NSBeep() return True elif(selector == 'moveToBeginningOfParagraph:'): - textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location, 0)) + textView.setSelectedRange_(NSMakeRange( + self.current_block_range().location, + 0)) return True elif(selector == 'moveToEndOfParagraph:'): - textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location + \ - self.currentBlockRange().length, 0)) + textView.setSelectedRange_(NSMakeRange( + self.current_block_range().location + \ + self.current_block_range().length, 0)) return True elif(selector == 'deleteToEndOfParagraph:'): - if(textView.selectedRange().location <= self.currentBlockRange().location): + if(textView.selectedRange().location <= \ + self.current_block_range().location): # Intersect the selected range with the current line range - if(self.currentBlockRange().length < 0): + if(self.current_block_range().length < 0): self.blockRanges[self.currentBlockID].length = 0 r = NSIntersectionRange(textView.rangesForUserTextChange()[0], - self.currentBlockRange()) + self.current_block_range()) if(r.length > 0): #no intersection textView.setSelectedRange_(r) @@ -227,7 +368,7 @@ class IPythonCocoaController(NSObject, FrontEndBase): return False # don't actually handle the delete elif(selector == 'insertTab:'): - if(len(self.currentLine().strip()) == 0): #only white space + if(len(self.current_line().strip()) == 0): #only white space return False else: self.textView.complete_(self) @@ -235,39 +376,45 @@ class IPythonCocoaController(NSObject, FrontEndBase): elif(selector == 'deleteBackward:'): #if we're at the beginning of the current block, ignore - if(textView.selectedRange().location == self.currentBlockRange().location): + if(textView.selectedRange().location == \ + self.current_block_range().location): return True else: - self.currentBlockRange().length-=1 + self.current_block_range().length-=1 return False return False - def textView_shouldChangeTextInRanges_replacementStrings_(self, textView, ranges, replacementStrings): + def textView_shouldChangeTextInRanges_replacementStrings_(self, + textView, ranges, replacementStrings): """ Delegate method for NSTextView. - Refuse change text in ranges not at end, but make those changes at end. + Refuse change text in ranges not at end, but make those changes at + end. """ - #print 'textView_shouldChangeTextInRanges_replacementStrings_:',ranges,replacementStrings assert(len(ranges) == len(replacementStrings)) allow = True for r,s in zip(ranges, replacementStrings): r = r.rangeValue() if(textView.textStorage().length() > 0 and - r.location < self.currentBlockRange().location): + r.location < self.current_block_range().location): self.insert_text(s) allow = False - self.blockRanges.setdefault(self.currentBlockID, self.currentBlockRange()).length += len(s) + self.blockRanges.setdefault(self.currentBlockID, + self.current_block_range()).length +=\ + len(s) return allow - def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, textView, words, charRange, index): + def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, + textView, words, charRange, index): try: - token = textView.textStorage().string().substringWithRange_(charRange) + ts = textView.textStorage() + token = ts.string().substringWithRange_(charRange) completions = blockingCallFromThread(self.complete, token) except: completions = objc.nil @@ -275,121 +422,4 @@ class IPythonCocoaController(NSObject, FrontEndBase): return (completions,0) - - def startNewBlock(self): - """""" - - self.currentBlockID = self.nextBlockID() - - - - def nextBlockID(self): - - return uuid.uuid4() - - def currentBlockRange(self): - return self.blockRanges.get(self.currentBlockID, NSMakeRange(self.textView.textStorage().length(), 0)) - - def currentBlock(self): - """The current block's text""" - - return self.textForRange(self.currentBlockRange()) - - def textForRange(self, textRange): - """textForRange""" - - return self.textView.textStorage().string().substringWithRange_(textRange) - - def currentLine(self): - block = self.textForRange(self.currentBlockRange()) - block = block.split('\n') - return block[-1] - - def update_cell_prompt(self, result): - if(isinstance(result, Failure)): - blockID = result.blockID - else: - blockID = result['blockID'] - - - self.insert_text(self.input_prompt(result=result), - textRange=NSMakeRange(self.blockRanges[blockID].location,0), - scrollToVisible=False - ) - - return result - - - def render_result(self, result): - blockID = result['blockID'] - inputRange = self.blockRanges[blockID] - del self.blockRanges[blockID] - - #print inputRange,self.currentBlockRange() - self.insert_text('\n' + - self.output_prompt(result) + - result.get('display',{}).get('pprint','') + - '\n\n', - textRange=NSMakeRange(inputRange.location+inputRange.length, 0)) - return result - - - def render_error(self, failure): - self.insert_text('\n\n'+str(failure)+'\n\n') - self.startNewBlock() - return failure - - - def insert_text(self, string=None, textRange=None, scrollToVisible=True): - """Insert text into textView at textRange, updating blockRanges as necessary""" - - if(textRange == None): - textRange = NSMakeRange(self.textView.textStorage().length(), 0) #range for end of text - - for r in self.blockRanges.itervalues(): - intersection = NSIntersectionRange(r,textRange) - if(intersection.length == 0): #ranges don't intersect - if r.location >= textRange.location: - r.location += len(string) - else: #ranges intersect - if(r.location <= textRange.location): - assert(intersection.length == textRange.length) - r.length += textRange.length - else: - r.location += intersection.length - - self.textView.replaceCharactersInRange_withString_(textRange, string) #textStorage().string() - self.textView.setSelectedRange_(NSMakeRange(textRange.location+len(string), 0)) - if(scrollToVisible): - self.textView.scrollRangeToVisible_(textRange) - - - - def replaceCurrentBlockWithString(self, textView, string): - textView.replaceCharactersInRange_withString_(self.currentBlockRange(), - string) - self.currentBlockRange().length = len(string) - r = NSMakeRange(textView.textStorage().length(), 0) - textView.scrollRangeToVisible_(r) - textView.setSelectedRange_(r) - - - def currentIndentString(self): - """returns string for indent or None if no indent""" - - if(len(self.currentBlock()) > 0): - lines = self.currentBlock().split('\n') - currentIndent = len(lines[-1]) - len(lines[-1]) - if(currentIndent == 0): - currentIndent = self.tabSpaces - - if(self.tabUsesSpaces): - result = ' ' * currentIndent - else: - result = '\t' * (currentIndent/self.tabSpaces) - else: - result = None - - return result - diff --git a/IPython/frontend/cocoa/tests/test_cocoa_frontend.py b/IPython/frontend/cocoa/tests/test_cocoa_frontend.py index ae1b9d6..eb4ae43 100644 --- a/IPython/frontend/cocoa/tests/test_cocoa_frontend.py +++ b/IPython/frontend/cocoa/tests/test_cocoa_frontend.py @@ -1,27 +1,20 @@ # encoding: utf-8 -"""This file contains unittests for the ipython1.frontend.cocoa.cocoa_frontend module. - -Things that should be tested: - - - IPythonCocoaController instantiates an IEngineInteractive - - IPythonCocoaController executes code on the engine - - IPythonCocoaController mirrors engine's user_ns +"""This file contains unittests for the +IPython.frontend.cocoa.cocoa_frontend module. """ __docformat__ = "restructuredtext en" - -#------------------------------------------------------------------------------- -# Copyright (C) 2005 Fernando Perez -# Brian E Granger -# Benjamin Ragan-Kelley -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports -#------------------------------------------------------------------------------- -from IPython.kernel.core.interpreter import Interpreter + +#--------------------------------------------------------------------------- +# Copyright (C) 2005 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#--------------------------------------------------------------------------- + +#--------------------------------------------------------------------------- +# Imports +#--------------------------------------------------------------------------- +from IPython.kernel.core.interpreter import Interpreter import IPython.kernel.engineservice as es from IPython.testing.util import DeferredTestCase from twisted.internet.defer import succeed @@ -51,7 +44,9 @@ class TestIPythonCocoaControler(DeferredTestCase): del result['number'] del result['id'] return result - self.assertDeferredEquals(self.controller.execute(code).addCallback(removeNumberAndID), expected) + self.assertDeferredEquals( + self.controller.execute(code).addCallback(removeNumberAndID), + expected) def testControllerMirrorsUserNSWithValuesAsStrings(self): code = """userns1=1;userns2=2""" diff --git a/IPython/frontend/frontendbase.py b/IPython/frontend/frontendbase.py index 577bc97..644166a 100644 --- a/IPython/frontend/frontendbase.py +++ b/IPython/frontend/frontendbase.py @@ -1,6 +1,8 @@ # encoding: utf-8 +# -*- test-case-name: IPython.frontend.tests.test_frontendbase -*- """ -frontendbase provides an interface and base class for GUI frontends for IPython.kernel/IPython.kernel.core. +frontendbase provides an interface and base class for GUI frontends for +IPython.kernel/IPython.kernel.core. Frontend implementations will likely want to subclass FrontEndBase. @@ -57,20 +59,34 @@ class IFrontEndFactory(zi.Interface): class IFrontEnd(zi.Interface): """Interface for frontends. All methods return t.i.d.Deferred""" - zi.Attribute("input_prompt_template", "string.Template instance substituteable with execute result.") - zi.Attribute("output_prompt_template", "string.Template instance substituteable with execute result.") - zi.Attribute("continuation_prompt_template", "string.Template instance substituteable with execute result.") + zi.Attribute("input_prompt_template", "string.Template instance\ + substituteable with execute result.") + zi.Attribute("output_prompt_template", "string.Template instance\ + substituteable with execute result.") + zi.Attribute("continuation_prompt_template", "string.Template instance\ + substituteable with execute result.") def update_cell_prompt(self, result): """Subclass may override to update the input prompt for a block. - Since this method will be called as a twisted.internet.defer.Deferred's callback, - implementations should return result when finished.""" + Since this method will be called as a + twisted.internet.defer.Deferred's callback, + implementations should return result when finished. + + NB: result is a failure if the execute returned a failre. + To get the blockID, you should do something like:: + if(isinstance(result, twisted.python.failure.Failure)): + blockID = result.blockID + else: + blockID = result['blockID'] + """ pass def render_result(self, result): - """Render the result of an execute call. Implementors may choose the method of rendering. - For example, a notebook-style frontend might render a Chaco plot inline. + """Render the result of an execute call. Implementors may choose the + method of rendering. + For example, a notebook-style frontend might render a Chaco plot + inline. Parameters: result : dict (result of IEngineBase.execute ) @@ -82,24 +98,31 @@ class IFrontEnd(zi.Interface): pass def render_error(self, failure): - """Subclasses must override to render the failure. Since this method will be called as a - twisted.internet.defer.Deferred's callback, implementations should return result - when finished.""" + """Subclasses must override to render the failure. Since this method + ill be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished. + """ pass def input_prompt(result={}): - """Returns the input prompt by subsituting into self.input_prompt_template""" + """Returns the input prompt by subsituting into + self.input_prompt_template + """ pass def output_prompt(result): - """Returns the output prompt by subsituting into self.output_prompt_template""" + """Returns the output prompt by subsituting into + self.output_prompt_template + """ pass def continuation_prompt(): - """Returns the continuation prompt by subsituting into self.continuation_prompt_template""" + """Returns the continuation prompt by subsituting into + self.continuation_prompt_template + """ pass @@ -221,7 +244,8 @@ class FrontEndBase(object): Parameters: block : {str, AST} blockID : any - Caller may provide an ID to identify this block. result['blockID'] := blockID + Caller may provide an ID to identify this block. + result['blockID'] := blockID Result: Deferred result of self.interpreter.execute @@ -243,8 +267,8 @@ class FrontEndBase(object): def _add_block_id(self, result, blockID): - """Add the blockID to result or failure. Unfortunatley, we have to treat failures - differently than result dicts + """Add the blockID to result or failure. Unfortunatley, we have to + treat failures differently than result dicts. """ if(isinstance(result, Failure)): @@ -291,11 +315,12 @@ class FrontEndBase(object): def update_cell_prompt(self, result): """Subclass may override to update the input prompt for a block. - Since this method will be called as a twisted.internet.defer.Deferred's callback, - implementations should return result when finished. + Since this method will be called as a + twisted.internet.defer.Deferred's callback, implementations should + return result when finished. - NP: result is a failure if the execute returned a failre. To get the blockID, you should - do something like:: + NB: result is a failure if the execute returned a failre. + To get the blockID, you should do something like:: if(isinstance(result, twisted.python.failure.Failure)): blockID = result.blockID else: @@ -308,17 +333,18 @@ class FrontEndBase(object): def render_result(self, result): - """Subclasses must override to render result. Since this method will be called as a - twisted.internet.defer.Deferred's callback, implementations should return result - when finished.""" + """Subclasses must override to render result. Since this method will + be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished. + """ return result def render_error(self, failure): - """Subclasses must override to render the failure. Since this method will be called as a - twisted.internet.defer.Deferred's callback, implementations should return result - when finished.""" + """Subclasses must override to render the failure. Since this method + will be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished.""" return failure diff --git a/IPython/frontend/tests/test_frontendbase.py b/IPython/frontend/tests/test_frontendbase.py index d3c265a..451e302 100644 --- a/IPython/frontend/tests/test_frontendbase.py +++ b/IPython/frontend/tests/test_frontendbase.py @@ -4,16 +4,16 @@ __docformat__ = "restructuredtext en" -#------------------------------------------------------------------------------- -# Copyright (C) 2008 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports -#------------------------------------------------------------------------------- +#--------------------------------------------------------------------------- +# Copyright (C) 2008 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#--------------------------------------------------------------------------- + +#--------------------------------------------------------------------------- +# Imports +#--------------------------------------------------------------------------- import unittest from IPython.frontend import frontendbase @@ -22,7 +22,8 @@ from IPython.kernel.engineservice import EngineService class FrontEndCallbackChecker(frontendbase.FrontEndBase): """FrontEndBase subclass for checking callbacks""" def __init__(self, engine=None, history=None): - super(FrontEndCallbackChecker, self).__init__(engine=engine, history=history) + super(FrontEndCallbackChecker, self).__init__(engine=engine, + history=history) self.updateCalled = False self.renderResultCalled = False self.renderErrorCalled = False @@ -51,7 +52,8 @@ class TestFrontendBase(unittest.TestCase): def test_implements_IFrontEnd(self): - assert(frontendbase.IFrontEnd.implementedBy(frontendbase.FrontEndBase)) + assert(frontendbase.IFrontEnd.implementedBy( + frontendbase.FrontEndBase)) def test_is_complete_returns_False_for_incomplete_block(self): diff --git a/IPython/genutils.py b/IPython/genutils.py index 2cc19c0..4bc418d 100644 --- a/IPython/genutils.py +++ b/IPython/genutils.py @@ -27,9 +27,11 @@ try: except ImportError: pass import os +import platform import re import shlex import shutil +import subprocess import sys import tempfile import time @@ -2041,6 +2043,55 @@ def wrap_deprecated(func, suggest = ''): stacklevel = 2) return func(*args, **kwargs) return newFunc - -#*************************** end of file ********************** + +def _num_cpus_unix(): + """Return the number of active CPUs on a Unix system.""" + return os.sysconf("SC_NPROCESSORS_ONLN") + + +def _num_cpus_darwin(): + """Return the number of active CPUs on a Darwin system.""" + p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE) + return p.stdout.read() + + +def _num_cpus_windows(): + """Return the number of active CPUs on a Windows system.""" + return os.environ.get("NUMBER_OF_PROCESSORS") + + +def num_cpus(): + """Return the effective number of CPUs in the system as an integer. + + This cross-platform function makes an attempt at finding the total number of + available CPUs in the system, as returned by various underlying system and + python calls. + + If it can't find a sensible answer, it returns 1 (though an error *may* make + it return a large positive number that's actually incorrect). + """ + + # Many thanks to the Parallel Python project (http://www.parallelpython.com) + # for the names of the keys we needed to look up for this function. This + # code was inspired by their equivalent function. + + ncpufuncs = {'Linux':_num_cpus_unix, + 'Darwin':_num_cpus_darwin, + 'Windows':_num_cpus_windows, + # On Vista, python < 2.5.2 has a bug and returns 'Microsoft' + # See http://bugs.python.org/issue1082 for details. + 'Microsoft':_num_cpus_windows, + } + + ncpufunc = ncpufuncs.get(platform.system(), + # default to unix version (Solaris, AIX, etc) + _num_cpus_unix) + + try: + ncpus = max(1,int(ncpufunc())) + except: + ncpus = 1 + return ncpus + +#*************************** end of file ********************** diff --git a/IPython/ipstruct.py b/IPython/ipstruct.py index d362416..47cdf1e 100644 --- a/IPython/ipstruct.py +++ b/IPython/ipstruct.py @@ -55,24 +55,32 @@ class Struct: Define a dictionary and initialize both with dict and k=v pairs: >>> d={'a':1,'b':2} >>> s=Struct(d,hi=10,ho=20) + The return of __repr__ can be used to create a new instance: >>> s - Struct({'ho': 20, 'b': 2, 'hi': 10, 'a': 1}) + Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20}) + + Note: the special '__allownew' key is used for internal purposes. + __str__ (called by print) shows it's not quite a regular dictionary: >>> print s - Struct {a: 1, b: 2, hi: 10, ho: 20} + Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20}) + Access by explicitly named key with dot notation: >>> s.a 1 + Or like a dictionary: >>> s['a'] 1 + If you want a variable to hold the key value, only dictionary access works: >>> key='hi' >>> s.key Traceback (most recent call last): File "", line 1, in ? AttributeError: Struct instance has no attribute 'key' + >>> s[key] 10 @@ -81,13 +89,16 @@ class Struct: accessed using the dictionary syntax. Again, an example: This doesn't work: - >>> s=Struct(4='hi') + >>> s=Struct(4='hi') #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... SyntaxError: keyword can't be an expression + But this does: >>> s=Struct() >>> s[4]='hi' >>> s - Struct({4: 'hi'}) + Struct({4: 'hi', '__allownew': True}) >>> s[4] 'hi' """ @@ -318,7 +329,8 @@ class Struct: if __conflict_solve: inv_conflict_solve_user = __conflict_solve.copy() for name, func in [('preserve',preserve), ('update',update), - ('add',add), ('add_flip',add_flip), ('add_s',add_s)]: + ('add',add), ('add_flip',add_flip), + ('add_s',add_s)]: if name in inv_conflict_solve_user.keys(): inv_conflict_solve_user[func] = inv_conflict_solve_user[name] del inv_conflict_solve_user[name] @@ -369,14 +381,14 @@ class Struct: return ret def get(self,attr,val=None): - """S.get(k[,d]) -> S[k] if S.has_key(k), else d. d defaults to None.""" + """S.get(k[,d]) -> S[k] if k in S, else d. d defaults to None.""" try: return self[attr] except KeyError: return val def setdefault(self,attr,val=None): - """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if not S.has_key(k)""" + """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if k not in S""" if not self.has_key(attr): self[attr] = val return self.get(attr,val) @@ -384,8 +396,8 @@ class Struct: def allow_new_attr(self, allow = True): """ Set whether new attributes can be created inside struct - This can be used to catch typos by verifying that the attribute user tries to - change already exists in this Struct. + This can be used to catch typos by verifying that the attribute user + tries to change already exists in this Struct. """ self['__allownew'] = allow diff --git a/IPython/kernel/engineservice.py b/IPython/kernel/engineservice.py index 90b5f98..c8ec1fc 100644 --- a/IPython/kernel/engineservice.py +++ b/IPython/kernel/engineservice.py @@ -847,11 +847,13 @@ class Command(object): self.deferred.errback(reason) class ThreadedEngineService(EngineService): - """An EngineService subclass that defers execute commands to a separate thread. + """An EngineService subclass that defers execute commands to a separate + thread. - ThreadedEngineService uses twisted.internet.threads.deferToThread to defer execute - requests to a separate thread. GUI frontends may want to use ThreadedEngineService as - the engine in an IPython.frontend.frontendbase.FrontEndBase subclass to prevent + ThreadedEngineService uses twisted.internet.threads.deferToThread to + defer execute requests to a separate thread. GUI frontends may want to + use ThreadedEngineService as the engine in an + IPython.frontend.frontendbase.FrontEndBase subclass to prevent block execution from blocking the GUI thread. """ diff --git a/docs/ChangeLog b/docs/attic/ChangeLog similarity index 100% rename from docs/ChangeLog rename to docs/attic/ChangeLog diff --git a/docs/source/changes.txt b/docs/source/changes.txt index 85d3b83..d10ced7 100644 --- a/docs/source/changes.txt +++ b/docs/source/changes.txt @@ -29,6 +29,7 @@ New features Development Team" as the copyright holder. We give more details about exactly what this means in this file. All developer should read this and use the new banner in all IPython source code files. + * sh profile: ./foo runs foo as system command, no need to do !./foo anymore Bug fixes --------- diff --git a/docs/source/development/development.txt b/docs/source/development/development.txt index 75cb395..375ae23 100644 --- a/docs/source/development/development.txt +++ b/docs/source/development/development.txt @@ -5,22 +5,6 @@ IPython development guidelines ================================== .. contents:: -.. - 1 Overview - 2 Project organization - 2.1 Subpackages - 2.2 Installation and dependencies - 2.3 Specific subpackages - 3 Version control - 4 Documentation - 4.1 Standalone documentation - 4.2 Docstring format - 5 Coding conventions - 5.1 General - 5.2 Naming conventions - 6 Testing - 7 Configuration -.. Overview @@ -136,11 +120,72 @@ Specific subpackages Version control =============== -In the past, IPython development has been done using `Subversion`__. We are currently trying out `Bazaar`__ and `Launchpad`__. +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 +to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython +development. First, you should install Bazaar. After you have done that, make +sure that it is working by getting the latest main branch of IPython:: + + $ bzr branch lp:ipython + +Now you can create a new branch for you to do your work in:: + + $ bzr branch ipython ipython-mybranch + +The typical work cycle in this branch will be to make changes in `ipython-mybranch` +and then commit those changes using the commit command:: + + $ ...do work in ipython-mybranch... + $ bzr ci -m "the commit message goes here" + +Please note that since we now don't use an old-style linear ChangeLog +(that tends to cause problems with distributed version control +systems), you should ensure that your log messages are reasonably +detailed. Use a docstring-like approach in the commit messages +(including the second line being left *blank*):: + + Single line summary of changes being committed. + + - more details when warranted ... + - including crediting outside contributors if they sent the + code/bug/idea! + +If we couple this with a policy of making single commits for each +reasonably atomic change, the bzr log should give an excellent view of +the project, and the `--short` log option becomes a nice summary. + +While working with this branch, it is a good idea to merge in changes that have been +made upstream in the parent branch. This can be done by doing:: + + $ bzr pull + +If this command shows that the branches have diverged, then you should do a merge +instead:: + + $ bzr merge lp:ipython + +If you want others to be able to see your branch, you can create an account with +launchpad and push the branch to your own workspace:: + + $ bzr push bzr+ssh://@bazaar.launchpad.net/~/+junk/ipython-mybranch + +Finally, once the work in your branch is done, you can merge your changes back into +the `ipython` branch by using merge:: + + $ cd ipython + $ merge ../ipython-mybranch + [resolve any conflicts] + $ bzr ci -m "Fixing that bug" + $ bzr push + +But this will require you to have write permissions to the `ipython` branch. It you don't +you can tell one of the IPython devs about your branch and they can do the merge for you. + +More information about Bazaar workflows can be found `here`__. .. __: http://subversion.tigris.org/ .. __: http://bazaar-vcs.org/ .. __: http://www.launchpad.net/ipython +.. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html Documentation ============= diff --git a/setupbase.py b/setupbase.py index 52d3695..c1d9a3a 100644 --- a/setupbase.py +++ b/setupbase.py @@ -160,7 +160,9 @@ def find_data_files(): ('data', manpagebase, manpages), ('data',pjoin(docdirbase, 'extensions'),igridhelpfiles), ] - return data_files + # import pprint + # pprint.pprint(data_files) + return [] #--------------------------------------------------------------------------- # Find scripts