From 099aada685f5b2ce3977bb896b71b94b2ab642b7 2008-09-10 22:59:33 From: Fernando Perez Date: 2008-09-10 22:59:33 Subject: [PATCH] Merge with upstream --- diff --git a/IPython/Extensions/ipy_miscapps.py b/IPython/Extensions/ipy_miscapps.py deleted file mode 100644 index 5e05d57..0000000 --- a/IPython/Extensions/ipy_miscapps.py +++ /dev/null @@ -1,19 +0,0 @@ -""" Completers for miscellaneous command line apps - -""" -import IPython.ipapi -ip = IPython.ipapi.get() -import os - -def surfraw_completer(self,cmdline): - """ Completer for 'surfraw' - - example:: - sr go => sr google - - """ - compl = [l.split(None,1)[0] for l in os.popen('sr -elvi')] - return compl - - -ip.set_hook('complete_command', surfraw_completer, str_key = 'sr') \ No newline at end of file diff --git a/IPython/UserConfig/ipythonrc-physics b/IPython/UserConfig/ipythonrc-physics index a2c45c5..c7c25a3 100644 --- a/IPython/UserConfig/ipythonrc-physics +++ b/IPython/UserConfig/ipythonrc-physics @@ -38,6 +38,8 @@ ececute rad = pi/180. execute print '*** q is an alias for PhysicalQuantityInteractive' execute print '*** g = 9.8 m/s^2 has been defined' execute print '*** rad = pi/180 has been defined' +execute import ipy_constants as C +execute print '*** C is the physical constants module' # Files to execute execfile diff --git a/IPython/config/api.py b/IPython/config/api.py index a24a42c..d394098 100644 --- a/IPython/config/api.py +++ b/IPython/config/api.py @@ -16,7 +16,9 @@ __docformat__ = "restructuredtext en" #------------------------------------------------------------------------------- import os -from IPython.config.cutils import get_home_dir, get_ipython_dir +from os.path import join as pjoin + +from IPython.genutils import get_home_dir, get_ipython_dir from IPython.external.configobj import ConfigObj # Traitlets config imports @@ -53,7 +55,7 @@ class ConfigObjManager(object): def write_default_config_file(self): ipdir = get_ipython_dir() - fname = ipdir + '/' + self.filename + fname = pjoin(ipdir, self.filename) if not os.path.isfile(fname): print "Writing the configuration file to: " + fname self.write_config_obj_to_file(fname) @@ -87,11 +89,11 @@ class ConfigObjManager(object): # In ipythondir if it is set if ipythondir is not None: - trythis = ipythondir + '/' + filename + trythis = pjoin(ipythondir, filename) if os.path.isfile(trythis): return trythis - trythis = get_ipython_dir() + '/' + filename + trythis = pjoin(get_ipython_dir(), filename) if os.path.isfile(trythis): return trythis diff --git a/IPython/config/cutils.py b/IPython/config/cutils.py index ad3cfc4..d72bcc9 100644 --- a/IPython/config/cutils.py +++ b/IPython/config/cutils.py @@ -22,71 +22,6 @@ import sys # Normal code begins #--------------------------------------------------------------------------- -class HomeDirError(Exception): - pass - -def get_home_dir(): - """Return the closest possible equivalent to a 'home' directory. - - We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH. - - Currently only Posix and NT are implemented, a HomeDirError exception is - raised for all other OSes. """ - - isdir = os.path.isdir - env = os.environ - try: - homedir = env['HOME'] - if not isdir(homedir): - # in case a user stuck some string which does NOT resolve to a - # valid path, it's as good as if we hadn't foud it - raise KeyError - return homedir - except KeyError: - if os.name == 'posix': - raise HomeDirError,'undefined $HOME, IPython can not proceed.' - elif os.name == 'nt': - # For some strange reason, win9x returns 'nt' for os.name. - try: - homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH']) - if not isdir(homedir): - homedir = os.path.join(env['USERPROFILE']) - if not isdir(homedir): - raise HomeDirError - return homedir - except: - try: - # Use the registry to get the 'My Documents' folder. - import _winreg as wreg - key = wreg.OpenKey(wreg.HKEY_CURRENT_USER, - "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") - homedir = wreg.QueryValueEx(key,'Personal')[0] - key.Close() - if not isdir(homedir): - e = ('Invalid "Personal" folder registry key ' - 'typically "My Documents".\n' - 'Value: %s\n' - 'This is not a valid directory on your system.' % - homedir) - raise HomeDirError(e) - return homedir - except HomeDirError: - raise - except: - return 'C:\\' - elif os.name == 'dos': - # Desperate, may do absurd things in classic MacOS. May work under DOS. - return 'C:\\' - else: - raise HomeDirError,'support for your operating system not implemented.' - -def get_ipython_dir(): - ipdir_def = '.ipython' - home_dir = get_home_dir() - ipdir = os.path.abspath(os.environ.get('IPYTHONDIR', - os.path.join(home_dir,ipdir_def))) - return ipdir - def import_item(key): """ Import and return bar given the string foo.bar. diff --git a/IPython/frontend/_process/killableprocess.py b/IPython/frontend/_process/killableprocess.py index e845686..955482c 100644 --- a/IPython/frontend/_process/killableprocess.py +++ b/IPython/frontend/_process/killableprocess.py @@ -50,7 +50,6 @@ import subprocess from subprocess import PIPE import sys import os -import time import types try: @@ -69,8 +68,16 @@ except ImportError: mswindows = (sys.platform == "win32") +skip = False + if mswindows: - import winprocess + import platform + if platform.uname()[3] == '' or platform.uname()[3] > '6.0.6000': + # Killable process does not work under vista when starting for + # something else than cmd. + skip = True + else: + import winprocess else: import signal @@ -78,7 +85,11 @@ if not mswindows: def DoNothing(*args): pass -class Popen(subprocess.Popen): + +if skip: + Popen = subprocess.Popen +else: + class Popen(subprocess.Popen): if not mswindows: # Override __init__ to set a preexec_fn def __init__(self, *args, **kwargs): diff --git a/IPython/frontend/_process/pipedprocess.py b/IPython/frontend/_process/pipedprocess.py index 2cda128..1b145af 100644 --- a/IPython/frontend/_process/pipedprocess.py +++ b/IPython/frontend/_process/pipedprocess.py @@ -50,7 +50,7 @@ class PipedProcess(Thread): """ env = os.environ env['TERM'] = 'xterm' - process = Popen((self.command_string + ' 2>&1', ), shell=True, + process = Popen(self.command_string + ' 2>&1', shell=True, env=env, universal_newlines=True, stdout=PIPE, stdin=PIPE, ) diff --git a/IPython/frontend/linefrontendbase.py b/IPython/frontend/linefrontendbase.py index 6f60d75..494fc71 100644 --- a/IPython/frontend/linefrontendbase.py +++ b/IPython/frontend/linefrontendbase.py @@ -20,6 +20,8 @@ import re import IPython import sys +import codeop +import traceback from frontendbase import FrontEndBase from IPython.kernel.core.interpreter import Interpreter @@ -76,6 +78,11 @@ class LineFrontEndBase(FrontEndBase): if banner is not None: self.banner = banner + + def start(self): + """ Put the frontend in a state where it is ready for user + interaction. + """ if self.banner is not None: self.write(self.banner, refresh=False) @@ -141,9 +148,18 @@ class LineFrontEndBase(FrontEndBase): and not re.findall(r"\n[\t ]*\n[\t ]*$", string)): return False else: - # Add line returns here, to make sure that the statement is - # complete. - return FrontEndBase.is_complete(self, string.rstrip() + '\n\n') + self.capture_output() + try: + # Add line returns here, to make sure that the statement is + # complete. + is_complete = codeop.compile_command(string.rstrip() + '\n\n', + "", "exec") + self.release_output() + except Exception, e: + # XXX: Hack: return True so that the + # code gets executed and the error captured. + is_complete = True + return is_complete def write(self, string, refresh=True): @@ -181,7 +197,7 @@ class LineFrontEndBase(FrontEndBase): #-------------------------------------------------------------------------- def prefilter_input(self, string): - """ Priflter the input to turn it in valid python. + """ Prefilter the input to turn it in valid python. """ string = string.replace('\r\n', '\n') string = string.replace('\t', 4*' ') @@ -210,9 +226,12 @@ class LineFrontEndBase(FrontEndBase): line = self.input_buffer new_line, completions = self.complete(line) if len(completions)>1: - self.write_completion(completions) - self.input_buffer = new_line + self.write_completion(completions, new_line=new_line) + elif not line == new_line: + self.input_buffer = new_line if self.debug: + print >>sys.__stdout__, 'line', line + print >>sys.__stdout__, 'new_line', new_line print >>sys.__stdout__, completions @@ -222,10 +241,15 @@ class LineFrontEndBase(FrontEndBase): return 80 - def write_completion(self, possibilities): + def write_completion(self, possibilities, new_line=None): """ Write the list of possible completions. + + new_line is the completed input line that should be displayed + after the completion are writen. If None, the input_buffer + before the completion is used. """ - current_buffer = self.input_buffer + if new_line is None: + new_line = self.input_buffer self.write('\n') max_len = len(max(possibilities, key=len)) + 1 @@ -246,7 +270,7 @@ class LineFrontEndBase(FrontEndBase): self.write(''.join(buf)) self.new_prompt(self.input_prompt_template.substitute( number=self.last_result['number'] + 1)) - self.input_buffer = current_buffer + self.input_buffer = new_line def new_prompt(self, prompt): @@ -275,6 +299,8 @@ class LineFrontEndBase(FrontEndBase): else: self.input_buffer += self._get_indent_string( current_buffer[:-1]) + if len(current_buffer.split('\n')) == 2: + self.input_buffer += '\t\t' if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'): self.input_buffer += '\t' diff --git a/IPython/frontend/prefilterfrontend.py b/IPython/frontend/prefilterfrontend.py index 8479132..ad6ce13 100644 --- a/IPython/frontend/prefilterfrontend.py +++ b/IPython/frontend/prefilterfrontend.py @@ -24,6 +24,7 @@ __docformat__ = "restructuredtext en" import sys from linefrontendbase import LineFrontEndBase, common_prefix +from frontendbase import FrontEndBase from IPython.ipmaker import make_IPython from IPython.ipapi import IPApi @@ -34,6 +35,7 @@ from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap from IPython.genutils import Term import pydoc import os +import sys def mk_system_call(system_call_function, command): @@ -57,6 +59,8 @@ class PrefilterFrontEnd(LineFrontEndBase): to execute the statements and the ipython0 used for code completion... """ + + debug = False def __init__(self, ipython0=None, *args, **kwargs): """ Parameters: @@ -65,12 +69,24 @@ class PrefilterFrontEnd(LineFrontEndBase): ipython0: an optional ipython0 instance to use for command prefiltering and completion. """ + LineFrontEndBase.__init__(self, *args, **kwargs) + self.shell.output_trap = RedirectorOutputTrap( + out_callback=self.write, + err_callback=self.write, + ) + self.shell.traceback_trap = SyncTracebackTrap( + formatters=self.shell.traceback_trap.formatters, + ) + + # Start the ipython0 instance: self.save_output_hooks() if ipython0 is None: # Instanciate an IPython0 interpreter to be able to use the # prefiltering. # XXX: argv=[] is a bit bold. - ipython0 = make_IPython(argv=[]) + ipython0 = make_IPython(argv=[], + user_ns=self.shell.user_ns, + user_global_ns=self.shell.user_global_ns) self.ipython0 = ipython0 # Set the pager: self.ipython0.set_hook('show_in_pager', @@ -86,24 +102,13 @@ class PrefilterFrontEnd(LineFrontEndBase): 'ls -CF') # And now clean up the mess created by ipython0 self.release_output() + + if not 'banner' in kwargs and self.banner is None: - kwargs['banner'] = self.ipython0.BANNER + """ + self.banner = self.ipython0.BANNER + """ This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code.""" - LineFrontEndBase.__init__(self, *args, **kwargs) - # XXX: Hack: mix the two namespaces - self.shell.user_ns.update(self.ipython0.user_ns) - self.ipython0.user_ns = self.shell.user_ns - self.shell.user_global_ns.update(self.ipython0.user_global_ns) - self.ipython0.user_global_ns = self.shell.user_global_ns - - self.shell.output_trap = RedirectorOutputTrap( - out_callback=self.write, - err_callback=self.write, - ) - self.shell.traceback_trap = SyncTracebackTrap( - formatters=self.shell.traceback_trap.formatters, - ) + self.start() #-------------------------------------------------------------------------- # FrontEndBase interface @@ -113,7 +118,7 @@ This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code.""" """ Use ipython0 to capture the last traceback and display it. """ self.capture_output() - self.ipython0.showtraceback() + self.ipython0.showtraceback(tb_offset=-1) self.release_output() @@ -164,6 +169,8 @@ This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code.""" def complete(self, line): + # FIXME: This should be factored out in the linefrontendbase + # method. word = line.split('\n')[-1].split(' ')[-1] completions = self.ipython0.complete(word) # FIXME: The proper sort should be done in the complete method. diff --git a/IPython/frontend/tests/test_frontendbase.py b/IPython/frontend/tests/test_frontendbase.py index c89af0b..617456e 100644 --- a/IPython/frontend/tests/test_frontendbase.py +++ b/IPython/frontend/tests/test_frontendbase.py @@ -25,6 +25,8 @@ except ImportError: import nose raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap") +from IPython.testing.decorators import skip + class FrontEndCallbackChecker(AsyncFrontEndBase): """FrontEndBase subclass for checking callbacks""" def __init__(self, engine=None, history=None): @@ -56,12 +58,10 @@ class TestAsyncFrontendBase(unittest.TestCase): self.fb = FrontEndCallbackChecker(engine=EngineService()) - def test_implements_IFrontEnd(self): assert(frontendbase.IFrontEnd.implementedBy( AsyncFrontEndBase)) - def test_is_complete_returns_False_for_incomplete_block(self): """""" @@ -80,7 +80,6 @@ class TestAsyncFrontendBase(unittest.TestCase): assert(self.fb.is_complete(block)) - def test_blockID_added_to_result(self): block = """3+3""" @@ -113,12 +112,11 @@ class TestAsyncFrontendBase(unittest.TestCase): d = self.fb.execute("10+10") d.addCallback(self.checkCallbacks) - def checkCallbacks(self, result): assert(self.fb.updateCalled) assert(self.fb.renderResultCalled) - + @skip("This test fails and lead to an unhandled error in a Deferred.") def test_error_callback_added_to_execute(self): """test that render_error called on execution error""" diff --git a/IPython/frontend/tests/test_process.py b/IPython/frontend/tests/test_process.py index d82c635..dc8db5f 100644 --- a/IPython/frontend/tests/test_process.py +++ b/IPython/frontend/tests/test_process.py @@ -19,6 +19,7 @@ import sys from IPython.frontend._process import PipedProcess from IPython.testing import decorators as testdec + def test_capture_out(): """ A simple test to see if we can execute a process and get the output. """ @@ -29,8 +30,7 @@ def test_capture_out(): result = s.getvalue().rstrip() assert result == '1' -# FIXME -@testdec.skip("This doesn't work under Windows") + def test_io(): """ Checks that we can send characters on stdin to the process. """ diff --git a/IPython/frontend/wx/console_widget.py b/IPython/frontend/wx/console_widget.py index 3afda8a..d61e84a 100644 --- a/IPython/frontend/wx/console_widget.py +++ b/IPython/frontend/wx/console_widget.py @@ -23,6 +23,7 @@ import wx import wx.stc as stc from wx.py import editwindow +import time import sys LINESEP = '\n' if sys.platform == 'win32': @@ -115,12 +116,15 @@ class ConsoleWidget(editwindow.EditWindow): # The color of the carret (call _apply_style() after setting) carret_color = 'BLACK' + # Store the last time a refresh was done + _last_refresh_time = 0 + #-------------------------------------------------------------------------- # Public API #-------------------------------------------------------------------------- def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, - size=wx.DefaultSize, style=0, ): + size=wx.DefaultSize, style=wx.WANTS_CHARS, ): editwindow.EditWindow.__init__(self, parent, id, pos, size, style) self._configure_scintilla() @@ -168,9 +172,14 @@ class ConsoleWidget(editwindow.EditWindow): self.GotoPos(self.GetLength()) if refresh: - # Maybe this is faster than wx.Yield() - self.ProcessEvent(wx.PaintEvent()) - #wx.Yield() + current_time = time.time() + if current_time - self._last_refresh_time > 0.03: + if sys.platform == 'win32': + wx.SafeYield() + else: + wx.Yield() + # self.ProcessEvent(wx.PaintEvent()) + self._last_refresh_time = current_time def new_prompt(self, prompt): @@ -183,7 +192,6 @@ class ConsoleWidget(editwindow.EditWindow): # now we update our cursor giving end of prompt self.current_prompt_pos = self.GetLength() self.current_prompt_line = self.GetCurrentLine() - wx.Yield() self.EnsureCaretVisible() diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py index b695897..d4182cc 100644 --- a/IPython/frontend/wx/wx_frontend.py +++ b/IPython/frontend/wx/wx_frontend.py @@ -128,6 +128,7 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): # while it is being swapped _out_buffer_lock = Lock() + # The different line markers used to higlight the prompts. _markers = dict() #-------------------------------------------------------------------------- @@ -135,12 +136,16 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): #-------------------------------------------------------------------------- def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, - size=wx.DefaultSize, style=wx.CLIP_CHILDREN, + size=wx.DefaultSize, + style=wx.CLIP_CHILDREN|wx.WANTS_CHARS, *args, **kwds): """ Create Shell instance. """ ConsoleWidget.__init__(self, parent, id, pos, size, style) PrefilterFrontEnd.__init__(self, **kwds) + + # Stick in our own raw_input: + self.ipython0.raw_input = self.raw_input # Marker for complete buffer. self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND, @@ -164,9 +169,11 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): # Inject self in namespace, for debug if self.debug: self.shell.user_ns['self'] = self + # Inject our own raw_input in namespace + self.shell.user_ns['raw_input'] = self.raw_input - def raw_input(self, prompt): + def raw_input(self, prompt=''): """ A replacement from python's raw_input. """ self.new_prompt(prompt) @@ -174,15 +181,13 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): if hasattr(self, '_cursor'): del self._cursor self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS)) - self.waiting = True self.__old_on_enter = self._on_enter + event_loop = wx.EventLoop() def my_on_enter(): - self.waiting = False + event_loop.Exit() self._on_enter = my_on_enter - # XXX: Busy waiting, ugly. - while self.waiting: - wx.Yield() - sleep(0.1) + # XXX: Running a separate event_loop. Ugly. + event_loop.Run() self._on_enter = self.__old_on_enter self._input_state = 'buffering' self._cursor = wx.BusyCursor() @@ -191,16 +196,18 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): def system_call(self, command_string): self._input_state = 'subprocess' + event_loop = wx.EventLoop() + def _end_system_call(): + self._input_state = 'buffering' + self._running_process = False + event_loop.Exit() + self._running_process = PipedProcess(command_string, out_callback=self.buffered_write, - end_callback = self._end_system_call) + end_callback = _end_system_call) self._running_process.start() - # XXX: another one of these polling loops to have a blocking - # call - wx.Yield() - while self._running_process: - wx.Yield() - sleep(0.1) + # XXX: Running a separate event_loop. Ugly. + event_loop.Run() # Be sure to flush the buffer. self._buffer_flush(event=None) @@ -226,8 +233,9 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): for name in symbol_string.split('.')[1:] + ['__doc__']: symbol = getattr(symbol, name) self.AutoCompCancel() - wx.Yield() - self.CallTipShow(self.GetCurrentPos(), symbol) + # Check that the symbol can indeed be converted to a string: + symbol += '' + wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol) except: # The retrieve symbol couldn't be converted to a string pass @@ -238,9 +246,9 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): true, open the menu. """ if self.debug: - print >>sys.__stdout__, "_popup_completion", + print >>sys.__stdout__, "_popup_completion" line = self.input_buffer - if (self.AutoCompActive() and not line[-1] == '.') \ + if (self.AutoCompActive() and line and not line[-1] == '.') \ or create==True: suggestion, completions = self.complete(line) offset=0 @@ -284,19 +292,21 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): if i in self._markers: self.MarkerDeleteHandle(self._markers[i]) self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER) - # Update the display: - wx.Yield() - self.GotoPos(self.GetLength()) - PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string) + # Use a callafter to update the display robustly under windows + def callback(): + self.GotoPos(self.GetLength()) + PrefilterFrontEnd.execute(self, python_string, + raw_string=raw_string) + wx.CallAfter(callback) def save_output_hooks(self): self.__old_raw_input = __builtin__.raw_input PrefilterFrontEnd.save_output_hooks(self) def capture_output(self): - __builtin__.raw_input = self.raw_input self.SetLexer(stc.STC_LEX_NULL) PrefilterFrontEnd.capture_output(self) + __builtin__.raw_input = self.raw_input def release_output(self): @@ -316,12 +326,24 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): def show_traceback(self): start_line = self.GetCurrentLine() PrefilterFrontEnd.show_traceback(self) - wx.Yield() + self.ProcessEvent(wx.PaintEvent()) + #wx.Yield() for i in range(start_line, self.GetCurrentLine()): self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER) #-------------------------------------------------------------------------- + # FrontEndBase interface + #-------------------------------------------------------------------------- + + def render_error(self, e): + start_line = self.GetCurrentLine() + self.write('\n' + e + '\n') + for i in range(start_line, self.GetCurrentLine()): + self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER) + + + #-------------------------------------------------------------------------- # ConsoleWidget interface #-------------------------------------------------------------------------- @@ -351,7 +373,8 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): if self._input_state == 'subprocess': if self.debug: print >>sys.__stderr__, 'Killing running process' - self._running_process.process.kill() + if hasattr(self._running_process, 'process'): + self._running_process.process.kill() elif self._input_state == 'buffering': if self.debug: print >>sys.__stderr__, 'Raising KeyboardInterrupt' @@ -376,7 +399,7 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): char = '\04' self._running_process.process.stdin.write(char) self._running_process.process.stdin.flush() - elif event.KeyCode in (ord('('), 57): + elif event.KeyCode in (ord('('), 57, 53): # Calltips event.Skip() self.do_calltip() @@ -410,8 +433,8 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): self.input_buffer = new_buffer # Tab-completion elif event.KeyCode == ord('\t'): - last_line = self.input_buffer.split('\n')[-1] - if not re.match(r'^\s*$', last_line): + current_line, current_line_number = self.CurLine + if not re.match(r'^\s*$', current_line): self.complete_current_input() if self.AutoCompActive(): wx.CallAfter(self._popup_completion, create=True) @@ -427,7 +450,7 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): if event.KeyCode in (59, ord('.')): # Intercepting '.' event.Skip() - self._popup_completion(create=True) + wx.CallAfter(self._popup_completion, create=True) else: ConsoleWidget._on_key_up(self, event, skip=skip) @@ -456,13 +479,6 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): # Private API #-------------------------------------------------------------------------- - def _end_system_call(self): - """ Called at the end of a system call. - """ - self._input_state = 'buffering' - self._running_process = False - - def _buffer_flush(self, event): """ Called by the timer to flush the write buffer. diff --git a/IPython/genutils.py b/IPython/genutils.py index b19ae3e..38f93ef 100644 --- a/IPython/genutils.py +++ b/IPython/genutils.py @@ -979,6 +979,38 @@ def get_home_dir(): else: raise HomeDirError,'support for your operating system not implemented.' + +def get_ipython_dir(): + """Get the IPython directory for this platform and user. + + This uses the logic in `get_home_dir` to find the home directory + and the adds either .ipython or _ipython to the end of the path. + """ + if os.name == 'posix': + ipdir_def = '.ipython' + else: + ipdir_def = '_ipython' + home_dir = get_home_dir() + ipdir = os.path.abspath(os.environ.get('IPYTHONDIR', + os.path.join(home_dir,ipdir_def))) + return ipdir + +def get_security_dir(): + """Get the IPython security directory. + + This directory is the default location for all security related files, + including SSL/TLS certificates and FURL files. + + If the directory does not exist, it is created with 0700 permissions. + If it exists, permissions are set to 0700. + """ + security_dir = os.path.join(get_ipython_dir(), 'security') + if not os.path.isdir(security_dir): + os.mkdir(security_dir, 0700) + else: + os.chmod(security_dir, 0700) + return security_dir + #**************************************************************************** # strings and text diff --git a/IPython/kernel/config/__init__.py b/IPython/kernel/config/__init__.py index efc8d0a..e24c5c9 100644 --- a/IPython/kernel/config/__init__.py +++ b/IPython/kernel/config/__init__.py @@ -15,17 +15,15 @@ __docformat__ = "restructuredtext en" # Imports #------------------------------------------------------------------------------- +from os.path import join as pjoin + from IPython.external.configobj import ConfigObj from IPython.config.api import ConfigObjManager -from IPython.config.cutils import get_ipython_dir +from IPython.genutils import get_ipython_dir, get_security_dir default_kernel_config = ConfigObj() -try: - ipython_dir = get_ipython_dir() + '/' -except: - # This will defaults to the cwd - ipython_dir = '' +security_dir = get_security_dir() #------------------------------------------------------------------------------- # Engine Configuration @@ -33,7 +31,7 @@ except: engine_config = dict( logfile = '', # Empty means log to stdout - furl_file = ipython_dir + 'ipcontroller-engine.furl' + furl_file = pjoin(security_dir, 'ipcontroller-engine.furl') ) #------------------------------------------------------------------------------- @@ -69,10 +67,10 @@ controller_config = dict( port = 0, # 0 means pick a port for me location = '', # Empty string means try to set automatically secure = True, - cert_file = ipython_dir + 'ipcontroller-engine.pem', + cert_file = pjoin(security_dir, 'ipcontroller-engine.pem'), ), engine_fc_interface = 'IPython.kernel.enginefc.IFCControllerBase', - engine_furl_file = ipython_dir + 'ipcontroller-engine.furl', + engine_furl_file = pjoin(security_dir, 'ipcontroller-engine.furl'), controller_interfaces = dict( # multiengine = dict( @@ -83,12 +81,12 @@ controller_config = dict( task = dict( controller_interface = 'IPython.kernel.task.ITaskController', fc_interface = 'IPython.kernel.taskfc.IFCTaskController', - furl_file = ipython_dir + 'ipcontroller-tc.furl' + furl_file = pjoin(security_dir, 'ipcontroller-tc.furl') ), multiengine = dict( controller_interface = 'IPython.kernel.multiengine.IMultiEngine', fc_interface = 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine', - furl_file = ipython_dir + 'ipcontroller-mec.furl' + furl_file = pjoin(security_dir, 'ipcontroller-mec.furl') ) ), @@ -97,7 +95,7 @@ controller_config = dict( port = 0, # 0 means pick a port for me location = '', # Empty string means try to set automatically secure = True, - cert_file = ipython_dir + 'ipcontroller-client.pem' + cert_file = pjoin(security_dir, 'ipcontroller-client.pem') ) ) @@ -108,10 +106,10 @@ controller_config = dict( client_config = dict( client_interfaces = dict( task = dict( - furl_file = ipython_dir + 'ipcontroller-tc.furl' + furl_file = pjoin(security_dir, 'ipcontroller-tc.furl') ), multiengine = dict( - furl_file = ipython_dir + 'ipcontroller-mec.furl' + furl_file = pjoin(security_dir, 'ipcontroller-mec.furl') ) ) ) diff --git a/IPython/kernel/controllerservice.py b/IPython/kernel/controllerservice.py index c341da6..3fe5f41 100644 --- a/IPython/kernel/controllerservice.py +++ b/IPython/kernel/controllerservice.py @@ -50,7 +50,7 @@ from IPython.kernel.engineservice import \ IEngineSerialized, \ IEngineQueued -from IPython.config import cutils +from IPython.genutils import get_ipython_dir from IPython.kernel import codeutil #------------------------------------------------------------------------------- @@ -170,7 +170,7 @@ class ControllerService(object, service.Service): def _getEngineInfoLogFile(self): # Store all logs inside the ipython directory - ipdir = cutils.get_ipython_dir() + ipdir = get_ipython_dir() pjoin = os.path.join logdir_base = pjoin(ipdir,'log') if not os.path.isdir(logdir_base): diff --git a/IPython/kernel/core/interpreter.py b/IPython/kernel/core/interpreter.py index f7d906c..bca170b 100644 --- a/IPython/kernel/core/interpreter.py +++ b/IPython/kernel/core/interpreter.py @@ -680,6 +680,13 @@ class Interpreter(object): # how trailing whitespace is handled, but this seems to work. python = python.strip() + # The compiler module does not like unicode. We need to convert + # it encode it: + if isinstance(python, unicode): + # Use the utf-8-sig BOM so the compiler detects this a UTF-8 + # encode string. + python = '\xef\xbb\xbf' + python.encode('utf-8') + # The compiler module will parse the code into an abstract syntax tree. ast = compiler.parse(python) diff --git a/IPython/kernel/core/tests/test_interpreter.py b/IPython/kernel/core/tests/test_interpreter.py new file mode 100644 index 0000000..3efc4f2 --- /dev/null +++ b/IPython/kernel/core/tests/test_interpreter.py @@ -0,0 +1,26 @@ +# encoding: utf-8 + +"""This file contains unittests for the interpreter.py module.""" + +__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 +#----------------------------------------------------------------------------- + +from IPython.kernel.core.interpreter import Interpreter + +def test_unicode(): + """ Test unicode handling with the interpreter. + """ + i = Interpreter() + i.execute_python(u'print "ù"') + i.execute_python('print "ù"') + diff --git a/IPython/kernel/scripts/ipcluster.py b/IPython/kernel/scripts/ipcluster.py index afcd53d..08e8c9a 100644 --- a/IPython/kernel/scripts/ipcluster.py +++ b/IPython/kernel/scripts/ipcluster.py @@ -93,7 +93,7 @@ from subprocess import Popen,call # IPython imports #--------------------------------------------------------------------------- from IPython.tools import utils -from IPython.config import cutils +from IPython.genutils import get_ipython_dir #--------------------------------------------------------------------------- # Normal code begins @@ -180,7 +180,7 @@ def clusterLocal(opt,arg): """Start a cluster on the local machine.""" # Store all logs inside the ipython directory - ipdir = cutils.get_ipython_dir() + ipdir = get_ipython_dir() pjoin = os.path.join logfile = opt.logfile @@ -256,6 +256,24 @@ def clusterLocal(opt,arg): def clusterRemote(opt,arg): """Start a remote cluster over SSH""" + # B. Granger, 9/3/08 + # The launching of a remote cluster using SSH and a clusterfile + # is broken. Because it won't be fixed before the 0.9 release, + # we are removing it. For now, we just print a message to the + # user and abort. + + print """The launching of a remote IPython cluster using SSL +and a clusterfile has been removed in this release. +It has been broken for a while and we are in the process +of building a new process management system that will be +used to provide a more robust way of starting an IPython +cluster. + +For now remote clusters have to be launched using ipcontroller +and ipengine separately. + """ + sys.exit(1) + # Load the remote cluster configuration clConfig = {} execfile(opt.clusterfile,clConfig) @@ -265,7 +283,7 @@ def clusterRemote(opt,arg): sshx = clConfig.get('sshx',os.environ.get('IPYTHON_SSHX','sshx')) # Store all logs inside the ipython directory - ipdir = cutils.get_ipython_dir() + ipdir = get_ipython_dir() pjoin = os.path.join logfile = opt.logfile @@ -311,6 +329,11 @@ def clusterRemote(opt,arg): def main(): """Main driver for the two big options: local or remote cluster.""" + if sys.platform=='win32': + print """ipcluster does not work on Microsoft Windows. Please start +your IPython cluster using the ipcontroller and ipengine scripts.""" + sys.exit(1) + opt,arg = parse_args() clusterfile = opt.clusterfile diff --git a/IPython/kernel/scripts/ipengine.py b/IPython/kernel/scripts/ipengine.py index ca5bcba..97fd2ca 100644 --- a/IPython/kernel/scripts/ipengine.py +++ b/IPython/kernel/scripts/ipengine.py @@ -105,6 +105,7 @@ def start_engine(): # register_engine to tell the controller we are ready to do work engine_connector = EngineConnector(tub_service) furl_file = kernel_config['engine']['furl_file'] + log.msg("Using furl file: %s" % furl_file) d = engine_connector.connect_to_controller(engine_service, furl_file) d.addErrback(lambda _: reactor.stop()) diff --git a/IPython/kernel/tests/engineservicetest.py b/IPython/kernel/tests/engineservicetest.py index afd28b4..4a1c6f8 100644 --- a/IPython/kernel/tests/engineservicetest.py +++ b/IPython/kernel/tests/engineservicetest.py @@ -245,7 +245,7 @@ class IEngineSerializedTestCase(object): self.assert_(es.IEngineSerialized.providedBy(self.engine)) def testIEngineSerializedInterfaceMethods(self): - """Does self.engine have the methods and attributes in IEngireCore.""" + """Does self.engine have the methods and attributes in IEngineCore.""" for m in list(es.IEngineSerialized): self.assert_(hasattr(self.engine, m)) @@ -288,7 +288,7 @@ class IEngineQueuedTestCase(object): self.assert_(es.IEngineQueued.providedBy(self.engine)) def testIEngineQueuedInterfaceMethods(self): - """Does self.engine have the methods and attributes in IEngireQueued.""" + """Does self.engine have the methods and attributes in IEngineQueued.""" for m in list(es.IEngineQueued): self.assert_(hasattr(self.engine, m)) @@ -326,7 +326,7 @@ class IEnginePropertiesTestCase(object): self.assert_(es.IEngineProperties.providedBy(self.engine)) def testIEnginePropertiesInterfaceMethods(self): - """Does self.engine have the methods and attributes in IEngireProperties.""" + """Does self.engine have the methods and attributes in IEngineProperties.""" for m in list(es.IEngineProperties): self.assert_(hasattr(self.engine, m)) diff --git a/IPython/testing/plugin/Makefile b/IPython/testing/plugin/Makefile index 878946c..9d8157a 100644 --- a/IPython/testing/plugin/Makefile +++ b/IPython/testing/plugin/Makefile @@ -1,6 +1,5 @@ # Set this prefix to where you want to install the plugin -PREFIX=~/usr/local -PREFIX=~/tmp/local +PREFIX=/usr/local NOSE0=nosetests -vs --with-doctest --doctest-tests --detailed-errors NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt \ diff --git a/IPython/tests/__init__.py b/IPython/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/tests/__init__.py diff --git a/IPython/tests/test_genutils.py b/IPython/tests/test_genutils.py new file mode 100644 index 0000000..9072ec1 --- /dev/null +++ b/IPython/tests/test_genutils.py @@ -0,0 +1,32 @@ +# encoding: utf-8 + +"""Tests for genutils.py""" + +__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 +#----------------------------------------------------------------------------- + +from IPython import genutils + + +def test_get_home_dir(): + """Make sure we can get the home directory.""" + home_dir = genutils.get_home_dir() + +def test_get_ipython_dir(): + """Make sure we can get the ipython directory.""" + ipdir = genutils.get_ipython_dir() + +def test_get_security_dir(): + """Make sure we can get the ipython/security directory.""" + sdir = genutils.get_security_dir() + \ No newline at end of file diff --git a/docs/examples/kernel/asynctask1.py b/docs/examples/kernel/asynctask1.py deleted file mode 100644 index 2b0ca34..0000000 --- a/docs/examples/kernel/asynctask1.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -# This example shows how the AsynTaskClient can be used -# This example is currently broken - -from twisted.internet import reactor, defer -from IPython.kernel import asyncclient - -mec = asyncclient.AsyncMultiEngineClient(('localhost', 10105)) -tc = asyncclient.AsyncTaskClient(('localhost',10113)) - -cmd1 = """\ -a = 5 -b = 10*d -c = a*b*d -""" - -t1 = asyncclient.Task(cmd1, clear_before=False, clear_after=True, pull=['a','b','c']) - -d = mec.push(dict(d=30)) - -def raise_and_print(tr): - tr.raiseException() - print "a, b: ", tr.ns.a, tr.ns.b - return tr - -d.addCallback(lambda _: tc.run(t1)) -d.addCallback(lambda tid: tc.get_task_result(tid,block=True)) -d.addCallback(raise_and_print) -d.addCallback(lambda _: reactor.stop()) -reactor.run() diff --git a/docs/examples/kernel/mcpricer.py b/docs/examples/kernel/mcpricer.py index 72206d0..2dac9f1 100644 --- a/docs/examples/kernel/mcpricer.py +++ b/docs/examples/kernel/mcpricer.py @@ -40,3 +40,4 @@ def main(): if __name__ == '__main__': main() + diff --git a/docs/examples/kernel/rmt.ipy b/docs/examples/kernel/rmt.ipy index c4f4063..ab47c5e 100644 --- a/docs/examples/kernel/rmt.ipy +++ b/docs/examples/kernel/rmt.ipy @@ -53,5 +53,5 @@ if __name__ == '__main__': %timeit -n1 -r1 parallelDiffs(rc, nmats, matsize) # Uncomment these to plot the histogram - import pylab - pylab.hist(parallelDiffs(rc,matsize,matsize)) + # import pylab + # pylab.hist(parallelDiffs(rc,matsize,matsize)) diff --git a/scripts/ipython_win_post_install.py b/scripts/ipython_win_post_install.py index ad7dab2..fb1c2bc 100755 --- a/scripts/ipython_win_post_install.py +++ b/scripts/ipython_win_post_install.py @@ -2,6 +2,7 @@ """Windows-specific part of the installation""" import os, sys, shutil +pjoin = os.path.join def mkshortcut(target,description,link_file,*args,**kw): """make a shortcut if it doesn't exist, and register its creation""" @@ -11,69 +12,85 @@ def mkshortcut(target,description,link_file,*args,**kw): def install(): """Routine to be run by the win32 installer with the -install switch.""" - + from IPython.Release import version - + # Get some system constants prefix = sys.prefix - python = prefix + r'\python.exe' - # Lookup path to common startmenu ... - ip_dir = get_special_folder_path('CSIDL_COMMON_PROGRAMS') + r'\IPython' + python = pjoin(prefix, 'python.exe') - # Some usability warnings at installation time. I don't want them at the - # top-level, so they don't appear if the user is uninstalling. - try: - import ctypes - except ImportError: - print ('To take full advantage of IPython, you need ctypes from:\n' - 'http://sourceforge.net/projects/ctypes') - - try: - import win32con - except ImportError: - print ('To take full advantage of IPython, you need pywin32 from:\n' - 'http://starship.python.net/crew/mhammond/win32/Downloads.html') - - try: - import readline - except ImportError: - print ('To take full advantage of IPython, you need readline from:\n' - 'https://launchpad.net/pyreadline') - - ipybase = '"' + prefix + r'\scripts\ipython"' + # Lookup path to common startmenu ... + ip_start_menu = pjoin(get_special_folder_path('CSIDL_COMMON_PROGRAMS'), 'IPython') # Create IPython entry ... - if not os.path.isdir(ip_dir): - os.mkdir(ip_dir) - directory_created(ip_dir) - - # Create program shortcuts ... - f = ip_dir + r'\IPython.lnk' - a = ipybase - mkshortcut(python,'IPython',f,a) - - f = ip_dir + r'\pysh.lnk' - a = ipybase+' -p sh' - mkshortcut(python,'IPython (command prompt mode)',f,a) - - f = ip_dir + r'\pylab.lnk' - a = ipybase+' -pylab' - mkshortcut(python,'IPython (PyLab mode)',f,a) - - f = ip_dir + r'\scipy.lnk' - a = ipybase+' -pylab -p scipy' - mkshortcut(python,'IPython (scipy profile)',f,a) - + if not os.path.isdir(ip_start_menu): + os.mkdir(ip_start_menu) + directory_created(ip_start_menu) + + # Create .py and .bat files to make things available from + # the Windows command line. Thanks to the Twisted project + # for this logic! + programs = [ + 'ipython', + 'iptest', + 'ipcontroller', + 'ipengine', + 'ipcluster', + 'ipythonx', + 'ipython-wx', + 'irunner' + ] + scripts = pjoin(prefix,'scripts') + for program in programs: + raw = pjoin(scripts, program) + bat = raw + '.bat' + py = raw + '.py' + # Create .py versions of the scripts + shutil.copy(raw, py) + # Create .bat files for each of the scripts + bat_file = file(bat,'w') + bat_file.write("@%s %s %%*" % (python, py)) + bat_file.close() + + # Now move onto setting the Start Menu up + ipybase = pjoin(scripts, 'ipython') + + link = pjoin(ip_start_menu, 'IPython.lnk') + cmd = '"%s"' % ipybase + mkshortcut(python,'IPython',link,cmd) + + link = pjoin(ip_start_menu, 'pysh.lnk') + cmd = '"%s" -p sh' % ipybase + mkshortcut(python,'IPython (command prompt mode)',link,cmd) + + link = pjoin(ip_start_menu, 'pylab.lnk') + cmd = '"%s" -pylab' % ipybase + mkshortcut(python,'IPython (PyLab mode)',link,cmd) + + link = pjoin(ip_start_menu, 'scipy.lnk') + cmd = '"%s" -pylab -p scipy' % ipybase + mkshortcut(python,'IPython (scipy profile)',link,cmd) + + link = pjoin(ip_start_menu, 'IPython test suite.lnk') + cmd = '"%s" -vv' % pjoin(scripts, 'iptest') + mkshortcut(python,'Run the IPython test suite',link,cmd) + + link = pjoin(ip_start_menu, 'ipcontroller.lnk') + cmd = '"%s" -xy' % pjoin(scripts, 'ipcontroller') + mkshortcut(python,'IPython controller',link,cmd) + + link = pjoin(ip_start_menu, 'ipengine.lnk') + cmd = '"%s"' % pjoin(scripts, 'ipengine') + mkshortcut(python,'IPython engine',link,cmd) + # Create documentation shortcuts ... t = prefix + r'\share\doc\ipython\manual\ipython.pdf' - f = ip_dir + r'\Manual in PDF.lnk' + f = ip_start_menu + r'\Manual in PDF.lnk' mkshortcut(t,r'IPython Manual - PDF-Format',f) - + t = prefix + r'\share\doc\ipython\manual\html\index.html' - f = ip_dir + r'\Manual in HTML.lnk' + f = ip_start_menu + r'\Manual in HTML.lnk' mkshortcut(t,'IPython Manual - HTML-Format',f) - - # make ipython.py - shutil.copy(prefix + r'\scripts\ipython', prefix + r'\scripts\ipython.py') + def remove(): """Routine to be run by the win32 installer with the -remove switch.""" diff --git a/setupbase.py b/setupbase.py index 28d8017..cd75644 100644 --- a/setupbase.py +++ b/setupbase.py @@ -115,6 +115,7 @@ def find_packages(): add_package(packages, 'kernel', config=True, tests=True, scripts=True) add_package(packages, 'kernel.core', config=True, tests=True) add_package(packages, 'testing', tests=True) + add_package(packages, 'tests') add_package(packages, 'testing.plugin', tests=False) add_package(packages, 'tools', tests=True) add_package(packages, 'UserConfig') diff --git a/setupegg.py b/setupegg.py index c59ea94..0924b19 100755 --- a/setupegg.py +++ b/setupegg.py @@ -1,14 +1,8 @@ #!/usr/bin/env python """Wrapper to run setup.py using setuptools.""" -import os import sys -# Add my local path to sys.path -home = os.environ['HOME'] -sys.path.insert(0,'%s/usr/local/lib/python%s/site-packages' % - (home,sys.version[:3])) - # now, import setuptools and call the actual setup import setuptools # print sys.argv