Show More
@@ -0,0 +1,38 b'' | |||||
|
1 | # encoding: utf-8 | |||
|
2 | """ | |||
|
3 | Test the LineFrontEnd | |||
|
4 | """ | |||
|
5 | ||||
|
6 | __docformat__ = "restructuredtext en" | |||
|
7 | ||||
|
8 | #------------------------------------------------------------------------------- | |||
|
9 | # Copyright (C) 2008 The IPython Development Team | |||
|
10 | # | |||
|
11 | # Distributed under the terms of the BSD License. The full license is | |||
|
12 | # in the file COPYING, distributed as part of this software. | |||
|
13 | #------------------------------------------------------------------------------- | |||
|
14 | ||||
|
15 | from IPython.frontend.linefrontendbase import LineFrontEndBase | |||
|
16 | from copy import deepcopy | |||
|
17 | ||||
|
18 | class ConcreteLineFrontEnd(LineFrontEndBase): | |||
|
19 | """ A concrete class to test the LineFrontEndBase. | |||
|
20 | """ | |||
|
21 | def capture_output(self): | |||
|
22 | pass | |||
|
23 | ||||
|
24 | def release_output(self): | |||
|
25 | pass | |||
|
26 | ||||
|
27 | ||||
|
28 | def test_is_complete(): | |||
|
29 | """ Tests line completion heuristic. | |||
|
30 | """ | |||
|
31 | frontend = ConcreteLineFrontEnd() | |||
|
32 | assert not frontend.is_complete('for x in \\') | |||
|
33 | assert not frontend.is_complete('for x in (1, ):') | |||
|
34 | assert frontend.is_complete('for x in (1, ):\n pass') | |||
|
35 | ||||
|
36 | ||||
|
37 | if __name__ == '__main__': | |||
|
38 | test_is_complete() |
@@ -149,8 +149,12 b' class LineFrontEndBase(FrontEndBase):' | |||||
149 | self.capture_output() |
|
149 | self.capture_output() | |
150 | try: |
|
150 | try: | |
151 | # Add line returns here, to make sure that the statement is |
|
151 | # Add line returns here, to make sure that the statement is | |
152 | # complete. |
|
152 | # complete (except if '\' was used). | |
153 | is_complete = codeop.compile_command(string.rstrip() + '\n\n', |
|
153 | # This should probably be done in a different place (like | |
|
154 | # maybe 'prefilter_input' method? For now, this works. | |||
|
155 | clean_string = string.rstrip('\n') | |||
|
156 | if not clean_string.endswith('\\'): clean_string +='\n\n' | |||
|
157 | is_complete = codeop.compile_command(clean_string, | |||
154 | "<string>", "exec") |
|
158 | "<string>", "exec") | |
155 | self.release_output() |
|
159 | self.release_output() | |
156 | except Exception, e: |
|
160 | except Exception, e: | |
@@ -270,15 +274,15 b' class LineFrontEndBase(FrontEndBase):' | |||||
270 | symbols_per_line = max(1, chars_per_line/max_len) |
|
274 | symbols_per_line = max(1, chars_per_line/max_len) | |
271 |
|
275 | |||
272 | pos = 1 |
|
276 | pos = 1 | |
273 | buf = [] |
|
277 | completion_string = [] | |
274 | for symbol in possibilities: |
|
278 | for symbol in possibilities: | |
275 | if pos < symbols_per_line: |
|
279 | if pos < symbols_per_line: | |
276 |
|
|
280 | completion_string.append(symbol.ljust(max_len)) | |
277 | pos += 1 |
|
281 | pos += 1 | |
278 | else: |
|
282 | else: | |
279 |
|
|
283 | completion_string.append(symbol.rstrip() + '\n') | |
280 | pos = 1 |
|
284 | pos = 1 | |
281 |
self.write(''.join( |
|
285 | self.write(''.join(completion_string)) | |
282 | self.new_prompt(self.input_prompt_template.substitute( |
|
286 | self.new_prompt(self.input_prompt_template.substitute( | |
283 | number=self.last_result['number'] + 1)) |
|
287 | number=self.last_result['number'] + 1)) | |
284 | self.input_buffer = new_line |
|
288 | self.input_buffer = new_line |
@@ -71,6 +71,13 b' class PrefilterFrontEnd(LineFrontEndBase):' | |||||
71 | ipython0: an optional ipython0 instance to use for command |
|
71 | ipython0: an optional ipython0 instance to use for command | |
72 | prefiltering and completion. |
|
72 | prefiltering and completion. | |
73 | """ |
|
73 | """ | |
|
74 | # This is a hack to avoid the IPython exception hook to trigger | |||
|
75 | # on exceptions (https://bugs.launchpad.net/bugs/337105) | |||
|
76 | # XXX: This is horrible: module-leve monkey patching -> side | |||
|
77 | # effects. | |||
|
78 | from IPython import iplib | |||
|
79 | iplib.InteractiveShell.isthreaded = True | |||
|
80 | ||||
74 | LineFrontEndBase.__init__(self, *args, **kwargs) |
|
81 | LineFrontEndBase.__init__(self, *args, **kwargs) | |
75 | self.shell.output_trap = RedirectorOutputTrap( |
|
82 | self.shell.output_trap = RedirectorOutputTrap( | |
76 | out_callback=self.write, |
|
83 | out_callback=self.write, | |
@@ -184,7 +191,6 b' class PrefilterFrontEnd(LineFrontEndBase):' | |||||
184 | # FIXME: This should be factored out in the linefrontendbase |
|
191 | # FIXME: This should be factored out in the linefrontendbase | |
185 | # method. |
|
192 | # method. | |
186 | word = self._get_completion_text(line) |
|
193 | word = self._get_completion_text(line) | |
187 | word = line.split('\n')[-1].split(' ')[-1] |
|
|||
188 | print 'Completion', word |
|
194 | print 'Completion', word | |
189 | completions = self.ipython0.complete(word) |
|
195 | completions = self.ipython0.complete(word) | |
190 | # FIXME: The proper sort should be done in the complete method. |
|
196 | # FIXME: The proper sort should be done in the complete method. |
@@ -15,9 +15,27 b' __docformat__ = "restructuredtext en"' | |||||
15 | from cStringIO import StringIO |
|
15 | from cStringIO import StringIO | |
16 | import string |
|
16 | import string | |
17 |
|
17 | |||
|
18 | from nose.tools import assert_equal | |||
|
19 | ||||
18 | from IPython.ipapi import get as get_ipython0 |
|
20 | from IPython.ipapi import get as get_ipython0 | |
19 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd |
|
21 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd | |
20 | from copy import deepcopy |
|
22 | from copy import copy, deepcopy | |
|
23 | ||||
|
24 | def safe_deepcopy(d): | |||
|
25 | """ Deep copy every key of the given dict, when possible. Elsewhere | |||
|
26 | do a copy. | |||
|
27 | """ | |||
|
28 | copied_d = dict() | |||
|
29 | for key, value in d.iteritems(): | |||
|
30 | try: | |||
|
31 | copied_d[key] = deepcopy(value) | |||
|
32 | except: | |||
|
33 | try: | |||
|
34 | copied_d[key] = copy(value) | |||
|
35 | except: | |||
|
36 | copied_d[key] = value | |||
|
37 | return copied_d | |||
|
38 | ||||
21 |
|
39 | |||
22 | class TestPrefilterFrontEnd(PrefilterFrontEnd): |
|
40 | class TestPrefilterFrontEnd(PrefilterFrontEnd): | |
23 |
|
41 | |||
@@ -26,16 +44,8 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):' | |||||
26 | banner = '' |
|
44 | banner = '' | |
27 |
|
45 | |||
28 | def __init__(self): |
|
46 | def __init__(self): | |
29 | ipython0 = get_ipython0().IP |
|
|||
30 | self.out = StringIO() |
|
47 | self.out = StringIO() | |
31 |
PrefilterFrontEnd.__init__(self |
|
48 | PrefilterFrontEnd.__init__(self) | |
32 | # Clean up the namespace for isolation between tests |
|
|||
33 | user_ns = self.ipython0.user_ns |
|
|||
34 | # We need to keep references to things so that they don't |
|
|||
35 | # get garbage collected (this stinks). |
|
|||
36 | self.shadow_ns = dict() |
|
|||
37 | for i in self.ipython0.magic_who_ls(): |
|
|||
38 | self.shadow_ns[i] = user_ns.pop(i) |
|
|||
39 | # Some more code for isolation (yeah, crazy) |
|
49 | # Some more code for isolation (yeah, crazy) | |
40 | self._on_enter() |
|
50 | self._on_enter() | |
41 | self.out.flush() |
|
51 | self.out.flush() | |
@@ -52,17 +62,31 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):' | |||||
52 |
|
62 | |||
53 | def isolate_ipython0(func): |
|
63 | def isolate_ipython0(func): | |
54 | """ Decorator to isolate execution that involves an iptyhon0. |
|
64 | """ Decorator to isolate execution that involves an iptyhon0. | |
|
65 | ||||
|
66 | Notes | |||
|
67 | ------ | |||
|
68 | ||||
|
69 | Apply only to functions with no arguments. Nose skips functions | |||
|
70 | with arguments. | |||
55 | """ |
|
71 | """ | |
56 |
def my_func( |
|
72 | def my_func(): | |
57 |
ip |
|
73 | iplib = get_ipython0() | |
58 | user_ns = deepcopy(ipython0.user_ns) |
|
74 | if iplib is None: | |
59 | global_ns = deepcopy(ipython0.global_ns) |
|
75 | return func() | |
|
76 | ipython0 = iplib.IP | |||
|
77 | global_ns = safe_deepcopy(ipython0.user_global_ns) | |||
|
78 | user_ns = safe_deepcopy(ipython0.user_ns) | |||
60 | try: |
|
79 | try: | |
61 |
func( |
|
80 | out = func() | |
62 | finally: |
|
81 | finally: | |
63 | ipython0.user_ns = user_ns |
|
82 | ipython0.user_ns = user_ns | |
64 | ipython0.global_ns = global_ns |
|
83 | ipython0.user_global_ns = global_ns | |
|
84 | # Undo the hack at creation of PrefilterFrontEnd | |||
|
85 | from IPython import iplib | |||
|
86 | iplib.InteractiveShell.isthreaded = False | |||
|
87 | return out | |||
65 |
|
88 | |||
|
89 | my_func.__name__ = func.__name__ | |||
66 | return my_func |
|
90 | return my_func | |
67 |
|
91 | |||
68 |
|
92 | |||
@@ -74,7 +98,7 b' def test_execution():' | |||||
74 | f.input_buffer = 'print 1' |
|
98 | f.input_buffer = 'print 1' | |
75 | f._on_enter() |
|
99 | f._on_enter() | |
76 | out_value = f.out.getvalue() |
|
100 | out_value = f.out.getvalue() | |
77 |
assert |
|
101 | assert_equal(out_value, '1\n') | |
78 |
|
102 | |||
79 |
|
103 | |||
80 | @isolate_ipython0 |
|
104 | @isolate_ipython0 | |
@@ -87,20 +111,20 b' def test_multiline():' | |||||
87 | f.input_buffer += 'print 1' |
|
111 | f.input_buffer += 'print 1' | |
88 | f._on_enter() |
|
112 | f._on_enter() | |
89 | out_value = f.out.getvalue() |
|
113 | out_value = f.out.getvalue() | |
90 |
assert |
|
114 | assert_equal(out_value, '') | |
91 | f._on_enter() |
|
115 | f._on_enter() | |
92 | out_value = f.out.getvalue() |
|
116 | out_value = f.out.getvalue() | |
93 |
assert |
|
117 | assert_equal(out_value, '1\n') | |
94 | f = TestPrefilterFrontEnd() |
|
118 | f = TestPrefilterFrontEnd() | |
95 | f.input_buffer='(1 +' |
|
119 | f.input_buffer='(1 +' | |
96 | f._on_enter() |
|
120 | f._on_enter() | |
97 | f.input_buffer += '0)' |
|
121 | f.input_buffer += '0)' | |
98 | f._on_enter() |
|
122 | f._on_enter() | |
99 | out_value = f.out.getvalue() |
|
123 | out_value = f.out.getvalue() | |
100 |
assert |
|
124 | assert_equal(out_value, '') | |
101 | f._on_enter() |
|
125 | f._on_enter() | |
102 | out_value = f.out.getvalue() |
|
126 | out_value = f.out.getvalue() | |
103 |
assert |
|
127 | assert_equal(out_value, '1\n') | |
104 |
|
128 | |||
105 |
|
129 | |||
106 | @isolate_ipython0 |
|
130 | @isolate_ipython0 | |
@@ -113,13 +137,13 b' def test_capture():' | |||||
113 | 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()' |
|
137 | 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()' | |
114 | f._on_enter() |
|
138 | f._on_enter() | |
115 | out_value = f.out.getvalue() |
|
139 | out_value = f.out.getvalue() | |
116 |
assert |
|
140 | assert_equal(out_value, '1') | |
117 | f = TestPrefilterFrontEnd() |
|
141 | f = TestPrefilterFrontEnd() | |
118 | f.input_buffer = \ |
|
142 | f.input_buffer = \ | |
119 | 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()' |
|
143 | 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()' | |
120 | f._on_enter() |
|
144 | f._on_enter() | |
121 | out_value = f.out.getvalue() |
|
145 | out_value = f.out.getvalue() | |
122 |
assert |
|
146 | assert_equal(out_value, '1') | |
123 |
|
147 | |||
124 |
|
148 | |||
125 | @isolate_ipython0 |
|
149 | @isolate_ipython0 | |
@@ -132,7 +156,7 b' def test_magic():' | |||||
132 | f.input_buffer += '%who' |
|
156 | f.input_buffer += '%who' | |
133 | f._on_enter() |
|
157 | f._on_enter() | |
134 | out_value = f.out.getvalue() |
|
158 | out_value = f.out.getvalue() | |
135 |
assert |
|
159 | assert_equal(out_value, 'Interactive namespace is empty.\n') | |
136 |
|
160 | |||
137 |
|
161 | |||
138 | @isolate_ipython0 |
|
162 | @isolate_ipython0 | |
@@ -156,8 +180,8 b' def test_help():' | |||||
156 |
|
180 | |||
157 |
|
181 | |||
158 | @isolate_ipython0 |
|
182 | @isolate_ipython0 | |
159 | def test_completion(): |
|
183 | def test_completion_simple(): | |
160 | """ Test command-line completion. |
|
184 | """ Test command-line completion on trivial examples. | |
161 | """ |
|
185 | """ | |
162 | f = TestPrefilterFrontEnd() |
|
186 | f = TestPrefilterFrontEnd() | |
163 | f.input_buffer = 'zzza = 1' |
|
187 | f.input_buffer = 'zzza = 1' | |
@@ -167,8 +191,47 b' def test_completion():' | |||||
167 | f.input_buffer = 'zz' |
|
191 | f.input_buffer = 'zz' | |
168 | f.complete_current_input() |
|
192 | f.complete_current_input() | |
169 | out_value = f.out.getvalue() |
|
193 | out_value = f.out.getvalue() | |
170 |
assert |
|
194 | assert_equal(out_value, '\nzzza zzzb ') | |
171 |
assert |
|
195 | assert_equal(f.input_buffer, 'zzz') | |
|
196 | ||||
|
197 | ||||
|
198 | @isolate_ipython0 | |||
|
199 | def test_completion_parenthesis(): | |||
|
200 | """ Test command-line completion when a parenthesis is open. | |||
|
201 | """ | |||
|
202 | f = TestPrefilterFrontEnd() | |||
|
203 | f.input_buffer = 'zzza = 1' | |||
|
204 | f._on_enter() | |||
|
205 | f.input_buffer = 'zzzb = 2' | |||
|
206 | f._on_enter() | |||
|
207 | f.input_buffer = 'map(zz' | |||
|
208 | f.complete_current_input() | |||
|
209 | out_value = f.out.getvalue() | |||
|
210 | assert_equal(out_value, '\nzzza zzzb ') | |||
|
211 | assert_equal(f.input_buffer, 'map(zzz') | |||
|
212 | ||||
|
213 | ||||
|
214 | @isolate_ipython0 | |||
|
215 | def test_completion_indexing(): | |||
|
216 | """ Test command-line completion when indexing on objects. | |||
|
217 | """ | |||
|
218 | f = TestPrefilterFrontEnd() | |||
|
219 | f.input_buffer = 'a = [0]' | |||
|
220 | f._on_enter() | |||
|
221 | f.input_buffer = 'a[0].' | |||
|
222 | f.complete_current_input() | |||
|
223 | assert_equal(f.input_buffer, 'a[0].__') | |||
|
224 | ||||
|
225 | ||||
|
226 | @isolate_ipython0 | |||
|
227 | def test_completion_equal(): | |||
|
228 | """ Test command-line completion when the delimiter is "=", not " ". | |||
|
229 | """ | |||
|
230 | f = TestPrefilterFrontEnd() | |||
|
231 | f.input_buffer = 'a=1.' | |||
|
232 | f.complete_current_input() | |||
|
233 | assert_equal(f.input_buffer, 'a=1.__') | |||
|
234 | ||||
172 |
|
235 | |||
173 |
|
236 | |||
174 | if __name__ == '__main__': |
|
237 | if __name__ == '__main__': | |
@@ -177,4 +240,5 b" if __name__ == '__main__':" | |||||
177 | test_execution() |
|
240 | test_execution() | |
178 | test_multiline() |
|
241 | test_multiline() | |
179 | test_capture() |
|
242 | test_capture() | |
180 | test_completion() |
|
243 | test_completion_simple() | |
|
244 | test_completion_complex() |
@@ -250,11 +250,8 b' class WxController(ConsoleWidget, PrefilterFrontEnd):' | |||||
250 | if (self.AutoCompActive() and line and not line[-1] == '.') \ |
|
250 | if (self.AutoCompActive() and line and not line[-1] == '.') \ | |
251 | or create==True: |
|
251 | or create==True: | |
252 | suggestion, completions = self.complete(line) |
|
252 | suggestion, completions = self.complete(line) | |
253 | offset=0 |
|
|||
254 | if completions: |
|
253 | if completions: | |
255 | complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]') |
|
254 | offset = len(self._get_completion_text(line)) | |
256 | residual = complete_sep.split(line)[-1] |
|
|||
257 | offset = len(residual) |
|
|||
258 | self.pop_completion(completions, offset=offset) |
|
255 | self.pop_completion(completions, offset=offset) | |
259 | if self.debug: |
|
256 | if self.debug: | |
260 | print >>sys.__stdout__, completions |
|
257 | print >>sys.__stdout__, completions |
General Comments 0
You need to be logged in to leave comments.
Login now