From 6383913647695ec86e04544a32234d378ccef175 2009-03-20 11:31:44 From: Gael Varoquaux Date: 2009-03-20 11:31:44 Subject: [PATCH] BUG: Integrate bug fixes from Enthought --- diff --git a/IPython/frontend/linefrontendbase.py b/IPython/frontend/linefrontendbase.py index 5aedb72..eb4e319 100644 --- a/IPython/frontend/linefrontendbase.py +++ b/IPython/frontend/linefrontendbase.py @@ -149,8 +149,12 @@ class LineFrontEndBase(FrontEndBase): 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', + # complete (except if '\' was used). + # This should probably be done in a different place (like + # maybe 'prefilter_input' method? For now, this works. + clean_string = string.rstrip('\n') + if not clean_string.endswith('\\'): clean_string +='\n\n' + is_complete = codeop.compile_command(clean_string, "", "exec") self.release_output() except Exception, e: @@ -270,15 +274,15 @@ class LineFrontEndBase(FrontEndBase): symbols_per_line = max(1, chars_per_line/max_len) pos = 1 - buf = [] + completion_string = [] for symbol in possibilities: if pos < symbols_per_line: - buf.append(symbol.ljust(max_len)) + completion_string.append(symbol.ljust(max_len)) pos += 1 else: - buf.append(symbol.rstrip() + '\n') + completion_string.append(symbol.rstrip() + '\n') pos = 1 - self.write(''.join(buf)) + self.write(''.join(completion_string)) self.new_prompt(self.input_prompt_template.substitute( number=self.last_result['number'] + 1)) self.input_buffer = new_line diff --git a/IPython/frontend/prefilterfrontend.py b/IPython/frontend/prefilterfrontend.py index 7b44ead..1c0902f 100644 --- a/IPython/frontend/prefilterfrontend.py +++ b/IPython/frontend/prefilterfrontend.py @@ -71,6 +71,13 @@ class PrefilterFrontEnd(LineFrontEndBase): ipython0: an optional ipython0 instance to use for command prefiltering and completion. """ + # This is a hack to avoid the IPython exception hook to trigger + # on exceptions (https://bugs.launchpad.net/bugs/337105) + # XXX: This is horrible: module-leve monkey patching -> side + # effects. + from IPython import iplib + iplib.InteractiveShell.isthreaded = True + LineFrontEndBase.__init__(self, *args, **kwargs) self.shell.output_trap = RedirectorOutputTrap( out_callback=self.write, @@ -184,7 +191,6 @@ class PrefilterFrontEnd(LineFrontEndBase): # FIXME: This should be factored out in the linefrontendbase # method. word = self._get_completion_text(line) - word = line.split('\n')[-1].split(' ')[-1] print 'Completion', word completions = self.ipython0.complete(word) # FIXME: The proper sort should be done in the complete method. diff --git a/IPython/frontend/tests/test_linefrontend.py b/IPython/frontend/tests/test_linefrontend.py new file mode 100644 index 0000000..11d3209 --- /dev/null +++ b/IPython/frontend/tests/test_linefrontend.py @@ -0,0 +1,38 @@ +# encoding: utf-8 +""" +Test the LineFrontEnd +""" + +__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. +#------------------------------------------------------------------------------- + +from IPython.frontend.linefrontendbase import LineFrontEndBase +from copy import deepcopy + +class ConcreteLineFrontEnd(LineFrontEndBase): + """ A concrete class to test the LineFrontEndBase. + """ + def capture_output(self): + pass + + def release_output(self): + pass + + +def test_is_complete(): + """ Tests line completion heuristic. + """ + frontend = ConcreteLineFrontEnd() + assert not frontend.is_complete('for x in \\') + assert not frontend.is_complete('for x in (1, ):') + assert frontend.is_complete('for x in (1, ):\n pass') + + +if __name__ == '__main__': + test_is_complete() diff --git a/IPython/frontend/tests/test_prefilterfrontend.py b/IPython/frontend/tests/test_prefilterfrontend.py index 665a2c5..5f22439 100644 --- a/IPython/frontend/tests/test_prefilterfrontend.py +++ b/IPython/frontend/tests/test_prefilterfrontend.py @@ -15,9 +15,27 @@ __docformat__ = "restructuredtext en" from cStringIO import StringIO import string +from nose.tools import assert_equal + from IPython.ipapi import get as get_ipython0 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd -from copy import deepcopy +from copy import copy, deepcopy + +def safe_deepcopy(d): + """ Deep copy every key of the given dict, when possible. Elsewhere + do a copy. + """ + copied_d = dict() + for key, value in d.iteritems(): + try: + copied_d[key] = deepcopy(value) + except: + try: + copied_d[key] = copy(value) + except: + copied_d[key] = value + return copied_d + class TestPrefilterFrontEnd(PrefilterFrontEnd): @@ -26,16 +44,8 @@ class TestPrefilterFrontEnd(PrefilterFrontEnd): banner = '' def __init__(self): - ipython0 = get_ipython0().IP self.out = StringIO() - PrefilterFrontEnd.__init__(self, ipython0=ipython0) - # Clean up the namespace for isolation between tests - user_ns = self.ipython0.user_ns - # We need to keep references to things so that they don't - # get garbage collected (this stinks). - self.shadow_ns = dict() - for i in self.ipython0.magic_who_ls(): - self.shadow_ns[i] = user_ns.pop(i) + PrefilterFrontEnd.__init__(self) # Some more code for isolation (yeah, crazy) self._on_enter() self.out.flush() @@ -52,17 +62,31 @@ class TestPrefilterFrontEnd(PrefilterFrontEnd): def isolate_ipython0(func): """ Decorator to isolate execution that involves an iptyhon0. + + Notes + ------ + + Apply only to functions with no arguments. Nose skips functions + with arguments. """ - def my_func(*args, **kwargs): - ipython0 = get_ipython0().IP - user_ns = deepcopy(ipython0.user_ns) - global_ns = deepcopy(ipython0.global_ns) + def my_func(): + iplib = get_ipython0() + if iplib is None: + return func() + ipython0 = iplib.IP + global_ns = safe_deepcopy(ipython0.user_global_ns) + user_ns = safe_deepcopy(ipython0.user_ns) try: - func(*args, **kwargs) + out = func() finally: ipython0.user_ns = user_ns - ipython0.global_ns = global_ns + ipython0.user_global_ns = global_ns + # Undo the hack at creation of PrefilterFrontEnd + from IPython import iplib + iplib.InteractiveShell.isthreaded = False + return out + my_func.__name__ = func.__name__ return my_func @@ -74,7 +98,7 @@ def test_execution(): f.input_buffer = 'print 1' f._on_enter() out_value = f.out.getvalue() - assert out_value == '1\n' + assert_equal(out_value, '1\n') @isolate_ipython0 @@ -87,20 +111,20 @@ def test_multiline(): f.input_buffer += 'print 1' f._on_enter() out_value = f.out.getvalue() - assert out_value == '' + assert_equal(out_value, '') f._on_enter() out_value = f.out.getvalue() - assert out_value == '1\n' + assert_equal(out_value, '1\n') f = TestPrefilterFrontEnd() f.input_buffer='(1 +' f._on_enter() f.input_buffer += '0)' f._on_enter() out_value = f.out.getvalue() - assert out_value == '' + assert_equal(out_value, '') f._on_enter() out_value = f.out.getvalue() - assert out_value == '1\n' + assert_equal(out_value, '1\n') @isolate_ipython0 @@ -113,13 +137,13 @@ def test_capture(): 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()' f._on_enter() out_value = f.out.getvalue() - assert out_value == '1' + assert_equal(out_value, '1') f = TestPrefilterFrontEnd() f.input_buffer = \ 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()' f._on_enter() out_value = f.out.getvalue() - assert out_value == '1' + assert_equal(out_value, '1') @isolate_ipython0 @@ -132,7 +156,7 @@ def test_magic(): f.input_buffer += '%who' f._on_enter() out_value = f.out.getvalue() - assert out_value == 'Interactive namespace is empty.\n' + assert_equal(out_value, 'Interactive namespace is empty.\n') @isolate_ipython0 @@ -156,8 +180,8 @@ def test_help(): @isolate_ipython0 -def test_completion(): - """ Test command-line completion. +def test_completion_simple(): + """ Test command-line completion on trivial examples. """ f = TestPrefilterFrontEnd() f.input_buffer = 'zzza = 1' @@ -167,8 +191,47 @@ def test_completion(): f.input_buffer = 'zz' f.complete_current_input() out_value = f.out.getvalue() - assert out_value == '\nzzza zzzb ' - assert f.input_buffer == 'zzz' + assert_equal(out_value, '\nzzza zzzb ') + assert_equal(f.input_buffer, 'zzz') + + +@isolate_ipython0 +def test_completion_parenthesis(): + """ Test command-line completion when a parenthesis is open. + """ + f = TestPrefilterFrontEnd() + f.input_buffer = 'zzza = 1' + f._on_enter() + f.input_buffer = 'zzzb = 2' + f._on_enter() + f.input_buffer = 'map(zz' + f.complete_current_input() + out_value = f.out.getvalue() + assert_equal(out_value, '\nzzza zzzb ') + assert_equal(f.input_buffer, 'map(zzz') + + +@isolate_ipython0 +def test_completion_indexing(): + """ Test command-line completion when indexing on objects. + """ + f = TestPrefilterFrontEnd() + f.input_buffer = 'a = [0]' + f._on_enter() + f.input_buffer = 'a[0].' + f.complete_current_input() + assert_equal(f.input_buffer, 'a[0].__') + + +@isolate_ipython0 +def test_completion_equal(): + """ Test command-line completion when the delimiter is "=", not " ". + """ + f = TestPrefilterFrontEnd() + f.input_buffer = 'a=1.' + f.complete_current_input() + assert_equal(f.input_buffer, 'a=1.__') + if __name__ == '__main__': @@ -177,4 +240,5 @@ if __name__ == '__main__': test_execution() test_multiline() test_capture() - test_completion() + test_completion_simple() + test_completion_complex() diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py index dc10e23..4c09681 100644 --- a/IPython/frontend/wx/wx_frontend.py +++ b/IPython/frontend/wx/wx_frontend.py @@ -250,11 +250,8 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): if (self.AutoCompActive() and line and not line[-1] == '.') \ or create==True: suggestion, completions = self.complete(line) - offset=0 if completions: - complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]') - residual = complete_sep.split(line)[-1] - offset = len(residual) + offset = len(self._get_completion_text(line)) self.pop_completion(completions, offset=offset) if self.debug: print >>sys.__stdout__, completions