##// END OF EJS Templates
Merge with upstream
Fernando Perez -
r1569:f15e50b5 merge
parent child Browse files
Show More
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
@@ -1,637 +1,640 b''
1 """Word completion for IPython.
1 """Word completion for IPython.
2
2
3 This module is a fork of the rlcompleter module in the Python standard
3 This module is a fork of the rlcompleter module in the Python standard
4 library. The original enhancements made to rlcompleter have been sent
4 library. The original enhancements made to rlcompleter have been sent
5 upstream and were accepted as of Python 2.3, but we need a lot more
5 upstream and were accepted as of Python 2.3, but we need a lot more
6 functionality specific to IPython, so this module will continue to live as an
6 functionality specific to IPython, so this module will continue to live as an
7 IPython-specific utility.
7 IPython-specific utility.
8
8
9 ---------------------------------------------------------------------------
9 ---------------------------------------------------------------------------
10 Original rlcompleter documentation:
10 Original rlcompleter documentation:
11
11
12 This requires the latest extension to the readline module (the
12 This requires the latest extension to the readline module (the
13 completes keywords, built-ins and globals in __main__; when completing
13 completes keywords, built-ins and globals in __main__; when completing
14 NAME.NAME..., it evaluates (!) the expression up to the last dot and
14 NAME.NAME..., it evaluates (!) the expression up to the last dot and
15 completes its attributes.
15 completes its attributes.
16
16
17 It's very cool to do "import string" type "string.", hit the
17 It's very cool to do "import string" type "string.", hit the
18 completion key (twice), and see the list of names defined by the
18 completion key (twice), and see the list of names defined by the
19 string module!
19 string module!
20
20
21 Tip: to use the tab key as the completion key, call
21 Tip: to use the tab key as the completion key, call
22
22
23 readline.parse_and_bind("tab: complete")
23 readline.parse_and_bind("tab: complete")
24
24
25 Notes:
25 Notes:
26
26
27 - Exceptions raised by the completer function are *ignored* (and
27 - Exceptions raised by the completer function are *ignored* (and
28 generally cause the completion to fail). This is a feature -- since
28 generally cause the completion to fail). This is a feature -- since
29 readline sets the tty device in raw (or cbreak) mode, printing a
29 readline sets the tty device in raw (or cbreak) mode, printing a
30 traceback wouldn't work well without some complicated hoopla to save,
30 traceback wouldn't work well without some complicated hoopla to save,
31 reset and restore the tty state.
31 reset and restore the tty state.
32
32
33 - The evaluation of the NAME.NAME... form may cause arbitrary
33 - The evaluation of the NAME.NAME... form may cause arbitrary
34 application defined code to be executed if an object with a
34 application defined code to be executed if an object with a
35 __getattr__ hook is found. Since it is the responsibility of the
35 __getattr__ hook is found. Since it is the responsibility of the
36 application (or the user) to enable this feature, I consider this an
36 application (or the user) to enable this feature, I consider this an
37 acceptable risk. More complicated expressions (e.g. function calls or
37 acceptable risk. More complicated expressions (e.g. function calls or
38 indexing operations) are *not* evaluated.
38 indexing operations) are *not* evaluated.
39
39
40 - GNU readline is also used by the built-in functions input() and
40 - GNU readline is also used by the built-in functions input() and
41 raw_input(), and thus these also benefit/suffer from the completer
41 raw_input(), and thus these also benefit/suffer from the completer
42 features. Clearly an interactive application can benefit by
42 features. Clearly an interactive application can benefit by
43 specifying its own completer function and using raw_input() for all
43 specifying its own completer function and using raw_input() for all
44 its input.
44 its input.
45
45
46 - When the original stdin is not a tty device, GNU readline is never
46 - When the original stdin is not a tty device, GNU readline is never
47 used, and this module (and the readline module) are silently inactive.
47 used, and this module (and the readline module) are silently inactive.
48
48
49 """
49 """
50
50
51 #*****************************************************************************
51 #*****************************************************************************
52 #
52 #
53 # Since this file is essentially a minimally modified copy of the rlcompleter
53 # Since this file is essentially a minimally modified copy of the rlcompleter
54 # module which is part of the standard Python distribution, I assume that the
54 # module which is part of the standard Python distribution, I assume that the
55 # proper procedure is to maintain its copyright as belonging to the Python
55 # proper procedure is to maintain its copyright as belonging to the Python
56 # Software Foundation (in addition to my own, for all new code).
56 # Software Foundation (in addition to my own, for all new code).
57 #
57 #
58 # Copyright (C) 2001 Python Software Foundation, www.python.org
58 # Copyright (C) 2001 Python Software Foundation, www.python.org
59 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
59 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
60 #
60 #
61 # Distributed under the terms of the BSD License. The full license is in
61 # Distributed under the terms of the BSD License. The full license is in
62 # the file COPYING, distributed as part of this software.
62 # the file COPYING, distributed as part of this software.
63 #
63 #
64 #*****************************************************************************
64 #*****************************************************************************
65
65
66 import __builtin__
66 import __builtin__
67 import __main__
67 import __main__
68 import glob
68 import glob
69 import keyword
69 import keyword
70 import os
70 import os
71 import re
71 import re
72 import shlex
72 import shlex
73 import sys
73 import sys
74 import IPython.rlineimpl as readline
74 import IPython.rlineimpl as readline
75 import itertools
75 import itertools
76 from IPython.ipstruct import Struct
76 from IPython.ipstruct import Struct
77 from IPython import ipapi
77 from IPython import ipapi
78 from IPython import generics
78 from IPython import generics
79 import types
79 import types
80
80
81 # Python 2.4 offers sets as a builtin
81 # Python 2.4 offers sets as a builtin
82 try:
82 try:
83 set()
83 set()
84 except NameError:
84 except NameError:
85 from sets import Set as set
85 from sets import Set as set
86
86
87 from IPython.genutils import debugx, dir2
87 from IPython.genutils import debugx, dir2
88
88
89 __all__ = ['Completer','IPCompleter']
89 __all__ = ['Completer','IPCompleter']
90
90
91 class Completer:
91 class Completer:
92 def __init__(self,namespace=None,global_namespace=None):
92 def __init__(self,namespace=None,global_namespace=None):
93 """Create a new completer for the command line.
93 """Create a new completer for the command line.
94
94
95 Completer([namespace,global_namespace]) -> completer instance.
95 Completer([namespace,global_namespace]) -> completer instance.
96
96
97 If unspecified, the default namespace where completions are performed
97 If unspecified, the default namespace where completions are performed
98 is __main__ (technically, __main__.__dict__). Namespaces should be
98 is __main__ (technically, __main__.__dict__). Namespaces should be
99 given as dictionaries.
99 given as dictionaries.
100
100
101 An optional second namespace can be given. This allows the completer
101 An optional second namespace can be given. This allows the completer
102 to handle cases where both the local and global scopes need to be
102 to handle cases where both the local and global scopes need to be
103 distinguished.
103 distinguished.
104
104
105 Completer instances should be used as the completion mechanism of
105 Completer instances should be used as the completion mechanism of
106 readline via the set_completer() call:
106 readline via the set_completer() call:
107
107
108 readline.set_completer(Completer(my_namespace).complete)
108 readline.set_completer(Completer(my_namespace).complete)
109 """
109 """
110
110
111 # Don't bind to namespace quite yet, but flag whether the user wants a
111 # Don't bind to namespace quite yet, but flag whether the user wants a
112 # specific namespace or to use __main__.__dict__. This will allow us
112 # specific namespace or to use __main__.__dict__. This will allow us
113 # to bind to __main__.__dict__ at completion time, not now.
113 # to bind to __main__.__dict__ at completion time, not now.
114 if namespace is None:
114 if namespace is None:
115 self.use_main_ns = 1
115 self.use_main_ns = 1
116 else:
116 else:
117 self.use_main_ns = 0
117 self.use_main_ns = 0
118 self.namespace = namespace
118 self.namespace = namespace
119
119
120 # The global namespace, if given, can be bound directly
120 # The global namespace, if given, can be bound directly
121 if global_namespace is None:
121 if global_namespace is None:
122 self.global_namespace = {}
122 self.global_namespace = {}
123 else:
123 else:
124 self.global_namespace = global_namespace
124 self.global_namespace = global_namespace
125
125
126 def complete(self, text, state):
126 def complete(self, text, state):
127 """Return the next possible completion for 'text'.
127 """Return the next possible completion for 'text'.
128
128
129 This is called successively with state == 0, 1, 2, ... until it
129 This is called successively with state == 0, 1, 2, ... until it
130 returns None. The completion should begin with 'text'.
130 returns None. The completion should begin with 'text'.
131
131
132 """
132 """
133 if self.use_main_ns:
133 if self.use_main_ns:
134 self.namespace = __main__.__dict__
134 self.namespace = __main__.__dict__
135
135
136 if state == 0:
136 if state == 0:
137 if "." in text:
137 if "." in text:
138 self.matches = self.attr_matches(text)
138 self.matches = self.attr_matches(text)
139 else:
139 else:
140 self.matches = self.global_matches(text)
140 self.matches = self.global_matches(text)
141 try:
141 try:
142 return self.matches[state]
142 return self.matches[state]
143 except IndexError:
143 except IndexError:
144 return None
144 return None
145
145
146 def global_matches(self, text):
146 def global_matches(self, text):
147 """Compute matches when text is a simple name.
147 """Compute matches when text is a simple name.
148
148
149 Return a list of all keywords, built-in functions and names currently
149 Return a list of all keywords, built-in functions and names currently
150 defined in self.namespace or self.global_namespace that match.
150 defined in self.namespace or self.global_namespace that match.
151
151
152 """
152 """
153 matches = []
153 matches = []
154 match_append = matches.append
154 match_append = matches.append
155 n = len(text)
155 n = len(text)
156 for lst in [keyword.kwlist,
156 for lst in [keyword.kwlist,
157 __builtin__.__dict__.keys(),
157 __builtin__.__dict__.keys(),
158 self.namespace.keys(),
158 self.namespace.keys(),
159 self.global_namespace.keys()]:
159 self.global_namespace.keys()]:
160 for word in lst:
160 for word in lst:
161 if word[:n] == text and word != "__builtins__":
161 if word[:n] == text and word != "__builtins__":
162 match_append(word)
162 match_append(word)
163 return matches
163 return matches
164
164
165 def attr_matches(self, text):
165 def attr_matches(self, text):
166 """Compute matches when text contains a dot.
166 """Compute matches when text contains a dot.
167
167
168 Assuming the text is of the form NAME.NAME....[NAME], and is
168 Assuming the text is of the form NAME.NAME....[NAME], and is
169 evaluatable in self.namespace or self.global_namespace, it will be
169 evaluatable in self.namespace or self.global_namespace, it will be
170 evaluated and its attributes (as revealed by dir()) are used as
170 evaluated and its attributes (as revealed by dir()) are used as
171 possible completions. (For class instances, class members are are
171 possible completions. (For class instances, class members are are
172 also considered.)
172 also considered.)
173
173
174 WARNING: this can still invoke arbitrary C code, if an object
174 WARNING: this can still invoke arbitrary C code, if an object
175 with a __getattr__ hook is evaluated.
175 with a __getattr__ hook is evaluated.
176
176
177 """
177 """
178 import re
178 import re
179
179
180 # Another option, seems to work great. Catches things like ''.<tab>
180 # Another option, seems to work great. Catches things like ''.<tab>
181 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
181 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
182
182
183 if not m:
183 if not m:
184 return []
184 return []
185
185
186 expr, attr = m.group(1, 3)
186 expr, attr = m.group(1, 3)
187 try:
187 try:
188 obj = eval(expr, self.namespace)
188 obj = eval(expr, self.namespace)
189 except:
189 except:
190 try:
190 try:
191 obj = eval(expr, self.global_namespace)
191 obj = eval(expr, self.global_namespace)
192 except:
192 except:
193 return []
193 return []
194
194
195 words = dir2(obj)
195 words = dir2(obj)
196
196
197 try:
197 try:
198 words = generics.complete_object(obj, words)
198 words = generics.complete_object(obj, words)
199 except ipapi.TryNext:
199 except ipapi.TryNext:
200 pass
200 pass
201 # Build match list to return
201 # Build match list to return
202 n = len(attr)
202 n = len(attr)
203 res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ]
203 res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ]
204 return res
204 return res
205
205
206 class IPCompleter(Completer):
206 class IPCompleter(Completer):
207 """Extension of the completer class with IPython-specific features"""
207 """Extension of the completer class with IPython-specific features"""
208
208
209 def __init__(self,shell,namespace=None,global_namespace=None,
209 def __init__(self,shell,namespace=None,global_namespace=None,
210 omit__names=0,alias_table=None):
210 omit__names=0,alias_table=None):
211 """IPCompleter() -> completer
211 """IPCompleter() -> completer
212
212
213 Return a completer object suitable for use by the readline library
213 Return a completer object suitable for use by the readline library
214 via readline.set_completer().
214 via readline.set_completer().
215
215
216 Inputs:
216 Inputs:
217
217
218 - shell: a pointer to the ipython shell itself. This is needed
218 - shell: a pointer to the ipython shell itself. This is needed
219 because this completer knows about magic functions, and those can
219 because this completer knows about magic functions, and those can
220 only be accessed via the ipython instance.
220 only be accessed via the ipython instance.
221
221
222 - namespace: an optional dict where completions are performed.
222 - namespace: an optional dict where completions are performed.
223
223
224 - global_namespace: secondary optional dict for completions, to
224 - global_namespace: secondary optional dict for completions, to
225 handle cases (such as IPython embedded inside functions) where
225 handle cases (such as IPython embedded inside functions) where
226 both Python scopes are visible.
226 both Python scopes are visible.
227
227
228 - The optional omit__names parameter sets the completer to omit the
228 - The optional omit__names parameter sets the completer to omit the
229 'magic' names (__magicname__) for python objects unless the text
229 'magic' names (__magicname__) for python objects unless the text
230 to be completed explicitly starts with one or more underscores.
230 to be completed explicitly starts with one or more underscores.
231
231
232 - If alias_table is supplied, it should be a dictionary of aliases
232 - If alias_table is supplied, it should be a dictionary of aliases
233 to complete. """
233 to complete. """
234
234
235 Completer.__init__(self,namespace,global_namespace)
235 Completer.__init__(self,namespace,global_namespace)
236 self.magic_prefix = shell.name+'.magic_'
236 self.magic_prefix = shell.name+'.magic_'
237 self.magic_escape = shell.ESC_MAGIC
237 self.magic_escape = shell.ESC_MAGIC
238 self.readline = readline
238 self.readline = readline
239 delims = self.readline.get_completer_delims()
239 delims = self.readline.get_completer_delims()
240 delims = delims.replace(self.magic_escape,'')
240 delims = delims.replace(self.magic_escape,'')
241 self.readline.set_completer_delims(delims)
241 self.readline.set_completer_delims(delims)
242 self.get_line_buffer = self.readline.get_line_buffer
242 self.get_line_buffer = self.readline.get_line_buffer
243 self.get_endidx = self.readline.get_endidx
243 self.get_endidx = self.readline.get_endidx
244 self.omit__names = omit__names
244 self.omit__names = omit__names
245 self.merge_completions = shell.rc.readline_merge_completions
245 self.merge_completions = shell.rc.readline_merge_completions
246 if alias_table is None:
246 if alias_table is None:
247 alias_table = {}
247 alias_table = {}
248 self.alias_table = alias_table
248 self.alias_table = alias_table
249 # Regexp to split filenames with spaces in them
249 # Regexp to split filenames with spaces in them
250 self.space_name_re = re.compile(r'([^\\] )')
250 self.space_name_re = re.compile(r'([^\\] )')
251 # Hold a local ref. to glob.glob for speed
251 # Hold a local ref. to glob.glob for speed
252 self.glob = glob.glob
252 self.glob = glob.glob
253
253
254 # Determine if we are running on 'dumb' terminals, like (X)Emacs
254 # Determine if we are running on 'dumb' terminals, like (X)Emacs
255 # buffers, to avoid completion problems.
255 # buffers, to avoid completion problems.
256 term = os.environ.get('TERM','xterm')
256 term = os.environ.get('TERM','xterm')
257 self.dumb_terminal = term in ['dumb','emacs']
257 self.dumb_terminal = term in ['dumb','emacs']
258
258
259 # Special handling of backslashes needed in win32 platforms
259 # Special handling of backslashes needed in win32 platforms
260 if sys.platform == "win32":
260 if sys.platform == "win32":
261 self.clean_glob = self._clean_glob_win32
261 self.clean_glob = self._clean_glob_win32
262 else:
262 else:
263 self.clean_glob = self._clean_glob
263 self.clean_glob = self._clean_glob
264 self.matchers = [self.python_matches,
264 self.matchers = [self.python_matches,
265 self.file_matches,
265 self.file_matches,
266 self.alias_matches,
266 self.alias_matches,
267 self.python_func_kw_matches]
267 self.python_func_kw_matches]
268
268
269
269
270 # Code contributed by Alex Schmolck, for ipython/emacs integration
270 # Code contributed by Alex Schmolck, for ipython/emacs integration
271 def all_completions(self, text):
271 def all_completions(self, text):
272 """Return all possible completions for the benefit of emacs."""
272 """Return all possible completions for the benefit of emacs."""
273
273
274 completions = []
274 completions = []
275 comp_append = completions.append
275 comp_append = completions.append
276 try:
276 try:
277 for i in xrange(sys.maxint):
277 for i in xrange(sys.maxint):
278 res = self.complete(text, i)
278 res = self.complete(text, i)
279
279
280 if not res: break
280 if not res: break
281
281
282 comp_append(res)
282 comp_append(res)
283 #XXX workaround for ``notDefined.<tab>``
283 #XXX workaround for ``notDefined.<tab>``
284 except NameError:
284 except NameError:
285 pass
285 pass
286 return completions
286 return completions
287 # /end Alex Schmolck code.
287 # /end Alex Schmolck code.
288
288
289 def _clean_glob(self,text):
289 def _clean_glob(self,text):
290 return self.glob("%s*" % text)
290 return self.glob("%s*" % text)
291
291
292 def _clean_glob_win32(self,text):
292 def _clean_glob_win32(self,text):
293 return [f.replace("\\","/")
293 return [f.replace("\\","/")
294 for f in self.glob("%s*" % text)]
294 for f in self.glob("%s*" % text)]
295
295
296 def file_matches(self, text):
296 def file_matches(self, text):
297 """Match filenames, expanding ~USER type strings.
297 """Match filenames, expanding ~USER type strings.
298
298
299 Most of the seemingly convoluted logic in this completer is an
299 Most of the seemingly convoluted logic in this completer is an
300 attempt to handle filenames with spaces in them. And yet it's not
300 attempt to handle filenames with spaces in them. And yet it's not
301 quite perfect, because Python's readline doesn't expose all of the
301 quite perfect, because Python's readline doesn't expose all of the
302 GNU readline details needed for this to be done correctly.
302 GNU readline details needed for this to be done correctly.
303
303
304 For a filename with a space in it, the printed completions will be
304 For a filename with a space in it, the printed completions will be
305 only the parts after what's already been typed (instead of the
305 only the parts after what's already been typed (instead of the
306 full completions, as is normally done). I don't think with the
306 full completions, as is normally done). I don't think with the
307 current (as of Python 2.3) Python readline it's possible to do
307 current (as of Python 2.3) Python readline it's possible to do
308 better."""
308 better."""
309
309
310 #print 'Completer->file_matches: <%s>' % text # dbg
310 #print 'Completer->file_matches: <%s>' % text # dbg
311
311
312 # chars that require escaping with backslash - i.e. chars
312 # chars that require escaping with backslash - i.e. chars
313 # that readline treats incorrectly as delimiters, but we
313 # that readline treats incorrectly as delimiters, but we
314 # don't want to treat as delimiters in filename matching
314 # don't want to treat as delimiters in filename matching
315 # when escaped with backslash
315 # when escaped with backslash
316
316
317 protectables = ' '
317 if sys.platform == 'win32':
318 protectables = ' '
319 else:
320 protectables = ' ()'
318
321
319 if text.startswith('!'):
322 if text.startswith('!'):
320 text = text[1:]
323 text = text[1:]
321 text_prefix = '!'
324 text_prefix = '!'
322 else:
325 else:
323 text_prefix = ''
326 text_prefix = ''
324
327
325 def protect_filename(s):
328 def protect_filename(s):
326 return "".join([(ch in protectables and '\\' + ch or ch)
329 return "".join([(ch in protectables and '\\' + ch or ch)
327 for ch in s])
330 for ch in s])
328
331
329 def single_dir_expand(matches):
332 def single_dir_expand(matches):
330 "Recursively expand match lists containing a single dir."
333 "Recursively expand match lists containing a single dir."
331
334
332 if len(matches) == 1 and os.path.isdir(matches[0]):
335 if len(matches) == 1 and os.path.isdir(matches[0]):
333 # Takes care of links to directories also. Use '/'
336 # Takes care of links to directories also. Use '/'
334 # explicitly, even under Windows, so that name completions
337 # explicitly, even under Windows, so that name completions
335 # don't end up escaped.
338 # don't end up escaped.
336 d = matches[0]
339 d = matches[0]
337 if d[-1] in ['/','\\']:
340 if d[-1] in ['/','\\']:
338 d = d[:-1]
341 d = d[:-1]
339
342
340 subdirs = os.listdir(d)
343 subdirs = os.listdir(d)
341 if subdirs:
344 if subdirs:
342 matches = [ (d + '/' + p) for p in subdirs]
345 matches = [ (d + '/' + p) for p in subdirs]
343 return single_dir_expand(matches)
346 return single_dir_expand(matches)
344 else:
347 else:
345 return matches
348 return matches
346 else:
349 else:
347 return matches
350 return matches
348
351
349 lbuf = self.lbuf
352 lbuf = self.lbuf
350 open_quotes = 0 # track strings with open quotes
353 open_quotes = 0 # track strings with open quotes
351 try:
354 try:
352 lsplit = shlex.split(lbuf)[-1]
355 lsplit = shlex.split(lbuf)[-1]
353 except ValueError:
356 except ValueError:
354 # typically an unmatched ", or backslash without escaped char.
357 # typically an unmatched ", or backslash without escaped char.
355 if lbuf.count('"')==1:
358 if lbuf.count('"')==1:
356 open_quotes = 1
359 open_quotes = 1
357 lsplit = lbuf.split('"')[-1]
360 lsplit = lbuf.split('"')[-1]
358 elif lbuf.count("'")==1:
361 elif lbuf.count("'")==1:
359 open_quotes = 1
362 open_quotes = 1
360 lsplit = lbuf.split("'")[-1]
363 lsplit = lbuf.split("'")[-1]
361 else:
364 else:
362 return []
365 return []
363 except IndexError:
366 except IndexError:
364 # tab pressed on empty line
367 # tab pressed on empty line
365 lsplit = ""
368 lsplit = ""
366
369
367 if lsplit != protect_filename(lsplit):
370 if lsplit != protect_filename(lsplit):
368 # if protectables are found, do matching on the whole escaped
371 # if protectables are found, do matching on the whole escaped
369 # name
372 # name
370 has_protectables = 1
373 has_protectables = 1
371 text0,text = text,lsplit
374 text0,text = text,lsplit
372 else:
375 else:
373 has_protectables = 0
376 has_protectables = 0
374 text = os.path.expanduser(text)
377 text = os.path.expanduser(text)
375
378
376 if text == "":
379 if text == "":
377 return [text_prefix + protect_filename(f) for f in self.glob("*")]
380 return [text_prefix + protect_filename(f) for f in self.glob("*")]
378
381
379 m0 = self.clean_glob(text.replace('\\',''))
382 m0 = self.clean_glob(text.replace('\\',''))
380 if has_protectables:
383 if has_protectables:
381 # If we had protectables, we need to revert our changes to the
384 # If we had protectables, we need to revert our changes to the
382 # beginning of filename so that we don't double-write the part
385 # beginning of filename so that we don't double-write the part
383 # of the filename we have so far
386 # of the filename we have so far
384 len_lsplit = len(lsplit)
387 len_lsplit = len(lsplit)
385 matches = [text_prefix + text0 +
388 matches = [text_prefix + text0 +
386 protect_filename(f[len_lsplit:]) for f in m0]
389 protect_filename(f[len_lsplit:]) for f in m0]
387 else:
390 else:
388 if open_quotes:
391 if open_quotes:
389 # if we have a string with an open quote, we don't need to
392 # if we have a string with an open quote, we don't need to
390 # protect the names at all (and we _shouldn't_, as it
393 # protect the names at all (and we _shouldn't_, as it
391 # would cause bugs when the filesystem call is made).
394 # would cause bugs when the filesystem call is made).
392 matches = m0
395 matches = m0
393 else:
396 else:
394 matches = [text_prefix +
397 matches = [text_prefix +
395 protect_filename(f) for f in m0]
398 protect_filename(f) for f in m0]
396
399
397 #print 'mm',matches # dbg
400 #print 'mm',matches # dbg
398 return single_dir_expand(matches)
401 return single_dir_expand(matches)
399
402
400 def alias_matches(self, text):
403 def alias_matches(self, text):
401 """Match internal system aliases"""
404 """Match internal system aliases"""
402 #print 'Completer->alias_matches:',text,'lb',self.lbuf # dbg
405 #print 'Completer->alias_matches:',text,'lb',self.lbuf # dbg
403
406
404 # if we are not in the first 'item', alias matching
407 # if we are not in the first 'item', alias matching
405 # doesn't make sense - unless we are starting with 'sudo' command.
408 # doesn't make sense - unless we are starting with 'sudo' command.
406 if ' ' in self.lbuf.lstrip() and not self.lbuf.lstrip().startswith('sudo'):
409 if ' ' in self.lbuf.lstrip() and not self.lbuf.lstrip().startswith('sudo'):
407 return []
410 return []
408 text = os.path.expanduser(text)
411 text = os.path.expanduser(text)
409 aliases = self.alias_table.keys()
412 aliases = self.alias_table.keys()
410 if text == "":
413 if text == "":
411 return aliases
414 return aliases
412 else:
415 else:
413 return [alias for alias in aliases if alias.startswith(text)]
416 return [alias for alias in aliases if alias.startswith(text)]
414
417
415 def python_matches(self,text):
418 def python_matches(self,text):
416 """Match attributes or global python names"""
419 """Match attributes or global python names"""
417
420
418 #print 'Completer->python_matches, txt=<%s>' % text # dbg
421 #print 'Completer->python_matches, txt=<%s>' % text # dbg
419 if "." in text:
422 if "." in text:
420 try:
423 try:
421 matches = self.attr_matches(text)
424 matches = self.attr_matches(text)
422 if text.endswith('.') and self.omit__names:
425 if text.endswith('.') and self.omit__names:
423 if self.omit__names == 1:
426 if self.omit__names == 1:
424 # true if txt is _not_ a __ name, false otherwise:
427 # true if txt is _not_ a __ name, false otherwise:
425 no__name = (lambda txt:
428 no__name = (lambda txt:
426 re.match(r'.*\.__.*?__',txt) is None)
429 re.match(r'.*\.__.*?__',txt) is None)
427 else:
430 else:
428 # true if txt is _not_ a _ name, false otherwise:
431 # true if txt is _not_ a _ name, false otherwise:
429 no__name = (lambda txt:
432 no__name = (lambda txt:
430 re.match(r'.*\._.*?',txt) is None)
433 re.match(r'.*\._.*?',txt) is None)
431 matches = filter(no__name, matches)
434 matches = filter(no__name, matches)
432 except NameError:
435 except NameError:
433 # catches <undefined attributes>.<tab>
436 # catches <undefined attributes>.<tab>
434 matches = []
437 matches = []
435 else:
438 else:
436 matches = self.global_matches(text)
439 matches = self.global_matches(text)
437 # this is so completion finds magics when automagic is on:
440 # this is so completion finds magics when automagic is on:
438 if (matches == [] and
441 if (matches == [] and
439 not text.startswith(os.sep) and
442 not text.startswith(os.sep) and
440 not ' ' in self.lbuf):
443 not ' ' in self.lbuf):
441 matches = self.attr_matches(self.magic_prefix+text)
444 matches = self.attr_matches(self.magic_prefix+text)
442 return matches
445 return matches
443
446
444 def _default_arguments(self, obj):
447 def _default_arguments(self, obj):
445 """Return the list of default arguments of obj if it is callable,
448 """Return the list of default arguments of obj if it is callable,
446 or empty list otherwise."""
449 or empty list otherwise."""
447
450
448 if not (inspect.isfunction(obj) or inspect.ismethod(obj)):
451 if not (inspect.isfunction(obj) or inspect.ismethod(obj)):
449 # for classes, check for __init__,__new__
452 # for classes, check for __init__,__new__
450 if inspect.isclass(obj):
453 if inspect.isclass(obj):
451 obj = (getattr(obj,'__init__',None) or
454 obj = (getattr(obj,'__init__',None) or
452 getattr(obj,'__new__',None))
455 getattr(obj,'__new__',None))
453 # for all others, check if they are __call__able
456 # for all others, check if they are __call__able
454 elif hasattr(obj, '__call__'):
457 elif hasattr(obj, '__call__'):
455 obj = obj.__call__
458 obj = obj.__call__
456 # XXX: is there a way to handle the builtins ?
459 # XXX: is there a way to handle the builtins ?
457 try:
460 try:
458 args,_,_1,defaults = inspect.getargspec(obj)
461 args,_,_1,defaults = inspect.getargspec(obj)
459 if defaults:
462 if defaults:
460 return args[-len(defaults):]
463 return args[-len(defaults):]
461 except TypeError: pass
464 except TypeError: pass
462 return []
465 return []
463
466
464 def python_func_kw_matches(self,text):
467 def python_func_kw_matches(self,text):
465 """Match named parameters (kwargs) of the last open function"""
468 """Match named parameters (kwargs) of the last open function"""
466
469
467 if "." in text: # a parameter cannot be dotted
470 if "." in text: # a parameter cannot be dotted
468 return []
471 return []
469 try: regexp = self.__funcParamsRegex
472 try: regexp = self.__funcParamsRegex
470 except AttributeError:
473 except AttributeError:
471 regexp = self.__funcParamsRegex = re.compile(r'''
474 regexp = self.__funcParamsRegex = re.compile(r'''
472 '.*?' | # single quoted strings or
475 '.*?' | # single quoted strings or
473 ".*?" | # double quoted strings or
476 ".*?" | # double quoted strings or
474 \w+ | # identifier
477 \w+ | # identifier
475 \S # other characters
478 \S # other characters
476 ''', re.VERBOSE | re.DOTALL)
479 ''', re.VERBOSE | re.DOTALL)
477 # 1. find the nearest identifier that comes before an unclosed
480 # 1. find the nearest identifier that comes before an unclosed
478 # parenthesis e.g. for "foo (1+bar(x), pa", the candidate is "foo"
481 # parenthesis e.g. for "foo (1+bar(x), pa", the candidate is "foo"
479 tokens = regexp.findall(self.get_line_buffer())
482 tokens = regexp.findall(self.get_line_buffer())
480 tokens.reverse()
483 tokens.reverse()
481 iterTokens = iter(tokens); openPar = 0
484 iterTokens = iter(tokens); openPar = 0
482 for token in iterTokens:
485 for token in iterTokens:
483 if token == ')':
486 if token == ')':
484 openPar -= 1
487 openPar -= 1
485 elif token == '(':
488 elif token == '(':
486 openPar += 1
489 openPar += 1
487 if openPar > 0:
490 if openPar > 0:
488 # found the last unclosed parenthesis
491 # found the last unclosed parenthesis
489 break
492 break
490 else:
493 else:
491 return []
494 return []
492 # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
495 # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
493 ids = []
496 ids = []
494 isId = re.compile(r'\w+$').match
497 isId = re.compile(r'\w+$').match
495 while True:
498 while True:
496 try:
499 try:
497 ids.append(iterTokens.next())
500 ids.append(iterTokens.next())
498 if not isId(ids[-1]):
501 if not isId(ids[-1]):
499 ids.pop(); break
502 ids.pop(); break
500 if not iterTokens.next() == '.':
503 if not iterTokens.next() == '.':
501 break
504 break
502 except StopIteration:
505 except StopIteration:
503 break
506 break
504 # lookup the candidate callable matches either using global_matches
507 # lookup the candidate callable matches either using global_matches
505 # or attr_matches for dotted names
508 # or attr_matches for dotted names
506 if len(ids) == 1:
509 if len(ids) == 1:
507 callableMatches = self.global_matches(ids[0])
510 callableMatches = self.global_matches(ids[0])
508 else:
511 else:
509 callableMatches = self.attr_matches('.'.join(ids[::-1]))
512 callableMatches = self.attr_matches('.'.join(ids[::-1]))
510 argMatches = []
513 argMatches = []
511 for callableMatch in callableMatches:
514 for callableMatch in callableMatches:
512 try: namedArgs = self._default_arguments(eval(callableMatch,
515 try: namedArgs = self._default_arguments(eval(callableMatch,
513 self.namespace))
516 self.namespace))
514 except: continue
517 except: continue
515 for namedArg in namedArgs:
518 for namedArg in namedArgs:
516 if namedArg.startswith(text):
519 if namedArg.startswith(text):
517 argMatches.append("%s=" %namedArg)
520 argMatches.append("%s=" %namedArg)
518 return argMatches
521 return argMatches
519
522
520 def dispatch_custom_completer(self,text):
523 def dispatch_custom_completer(self,text):
521 #print "Custom! '%s' %s" % (text, self.custom_completers) # dbg
524 #print "Custom! '%s' %s" % (text, self.custom_completers) # dbg
522 line = self.full_lbuf
525 line = self.full_lbuf
523 if not line.strip():
526 if not line.strip():
524 return None
527 return None
525
528
526 event = Struct()
529 event = Struct()
527 event.line = line
530 event.line = line
528 event.symbol = text
531 event.symbol = text
529 cmd = line.split(None,1)[0]
532 cmd = line.split(None,1)[0]
530 event.command = cmd
533 event.command = cmd
531 #print "\ncustom:{%s]\n" % event # dbg
534 #print "\ncustom:{%s]\n" % event # dbg
532
535
533 # for foo etc, try also to find completer for %foo
536 # for foo etc, try also to find completer for %foo
534 if not cmd.startswith(self.magic_escape):
537 if not cmd.startswith(self.magic_escape):
535 try_magic = self.custom_completers.s_matches(
538 try_magic = self.custom_completers.s_matches(
536 self.magic_escape + cmd)
539 self.magic_escape + cmd)
537 else:
540 else:
538 try_magic = []
541 try_magic = []
539
542
540
543
541 for c in itertools.chain(
544 for c in itertools.chain(
542 self.custom_completers.s_matches(cmd),
545 self.custom_completers.s_matches(cmd),
543 try_magic,
546 try_magic,
544 self.custom_completers.flat_matches(self.lbuf)):
547 self.custom_completers.flat_matches(self.lbuf)):
545 #print "try",c # dbg
548 #print "try",c # dbg
546 try:
549 try:
547 res = c(event)
550 res = c(event)
548 # first, try case sensitive match
551 # first, try case sensitive match
549 withcase = [r for r in res if r.startswith(text)]
552 withcase = [r for r in res if r.startswith(text)]
550 if withcase:
553 if withcase:
551 return withcase
554 return withcase
552 # if none, then case insensitive ones are ok too
555 # if none, then case insensitive ones are ok too
553 return [r for r in res if r.lower().startswith(text.lower())]
556 return [r for r in res if r.lower().startswith(text.lower())]
554 except ipapi.TryNext:
557 except ipapi.TryNext:
555 pass
558 pass
556
559
557 return None
560 return None
558
561
559 def complete(self, text, state,line_buffer=None):
562 def complete(self, text, state,line_buffer=None):
560 """Return the next possible completion for 'text'.
563 """Return the next possible completion for 'text'.
561
564
562 This is called successively with state == 0, 1, 2, ... until it
565 This is called successively with state == 0, 1, 2, ... until it
563 returns None. The completion should begin with 'text'.
566 returns None. The completion should begin with 'text'.
564
567
565 :Keywords:
568 :Keywords:
566 - line_buffer: string
569 - line_buffer: string
567 If not given, the completer attempts to obtain the current line buffer
570 If not given, the completer attempts to obtain the current line buffer
568 via readline. This keyword allows clients which are requesting for
571 via readline. This keyword allows clients which are requesting for
569 text completions in non-readline contexts to inform the completer of
572 text completions in non-readline contexts to inform the completer of
570 the entire text.
573 the entire text.
571 """
574 """
572
575
573 #print '\n*** COMPLETE: <%s> (%s)' % (text,state) # dbg
576 #print '\n*** COMPLETE: <%s> (%s)' % (text,state) # dbg
574
577
575 # if there is only a tab on a line with only whitespace, instead
578 # if there is only a tab on a line with only whitespace, instead
576 # of the mostly useless 'do you want to see all million
579 # of the mostly useless 'do you want to see all million
577 # completions' message, just do the right thing and give the user
580 # completions' message, just do the right thing and give the user
578 # his tab! Incidentally, this enables pasting of tabbed text from
581 # his tab! Incidentally, this enables pasting of tabbed text from
579 # an editor (as long as autoindent is off).
582 # an editor (as long as autoindent is off).
580
583
581 # It should be noted that at least pyreadline still shows
584 # It should be noted that at least pyreadline still shows
582 # file completions - is there a way around it?
585 # file completions - is there a way around it?
583
586
584 # don't apply this on 'dumb' terminals, such as emacs buffers, so we
587 # don't apply this on 'dumb' terminals, such as emacs buffers, so we
585 # don't interfere with their own tab-completion mechanism.
588 # don't interfere with their own tab-completion mechanism.
586 if line_buffer is None:
589 if line_buffer is None:
587 self.full_lbuf = self.get_line_buffer()
590 self.full_lbuf = self.get_line_buffer()
588 else:
591 else:
589 self.full_lbuf = line_buffer
592 self.full_lbuf = line_buffer
590
593
591 if not (self.dumb_terminal or self.full_lbuf.strip()):
594 if not (self.dumb_terminal or self.full_lbuf.strip()):
592 self.readline.insert_text('\t')
595 self.readline.insert_text('\t')
593 return None
596 return None
594
597
595 magic_escape = self.magic_escape
598 magic_escape = self.magic_escape
596 magic_prefix = self.magic_prefix
599 magic_prefix = self.magic_prefix
597
600
598 self.lbuf = self.full_lbuf[:self.get_endidx()]
601 self.lbuf = self.full_lbuf[:self.get_endidx()]
599
602
600 try:
603 try:
601 if text.startswith(magic_escape):
604 if text.startswith(magic_escape):
602 text = text.replace(magic_escape,magic_prefix)
605 text = text.replace(magic_escape,magic_prefix)
603 elif text.startswith('~'):
606 elif text.startswith('~'):
604 text = os.path.expanduser(text)
607 text = os.path.expanduser(text)
605 if state == 0:
608 if state == 0:
606 custom_res = self.dispatch_custom_completer(text)
609 custom_res = self.dispatch_custom_completer(text)
607 if custom_res is not None:
610 if custom_res is not None:
608 # did custom completers produce something?
611 # did custom completers produce something?
609 self.matches = custom_res
612 self.matches = custom_res
610 else:
613 else:
611 # Extend the list of completions with the results of each
614 # Extend the list of completions with the results of each
612 # matcher, so we return results to the user from all
615 # matcher, so we return results to the user from all
613 # namespaces.
616 # namespaces.
614 if self.merge_completions:
617 if self.merge_completions:
615 self.matches = []
618 self.matches = []
616 for matcher in self.matchers:
619 for matcher in self.matchers:
617 self.matches.extend(matcher(text))
620 self.matches.extend(matcher(text))
618 else:
621 else:
619 for matcher in self.matchers:
622 for matcher in self.matchers:
620 self.matches = matcher(text)
623 self.matches = matcher(text)
621 if self.matches:
624 if self.matches:
622 break
625 break
623 def uniq(alist):
626 def uniq(alist):
624 set = {}
627 set = {}
625 return [set.setdefault(e,e) for e in alist if e not in set]
628 return [set.setdefault(e,e) for e in alist if e not in set]
626 self.matches = uniq(self.matches)
629 self.matches = uniq(self.matches)
627 try:
630 try:
628 ret = self.matches[state].replace(magic_prefix,magic_escape)
631 ret = self.matches[state].replace(magic_prefix,magic_escape)
629 return ret
632 return ret
630 except IndexError:
633 except IndexError:
631 return None
634 return None
632 except:
635 except:
633 #from IPython.ultraTB import AutoFormattedTB; # dbg
636 #from IPython.ultraTB import AutoFormattedTB; # dbg
634 #tb=AutoFormattedTB('Verbose');tb() #dbg
637 #tb=AutoFormattedTB('Verbose');tb() #dbg
635
638
636 # If completion fails, don't annoy the user.
639 # If completion fails, don't annoy the user.
637 return None
640 return None
@@ -1,92 +1,76 b''
1 """
1 """
2 Base front end class for all async frontends.
2 Base front end class for all async frontends.
3 """
3 """
4 __docformat__ = "restructuredtext en"
4 __docformat__ = "restructuredtext en"
5
5
6 #-------------------------------------------------------------------------------
6 #-------------------------------------------------------------------------------
7 # Copyright (C) 2008 The IPython Development Team
7 # Copyright (C) 2008 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12
12
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 import uuid
17 import uuid
18
18
19 try:
20 from zope.interface import Interface, Attribute, implements, classProvides
21 except ImportError, e:
22 e.message = """%s
23 ________________________________________________________________________________
24 zope.interface is required to run asynchronous frontends.""" % e.message
25 e.args = (e.message, ) + e.args[1:]
26
19
27 from frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
20 from zope.interface import Interface, Attribute, implements, classProvides
28
21 from twisted.python.failure import Failure
29 from IPython.kernel.engineservice import IEngineCore
22 from IPython.frontend.frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
30 from IPython.kernel.core.history import FrontEndHistory
23 from IPython.kernel.core.history import FrontEndHistory
31
24 from IPython.kernel.engineservice import IEngineCore
32 try:
33 from twisted.python.failure import Failure
34 except ImportError, e:
35 e.message = """%s
36 ________________________________________________________________________________
37 twisted is required to run asynchronous frontends.""" % e.message
38 e.args = (e.message, ) + e.args[1:]
39
40
41
25
42
26
43 class AsyncFrontEndBase(FrontEndBase):
27 class AsyncFrontEndBase(FrontEndBase):
44 """
28 """
45 Overrides FrontEndBase to wrap execute in a deferred result.
29 Overrides FrontEndBase to wrap execute in a deferred result.
46 All callbacks are made as callbacks on the deferred result.
30 All callbacks are made as callbacks on the deferred result.
47 """
31 """
48
32
49 implements(IFrontEnd)
33 implements(IFrontEnd)
50 classProvides(IFrontEndFactory)
34 classProvides(IFrontEndFactory)
51
35
52 def __init__(self, engine=None, history=None):
36 def __init__(self, engine=None, history=None):
53 assert(engine==None or IEngineCore.providedBy(engine))
37 assert(engine==None or IEngineCore.providedBy(engine))
54 self.engine = IEngineCore(engine)
38 self.engine = IEngineCore(engine)
55 if history is None:
39 if history is None:
56 self.history = FrontEndHistory(input_cache=[''])
40 self.history = FrontEndHistory(input_cache=[''])
57 else:
41 else:
58 self.history = history
42 self.history = history
59
43
60
44
61 def execute(self, block, blockID=None):
45 def execute(self, block, blockID=None):
62 """Execute the block and return the deferred result.
46 """Execute the block and return the deferred result.
63
47
64 Parameters:
48 Parameters:
65 block : {str, AST}
49 block : {str, AST}
66 blockID : any
50 blockID : any
67 Caller may provide an ID to identify this block.
51 Caller may provide an ID to identify this block.
68 result['blockID'] := blockID
52 result['blockID'] := blockID
69
53
70 Result:
54 Result:
71 Deferred result of self.interpreter.execute
55 Deferred result of self.interpreter.execute
72 """
56 """
73
57
74 if(not self.is_complete(block)):
58 if(not self.is_complete(block)):
75 return Failure(Exception("Block is not compilable"))
59 return Failure(Exception("Block is not compilable"))
76
60
77 if(blockID == None):
61 if(blockID == None):
78 blockID = uuid.uuid4() #random UUID
62 blockID = uuid.uuid4() #random UUID
79
63
80 d = self.engine.execute(block)
64 d = self.engine.execute(block)
81 d.addCallback(self._add_history, block=block)
65 d.addCallback(self._add_history, block=block)
82 d.addCallbacks(self._add_block_id_for_result,
66 d.addCallbacks(self._add_block_id_for_result,
83 errback=self._add_block_id_for_failure,
67 errback=self._add_block_id_for_failure,
84 callbackArgs=(blockID,),
68 callbackArgs=(blockID,),
85 errbackArgs=(blockID,))
69 errbackArgs=(blockID,))
86 d.addBoth(self.update_cell_prompt, blockID=blockID)
70 d.addBoth(self.update_cell_prompt, blockID=blockID)
87 d.addCallbacks(self.render_result,
71 d.addCallbacks(self.render_result,
88 errback=self.render_error)
72 errback=self.render_error)
89
73
90 return d
74 return d
91
75
92
76
@@ -1,91 +1,94 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """This file contains unittests for the
2 """This file contains unittests for the
3 IPython.frontend.cocoa.cocoa_frontend module.
3 IPython.frontend.cocoa.cocoa_frontend module.
4 """
4 """
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #---------------------------------------------------------------------------
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2005 The IPython Development Team
8 # Copyright (C) 2005 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
12 #---------------------------------------------------------------------------
13
13
14 #---------------------------------------------------------------------------
14 #---------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #---------------------------------------------------------------------------
16 #---------------------------------------------------------------------------
17 from IPython.kernel.core.interpreter import Interpreter
17
18 import IPython.kernel.engineservice as es
18 try:
19 from IPython.testing.util import DeferredTestCase
19 from IPython.kernel.core.interpreter import Interpreter
20 from twisted.internet.defer import succeed
20 import IPython.kernel.engineservice as es
21 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
21 from IPython.testing.util import DeferredTestCase
22
22 from twisted.internet.defer import succeed
23 from Foundation import NSMakeRect
23 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
24 from AppKit import NSTextView, NSScrollView
24 from Foundation import NSMakeRect
25 from AppKit import NSTextView, NSScrollView
26 except ImportError:
27 import nose
28 raise nose.SkipTest("This test requires zope.interface, Twisted, Foolscap and PyObjC")
25
29
26 class TestIPythonCocoaControler(DeferredTestCase):
30 class TestIPythonCocoaControler(DeferredTestCase):
27 """Tests for IPythonCocoaController"""
31 """Tests for IPythonCocoaController"""
28
32
29 def setUp(self):
33 def setUp(self):
30 self.controller = IPythonCocoaController.alloc().init()
34 self.controller = IPythonCocoaController.alloc().init()
31 self.engine = es.EngineService()
35 self.engine = es.EngineService()
32 self.engine.startService()
36 self.engine.startService()
33
37
34
35 def tearDown(self):
38 def tearDown(self):
36 self.controller = None
39 self.controller = None
37 self.engine.stopService()
40 self.engine.stopService()
38
41
39 def testControllerExecutesCode(self):
42 def testControllerExecutesCode(self):
40 code ="""5+5"""
43 code ="""5+5"""
41 expected = Interpreter().execute(code)
44 expected = Interpreter().execute(code)
42 del expected['number']
45 del expected['number']
43 def removeNumberAndID(result):
46 def removeNumberAndID(result):
44 del result['number']
47 del result['number']
45 del result['id']
48 del result['id']
46 return result
49 return result
47 self.assertDeferredEquals(
50 self.assertDeferredEquals(
48 self.controller.execute(code).addCallback(removeNumberAndID),
51 self.controller.execute(code).addCallback(removeNumberAndID),
49 expected)
52 expected)
50
53
51 def testControllerMirrorsUserNSWithValuesAsStrings(self):
54 def testControllerMirrorsUserNSWithValuesAsStrings(self):
52 code = """userns1=1;userns2=2"""
55 code = """userns1=1;userns2=2"""
53 def testControllerUserNS(result):
56 def testControllerUserNS(result):
54 self.assertEquals(self.controller.userNS['userns1'], 1)
57 self.assertEquals(self.controller.userNS['userns1'], 1)
55 self.assertEquals(self.controller.userNS['userns2'], 2)
58 self.assertEquals(self.controller.userNS['userns2'], 2)
56
59
57 self.controller.execute(code).addCallback(testControllerUserNS)
60 self.controller.execute(code).addCallback(testControllerUserNS)
58
61
59
62
60 def testControllerInstantiatesIEngine(self):
63 def testControllerInstantiatesIEngine(self):
61 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
64 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
62
65
63 def testControllerCompletesToken(self):
66 def testControllerCompletesToken(self):
64 code = """longNameVariable=10"""
67 code = """longNameVariable=10"""
65 def testCompletes(result):
68 def testCompletes(result):
66 self.assert_("longNameVariable" in result)
69 self.assert_("longNameVariable" in result)
67
70
68 def testCompleteToken(result):
71 def testCompleteToken(result):
69 self.controller.complete("longNa").addCallback(testCompletes)
72 self.controller.complete("longNa").addCallback(testCompletes)
70
73
71 self.controller.execute(code).addCallback(testCompletes)
74 self.controller.execute(code).addCallback(testCompletes)
72
75
73
76
74 def testCurrentIndent(self):
77 def testCurrentIndent(self):
75 """test that current_indent_string returns current indent or None.
78 """test that current_indent_string returns current indent or None.
76 Uses _indent_for_block for direct unit testing.
79 Uses _indent_for_block for direct unit testing.
77 """
80 """
78
81
79 self.controller.tabUsesSpaces = True
82 self.controller.tabUsesSpaces = True
80 self.assert_(self.controller._indent_for_block("""a=3""") == None)
83 self.assert_(self.controller._indent_for_block("""a=3""") == None)
81 self.assert_(self.controller._indent_for_block("") == None)
84 self.assert_(self.controller._indent_for_block("") == None)
82 block = """def test():\n a=3"""
85 block = """def test():\n a=3"""
83 self.assert_(self.controller._indent_for_block(block) == \
86 self.assert_(self.controller._indent_for_block(block) == \
84 ' ' * self.controller.tabSpaces)
87 ' ' * self.controller.tabSpaces)
85
88
86 block = """if(True):\n%sif(False):\n%spass""" % \
89 block = """if(True):\n%sif(False):\n%spass""" % \
87 (' '*self.controller.tabSpaces,
90 (' '*self.controller.tabSpaces,
88 2*' '*self.controller.tabSpaces)
91 2*' '*self.controller.tabSpaces)
89 self.assert_(self.controller._indent_for_block(block) == \
92 self.assert_(self.controller._indent_for_block(block) == \
90 2*(' '*self.controller.tabSpaces))
93 2*(' '*self.controller.tabSpaces))
91
94
@@ -1,359 +1,362 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
3 """
3 """
4 frontendbase provides an interface and base class for GUI frontends for
4 frontendbase provides an interface and base class for GUI frontends for
5 IPython.kernel/IPython.kernel.core.
5 IPython.kernel/IPython.kernel.core.
6
6
7 Frontend implementations will likely want to subclass FrontEndBase.
7 Frontend implementations will likely want to subclass FrontEndBase.
8
8
9 Author: Barry Wark
9 Author: Barry Wark
10 """
10 """
11 __docformat__ = "restructuredtext en"
11 __docformat__ = "restructuredtext en"
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 import string
23 import string
24 import uuid
24 import uuid
25 import _ast
25 import _ast
26
26
27 from zopeinterface import Interface, Attribute, implements, classProvides
27 from IPython.frontend.zopeinterface import (
28
28 Interface,
29 Attribute,
30 implements,
31 classProvides
32 )
29 from IPython.kernel.core.history import FrontEndHistory
33 from IPython.kernel.core.history import FrontEndHistory
30 from IPython.kernel.core.util import Bunch
34 from IPython.kernel.core.util import Bunch
31 from IPython.kernel.engineservice import IEngineCore
32
35
33 ##############################################################################
36 ##############################################################################
34 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
37 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
35 # not
38 # not
36
39
37 rc = Bunch()
40 rc = Bunch()
38 rc.prompt_in1 = r'In [$number]: '
41 rc.prompt_in1 = r'In [$number]: '
39 rc.prompt_in2 = r'...'
42 rc.prompt_in2 = r'...'
40 rc.prompt_out = r'Out [$number]: '
43 rc.prompt_out = r'Out [$number]: '
41
44
42 ##############################################################################
45 ##############################################################################
43 # Interface definitions
46 # Interface definitions
44 ##############################################################################
47 ##############################################################################
45
48
46 class IFrontEndFactory(Interface):
49 class IFrontEndFactory(Interface):
47 """Factory interface for frontends."""
50 """Factory interface for frontends."""
48
51
49 def __call__(engine=None, history=None):
52 def __call__(engine=None, history=None):
50 """
53 """
51 Parameters:
54 Parameters:
52 interpreter : IPython.kernel.engineservice.IEngineCore
55 interpreter : IPython.kernel.engineservice.IEngineCore
53 """
56 """
54
57
55 pass
58 pass
56
59
57
60
58 class IFrontEnd(Interface):
61 class IFrontEnd(Interface):
59 """Interface for frontends. All methods return t.i.d.Deferred"""
62 """Interface for frontends. All methods return t.i.d.Deferred"""
60
63
61 Attribute("input_prompt_template", "string.Template instance\
64 Attribute("input_prompt_template", "string.Template instance\
62 substituteable with execute result.")
65 substituteable with execute result.")
63 Attribute("output_prompt_template", "string.Template instance\
66 Attribute("output_prompt_template", "string.Template instance\
64 substituteable with execute result.")
67 substituteable with execute result.")
65 Attribute("continuation_prompt_template", "string.Template instance\
68 Attribute("continuation_prompt_template", "string.Template instance\
66 substituteable with execute result.")
69 substituteable with execute result.")
67
70
68 def update_cell_prompt(result, blockID=None):
71 def update_cell_prompt(result, blockID=None):
69 """Subclass may override to update the input prompt for a block.
72 """Subclass may override to update the input prompt for a block.
70
73
71 In asynchronous frontends, this method will be called as a
74 In asynchronous frontends, this method will be called as a
72 twisted.internet.defer.Deferred's callback/errback.
75 twisted.internet.defer.Deferred's callback/errback.
73 Implementations should thus return result when finished.
76 Implementations should thus return result when finished.
74
77
75 Result is a result dict in case of success, and a
78 Result is a result dict in case of success, and a
76 twisted.python.util.failure.Failure in case of an error
79 twisted.python.util.failure.Failure in case of an error
77 """
80 """
78
81
79 pass
82 pass
80
83
81 def render_result(result):
84 def render_result(result):
82 """Render the result of an execute call. Implementors may choose the
85 """Render the result of an execute call. Implementors may choose the
83 method of rendering.
86 method of rendering.
84 For example, a notebook-style frontend might render a Chaco plot
87 For example, a notebook-style frontend might render a Chaco plot
85 inline.
88 inline.
86
89
87 Parameters:
90 Parameters:
88 result : dict (result of IEngineBase.execute )
91 result : dict (result of IEngineBase.execute )
89 blockID = result['blockID']
92 blockID = result['blockID']
90
93
91 Result:
94 Result:
92 Output of frontend rendering
95 Output of frontend rendering
93 """
96 """
94
97
95 pass
98 pass
96
99
97 def render_error(failure):
100 def render_error(failure):
98 """Subclasses must override to render the failure.
101 """Subclasses must override to render the failure.
99
102
100 In asynchronous frontend, since this method will be called as a
103 In asynchronous frontend, since this method will be called as a
101 twisted.internet.defer.Deferred's callback. Implementations
104 twisted.internet.defer.Deferred's callback. Implementations
102 should thus return result when finished.
105 should thus return result when finished.
103
106
104 blockID = failure.blockID
107 blockID = failure.blockID
105 """
108 """
106
109
107 pass
110 pass
108
111
109 def input_prompt(number=''):
112 def input_prompt(number=''):
110 """Returns the input prompt by subsituting into
113 """Returns the input prompt by subsituting into
111 self.input_prompt_template
114 self.input_prompt_template
112 """
115 """
113 pass
116 pass
114
117
115 def output_prompt(number=''):
118 def output_prompt(number=''):
116 """Returns the output prompt by subsituting into
119 """Returns the output prompt by subsituting into
117 self.output_prompt_template
120 self.output_prompt_template
118 """
121 """
119
122
120 pass
123 pass
121
124
122 def continuation_prompt():
125 def continuation_prompt():
123 """Returns the continuation prompt by subsituting into
126 """Returns the continuation prompt by subsituting into
124 self.continuation_prompt_template
127 self.continuation_prompt_template
125 """
128 """
126
129
127 pass
130 pass
128
131
129 def is_complete(block):
132 def is_complete(block):
130 """Returns True if block is complete, False otherwise."""
133 """Returns True if block is complete, False otherwise."""
131
134
132 pass
135 pass
133
136
134 def compile_ast(block):
137 def compile_ast(block):
135 """Compiles block to an _ast.AST"""
138 """Compiles block to an _ast.AST"""
136
139
137 pass
140 pass
138
141
139 def get_history_previous(current_block):
142 def get_history_previous(current_block):
140 """Returns the block previous in the history. Saves currentBlock if
143 """Returns the block previous in the history. Saves currentBlock if
141 the history_cursor is currently at the end of the input history"""
144 the history_cursor is currently at the end of the input history"""
142 pass
145 pass
143
146
144 def get_history_next():
147 def get_history_next():
145 """Returns the next block in the history."""
148 """Returns the next block in the history."""
146
149
147 pass
150 pass
148
151
149 def complete(self, line):
152 def complete(self, line):
150 """Returns the list of possible completions, and the completed
153 """Returns the list of possible completions, and the completed
151 line.
154 line.
152
155
153 The input argument is the full line to be completed. This method
156 The input argument is the full line to be completed. This method
154 returns both the line completed as much as possible, and the list
157 returns both the line completed as much as possible, and the list
155 of further possible completions (full words).
158 of further possible completions (full words).
156 """
159 """
157 pass
160 pass
158
161
159
162
160 ##############################################################################
163 ##############################################################################
161 # Base class for all the frontends.
164 # Base class for all the frontends.
162 ##############################################################################
165 ##############################################################################
163
166
164 class FrontEndBase(object):
167 class FrontEndBase(object):
165 """
168 """
166 FrontEndBase manages the state tasks for a CLI frontend:
169 FrontEndBase manages the state tasks for a CLI frontend:
167 - Input and output history management
170 - Input and output history management
168 - Input/continuation and output prompt generation
171 - Input/continuation and output prompt generation
169
172
170 Some issues (due to possibly unavailable engine):
173 Some issues (due to possibly unavailable engine):
171 - How do we get the current cell number for the engine?
174 - How do we get the current cell number for the engine?
172 - How do we handle completions?
175 - How do we handle completions?
173 """
176 """
174
177
175 history_cursor = 0
178 history_cursor = 0
176
179
177 input_prompt_template = string.Template(rc.prompt_in1)
180 input_prompt_template = string.Template(rc.prompt_in1)
178 output_prompt_template = string.Template(rc.prompt_out)
181 output_prompt_template = string.Template(rc.prompt_out)
179 continuation_prompt_template = string.Template(rc.prompt_in2)
182 continuation_prompt_template = string.Template(rc.prompt_in2)
180
183
181 def __init__(self, shell=None, history=None):
184 def __init__(self, shell=None, history=None):
182 self.shell = shell
185 self.shell = shell
183 if history is None:
186 if history is None:
184 self.history = FrontEndHistory(input_cache=[''])
187 self.history = FrontEndHistory(input_cache=[''])
185 else:
188 else:
186 self.history = history
189 self.history = history
187
190
188
191
189 def input_prompt(self, number=''):
192 def input_prompt(self, number=''):
190 """Returns the current input prompt
193 """Returns the current input prompt
191
194
192 It would be great to use ipython1.core.prompts.Prompt1 here
195 It would be great to use ipython1.core.prompts.Prompt1 here
193 """
196 """
194 return self.input_prompt_template.safe_substitute({'number':number})
197 return self.input_prompt_template.safe_substitute({'number':number})
195
198
196
199
197 def continuation_prompt(self):
200 def continuation_prompt(self):
198 """Returns the current continuation prompt"""
201 """Returns the current continuation prompt"""
199
202
200 return self.continuation_prompt_template.safe_substitute()
203 return self.continuation_prompt_template.safe_substitute()
201
204
202 def output_prompt(self, number=''):
205 def output_prompt(self, number=''):
203 """Returns the output prompt for result"""
206 """Returns the output prompt for result"""
204
207
205 return self.output_prompt_template.safe_substitute({'number':number})
208 return self.output_prompt_template.safe_substitute({'number':number})
206
209
207
210
208 def is_complete(self, block):
211 def is_complete(self, block):
209 """Determine if block is complete.
212 """Determine if block is complete.
210
213
211 Parameters
214 Parameters
212 block : string
215 block : string
213
216
214 Result
217 Result
215 True if block can be sent to the engine without compile errors.
218 True if block can be sent to the engine without compile errors.
216 False otherwise.
219 False otherwise.
217 """
220 """
218
221
219 try:
222 try:
220 ast = self.compile_ast(block)
223 ast = self.compile_ast(block)
221 except:
224 except:
222 return False
225 return False
223
226
224 lines = block.split('\n')
227 lines = block.split('\n')
225 return (len(lines)==1 or str(lines[-1])=='')
228 return (len(lines)==1 or str(lines[-1])=='')
226
229
227
230
228 def compile_ast(self, block):
231 def compile_ast(self, block):
229 """Compile block to an AST
232 """Compile block to an AST
230
233
231 Parameters:
234 Parameters:
232 block : str
235 block : str
233
236
234 Result:
237 Result:
235 AST
238 AST
236
239
237 Throws:
240 Throws:
238 Exception if block cannot be compiled
241 Exception if block cannot be compiled
239 """
242 """
240
243
241 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
244 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
242
245
243
246
244 def execute(self, block, blockID=None):
247 def execute(self, block, blockID=None):
245 """Execute the block and return the result.
248 """Execute the block and return the result.
246
249
247 Parameters:
250 Parameters:
248 block : {str, AST}
251 block : {str, AST}
249 blockID : any
252 blockID : any
250 Caller may provide an ID to identify this block.
253 Caller may provide an ID to identify this block.
251 result['blockID'] := blockID
254 result['blockID'] := blockID
252
255
253 Result:
256 Result:
254 Deferred result of self.interpreter.execute
257 Deferred result of self.interpreter.execute
255 """
258 """
256
259
257 if(not self.is_complete(block)):
260 if(not self.is_complete(block)):
258 raise Exception("Block is not compilable")
261 raise Exception("Block is not compilable")
259
262
260 if(blockID == None):
263 if(blockID == None):
261 blockID = uuid.uuid4() #random UUID
264 blockID = uuid.uuid4() #random UUID
262
265
263 try:
266 try:
264 result = self.shell.execute(block)
267 result = self.shell.execute(block)
265 except Exception,e:
268 except Exception,e:
266 e = self._add_block_id_for_failure(e, blockID=blockID)
269 e = self._add_block_id_for_failure(e, blockID=blockID)
267 e = self.update_cell_prompt(e, blockID=blockID)
270 e = self.update_cell_prompt(e, blockID=blockID)
268 e = self.render_error(e)
271 e = self.render_error(e)
269 else:
272 else:
270 result = self._add_block_id_for_result(result, blockID=blockID)
273 result = self._add_block_id_for_result(result, blockID=blockID)
271 result = self.update_cell_prompt(result, blockID=blockID)
274 result = self.update_cell_prompt(result, blockID=blockID)
272 result = self.render_result(result)
275 result = self.render_result(result)
273
276
274 return result
277 return result
275
278
276
279
277 def _add_block_id_for_result(self, result, blockID):
280 def _add_block_id_for_result(self, result, blockID):
278 """Add the blockID to result or failure. Unfortunatley, we have to
281 """Add the blockID to result or failure. Unfortunatley, we have to
279 treat failures differently than result dicts.
282 treat failures differently than result dicts.
280 """
283 """
281
284
282 result['blockID'] = blockID
285 result['blockID'] = blockID
283
286
284 return result
287 return result
285
288
286 def _add_block_id_for_failure(self, failure, blockID):
289 def _add_block_id_for_failure(self, failure, blockID):
287 """_add_block_id_for_failure"""
290 """_add_block_id_for_failure"""
288 failure.blockID = blockID
291 failure.blockID = blockID
289 return failure
292 return failure
290
293
291
294
292 def _add_history(self, result, block=None):
295 def _add_history(self, result, block=None):
293 """Add block to the history"""
296 """Add block to the history"""
294
297
295 assert(block != None)
298 assert(block != None)
296 self.history.add_items([block])
299 self.history.add_items([block])
297 self.history_cursor += 1
300 self.history_cursor += 1
298
301
299 return result
302 return result
300
303
301
304
302 def get_history_previous(self, current_block):
305 def get_history_previous(self, current_block):
303 """ Returns previous history string and decrement history cursor.
306 """ Returns previous history string and decrement history cursor.
304 """
307 """
305 command = self.history.get_history_item(self.history_cursor - 1)
308 command = self.history.get_history_item(self.history_cursor - 1)
306
309
307 if command is not None:
310 if command is not None:
308 if(self.history_cursor+1 == len(self.history.input_cache)):
311 if(self.history_cursor+1 == len(self.history.input_cache)):
309 self.history.input_cache[self.history_cursor] = current_block
312 self.history.input_cache[self.history_cursor] = current_block
310 self.history_cursor -= 1
313 self.history_cursor -= 1
311 return command
314 return command
312
315
313
316
314 def get_history_next(self):
317 def get_history_next(self):
315 """ Returns next history string and increment history cursor.
318 """ Returns next history string and increment history cursor.
316 """
319 """
317 command = self.history.get_history_item(self.history_cursor+1)
320 command = self.history.get_history_item(self.history_cursor+1)
318
321
319 if command is not None:
322 if command is not None:
320 self.history_cursor += 1
323 self.history_cursor += 1
321 return command
324 return command
322
325
323 ###
326 ###
324 # Subclasses probably want to override these methods...
327 # Subclasses probably want to override these methods...
325 ###
328 ###
326
329
327 def update_cell_prompt(self, result, blockID=None):
330 def update_cell_prompt(self, result, blockID=None):
328 """Subclass may override to update the input prompt for a block.
331 """Subclass may override to update the input prompt for a block.
329
332
330 This method only really makes sens in asyncrhonous frontend.
333 This method only really makes sens in asyncrhonous frontend.
331 Since this method will be called as a
334 Since this method will be called as a
332 twisted.internet.defer.Deferred's callback, implementations should
335 twisted.internet.defer.Deferred's callback, implementations should
333 return result when finished.
336 return result when finished.
334 """
337 """
335
338
336 raise NotImplementedError
339 raise NotImplementedError
337
340
338
341
339 def render_result(self, result):
342 def render_result(self, result):
340 """Subclasses must override to render result.
343 """Subclasses must override to render result.
341
344
342 In asynchronous frontends, this method will be called as a
345 In asynchronous frontends, this method will be called as a
343 twisted.internet.defer.Deferred's callback. Implementations
346 twisted.internet.defer.Deferred's callback. Implementations
344 should thus return result when finished.
347 should thus return result when finished.
345 """
348 """
346
349
347 raise NotImplementedError
350 raise NotImplementedError
348
351
349
352
350 def render_error(self, failure):
353 def render_error(self, failure):
351 """Subclasses must override to render the failure.
354 """Subclasses must override to render the failure.
352
355
353 In asynchronous frontends, this method will be called as a
356 In asynchronous frontends, this method will be called as a
354 twisted.internet.defer.Deferred's callback. Implementations
357 twisted.internet.defer.Deferred's callback. Implementations
355 should thus return result when finished.
358 should thus return result when finished.
356 """
359 """
357
360
358 raise NotImplementedError
361 raise NotImplementedError
359
362
@@ -1,152 +1,157 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the frontendbase module."""
3 """This file contains unittests for the frontendbase module."""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #---------------------------------------------------------------------------
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
12 #---------------------------------------------------------------------------
13
13
14 #---------------------------------------------------------------------------
14 #---------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #---------------------------------------------------------------------------
16 #---------------------------------------------------------------------------
17
17
18 import unittest
18 import unittest
19 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
19
20 from IPython.frontend import frontendbase
20 try:
21 from IPython.kernel.engineservice import EngineService
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend import frontendbase
23 from IPython.kernel.engineservice import EngineService
24 except ImportError:
25 import nose
26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
22
27
23 class FrontEndCallbackChecker(AsyncFrontEndBase):
28 class FrontEndCallbackChecker(AsyncFrontEndBase):
24 """FrontEndBase subclass for checking callbacks"""
29 """FrontEndBase subclass for checking callbacks"""
25 def __init__(self, engine=None, history=None):
30 def __init__(self, engine=None, history=None):
26 super(FrontEndCallbackChecker, self).__init__(engine=engine,
31 super(FrontEndCallbackChecker, self).__init__(engine=engine,
27 history=history)
32 history=history)
28 self.updateCalled = False
33 self.updateCalled = False
29 self.renderResultCalled = False
34 self.renderResultCalled = False
30 self.renderErrorCalled = False
35 self.renderErrorCalled = False
31
36
32 def update_cell_prompt(self, result, blockID=None):
37 def update_cell_prompt(self, result, blockID=None):
33 self.updateCalled = True
38 self.updateCalled = True
34 return result
39 return result
35
40
36 def render_result(self, result):
41 def render_result(self, result):
37 self.renderResultCalled = True
42 self.renderResultCalled = True
38 return result
43 return result
39
44
40
45
41 def render_error(self, failure):
46 def render_error(self, failure):
42 self.renderErrorCalled = True
47 self.renderErrorCalled = True
43 return failure
48 return failure
44
49
45
50
46
51
47
52
48 class TestAsyncFrontendBase(unittest.TestCase):
53 class TestAsyncFrontendBase(unittest.TestCase):
49 def setUp(self):
54 def setUp(self):
50 """Setup the EngineService and FrontEndBase"""
55 """Setup the EngineService and FrontEndBase"""
51
56
52 self.fb = FrontEndCallbackChecker(engine=EngineService())
57 self.fb = FrontEndCallbackChecker(engine=EngineService())
53
58
54
59
55 def test_implements_IFrontEnd(self):
60 def test_implements_IFrontEnd(self):
56 assert(frontendbase.IFrontEnd.implementedBy(
61 assert(frontendbase.IFrontEnd.implementedBy(
57 AsyncFrontEndBase))
62 AsyncFrontEndBase))
58
63
59
64
60 def test_is_complete_returns_False_for_incomplete_block(self):
65 def test_is_complete_returns_False_for_incomplete_block(self):
61 """"""
66 """"""
62
67
63 block = """def test(a):"""
68 block = """def test(a):"""
64
69
65 assert(self.fb.is_complete(block) == False)
70 assert(self.fb.is_complete(block) == False)
66
71
67 def test_is_complete_returns_True_for_complete_block(self):
72 def test_is_complete_returns_True_for_complete_block(self):
68 """"""
73 """"""
69
74
70 block = """def test(a): pass"""
75 block = """def test(a): pass"""
71
76
72 assert(self.fb.is_complete(block))
77 assert(self.fb.is_complete(block))
73
78
74 block = """a=3"""
79 block = """a=3"""
75
80
76 assert(self.fb.is_complete(block))
81 assert(self.fb.is_complete(block))
77
82
78
83
79 def test_blockID_added_to_result(self):
84 def test_blockID_added_to_result(self):
80 block = """3+3"""
85 block = """3+3"""
81
86
82 d = self.fb.execute(block, blockID='TEST_ID')
87 d = self.fb.execute(block, blockID='TEST_ID')
83
88
84 d.addCallback(self.checkBlockID, expected='TEST_ID')
89 d.addCallback(self.checkBlockID, expected='TEST_ID')
85
90
86 def test_blockID_added_to_failure(self):
91 def test_blockID_added_to_failure(self):
87 block = "raise Exception()"
92 block = "raise Exception()"
88
93
89 d = self.fb.execute(block,blockID='TEST_ID')
94 d = self.fb.execute(block,blockID='TEST_ID')
90 d.addErrback(self.checkFailureID, expected='TEST_ID')
95 d.addErrback(self.checkFailureID, expected='TEST_ID')
91
96
92 def checkBlockID(self, result, expected=""):
97 def checkBlockID(self, result, expected=""):
93 assert(result['blockID'] == expected)
98 assert(result['blockID'] == expected)
94
99
95
100
96 def checkFailureID(self, failure, expected=""):
101 def checkFailureID(self, failure, expected=""):
97 assert(failure.blockID == expected)
102 assert(failure.blockID == expected)
98
103
99
104
100 def test_callbacks_added_to_execute(self):
105 def test_callbacks_added_to_execute(self):
101 """test that
106 """test that
102 update_cell_prompt
107 update_cell_prompt
103 render_result
108 render_result
104
109
105 are added to execute request
110 are added to execute request
106 """
111 """
107
112
108 d = self.fb.execute("10+10")
113 d = self.fb.execute("10+10")
109 d.addCallback(self.checkCallbacks)
114 d.addCallback(self.checkCallbacks)
110
115
111
116
112 def checkCallbacks(self, result):
117 def checkCallbacks(self, result):
113 assert(self.fb.updateCalled)
118 assert(self.fb.updateCalled)
114 assert(self.fb.renderResultCalled)
119 assert(self.fb.renderResultCalled)
115
120
116
121
117 def test_error_callback_added_to_execute(self):
122 def test_error_callback_added_to_execute(self):
118 """test that render_error called on execution error"""
123 """test that render_error called on execution error"""
119
124
120 d = self.fb.execute("raise Exception()")
125 d = self.fb.execute("raise Exception()")
121 d.addCallback(self.checkRenderError)
126 d.addCallback(self.checkRenderError)
122
127
123 def checkRenderError(self, result):
128 def checkRenderError(self, result):
124 assert(self.fb.renderErrorCalled)
129 assert(self.fb.renderErrorCalled)
125
130
126 def test_history_returns_expected_block(self):
131 def test_history_returns_expected_block(self):
127 """Make sure history browsing doesn't fail"""
132 """Make sure history browsing doesn't fail"""
128
133
129 blocks = ["a=1","a=2","a=3"]
134 blocks = ["a=1","a=2","a=3"]
130 for b in blocks:
135 for b in blocks:
131 d = self.fb.execute(b)
136 d = self.fb.execute(b)
132
137
133 # d is now the deferred for the last executed block
138 # d is now the deferred for the last executed block
134 d.addCallback(self.historyTests, blocks)
139 d.addCallback(self.historyTests, blocks)
135
140
136
141
137 def historyTests(self, result, blocks):
142 def historyTests(self, result, blocks):
138 """historyTests"""
143 """historyTests"""
139
144
140 assert(len(blocks) >= 3)
145 assert(len(blocks) >= 3)
141 assert(self.fb.get_history_previous("") == blocks[-2])
146 assert(self.fb.get_history_previous("") == blocks[-2])
142 assert(self.fb.get_history_previous("") == blocks[-3])
147 assert(self.fb.get_history_previous("") == blocks[-3])
143 assert(self.fb.get_history_next() == blocks[-2])
148 assert(self.fb.get_history_next() == blocks[-2])
144
149
145
150
146 def test_history_returns_none_at_startup(self):
151 def test_history_returns_none_at_startup(self):
147 """test_history_returns_none_at_startup"""
152 """test_history_returns_none_at_startup"""
148
153
149 assert(self.fb.get_history_previous("")==None)
154 assert(self.fb.get_history_previous("")==None)
150 assert(self.fb.get_history_next()==None)
155 assert(self.fb.get_history_next()==None)
151
156
152
157
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
@@ -1,828 +1,827 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """"""
3 """"""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 from twisted.internet import defer
18 from twisted.internet import defer
19
19
20 from IPython.kernel import engineservice as es
20 from IPython.kernel import engineservice as es
21 from IPython.kernel import multiengine as me
21 from IPython.kernel import multiengine as me
22 from IPython.kernel import newserialized
22 from IPython.kernel import newserialized
23 from IPython.kernel.error import NotDefined
24 from IPython.testing import util
23 from IPython.testing import util
25 from IPython.testing.parametric import parametric, Parametric
24 from IPython.testing.parametric import parametric, Parametric
26 from IPython.kernel import newserialized
25 from IPython.kernel import newserialized
27 from IPython.kernel.util import printer
26 from IPython.kernel.util import printer
28 from IPython.kernel.error import (InvalidEngineID,
27 from IPython.kernel.error import (InvalidEngineID,
29 NoEnginesRegistered,
28 NoEnginesRegistered,
30 CompositeError,
29 CompositeError,
31 InvalidDeferredID)
30 InvalidDeferredID)
32 from IPython.kernel.tests.engineservicetest import validCommands, invalidCommands
31 from IPython.kernel.tests.engineservicetest import validCommands, invalidCommands
33 from IPython.kernel.core.interpreter import Interpreter
32 from IPython.kernel.core.interpreter import Interpreter
34
33
35
34
36 #-------------------------------------------------------------------------------
35 #-------------------------------------------------------------------------------
37 # Base classes and utilities
36 # Base classes and utilities
38 #-------------------------------------------------------------------------------
37 #-------------------------------------------------------------------------------
39
38
40 class IMultiEngineBaseTestCase(object):
39 class IMultiEngineBaseTestCase(object):
41 """Basic utilities for working with multiengine tests.
40 """Basic utilities for working with multiengine tests.
42
41
43 Some subclass should define:
42 Some subclass should define:
44
43
45 * self.multiengine
44 * self.multiengine
46 * self.engines to keep track of engines for clean up"""
45 * self.engines to keep track of engines for clean up"""
47
46
48 def createShell(self):
47 def createShell(self):
49 return Interpreter()
48 return Interpreter()
50
49
51 def addEngine(self, n=1):
50 def addEngine(self, n=1):
52 for i in range(n):
51 for i in range(n):
53 e = es.EngineService()
52 e = es.EngineService()
54 e.startService()
53 e.startService()
55 regDict = self.controller.register_engine(es.QueuedEngine(e), None)
54 regDict = self.controller.register_engine(es.QueuedEngine(e), None)
56 e.id = regDict['id']
55 e.id = regDict['id']
57 self.engines.append(e)
56 self.engines.append(e)
58
57
59
58
60 def testf(x):
59 def testf(x):
61 return 2.0*x
60 return 2.0*x
62
61
63
62
64 globala = 99
63 globala = 99
65
64
66
65
67 def testg(x):
66 def testg(x):
68 return globala*x
67 return globala*x
69
68
70
69
71 def isdid(did):
70 def isdid(did):
72 if not isinstance(did, str):
71 if not isinstance(did, str):
73 return False
72 return False
74 if not len(did)==40:
73 if not len(did)==40:
75 return False
74 return False
76 return True
75 return True
77
76
78
77
79 def _raise_it(f):
78 def _raise_it(f):
80 try:
79 try:
81 f.raiseException()
80 f.raiseException()
82 except CompositeError, e:
81 except CompositeError, e:
83 e.raise_exception()
82 e.raise_exception()
84
83
85 #-------------------------------------------------------------------------------
84 #-------------------------------------------------------------------------------
86 # IMultiEngineTestCase
85 # IMultiEngineTestCase
87 #-------------------------------------------------------------------------------
86 #-------------------------------------------------------------------------------
88
87
89 class IMultiEngineTestCase(IMultiEngineBaseTestCase):
88 class IMultiEngineTestCase(IMultiEngineBaseTestCase):
90 """A test for any object that implements IEngineMultiplexer.
89 """A test for any object that implements IEngineMultiplexer.
91
90
92 self.multiengine must be defined and implement IEngineMultiplexer.
91 self.multiengine must be defined and implement IEngineMultiplexer.
93 """
92 """
94
93
95 def testIMultiEngineInterface(self):
94 def testIMultiEngineInterface(self):
96 """Does self.engine claim to implement IEngineCore?"""
95 """Does self.engine claim to implement IEngineCore?"""
97 self.assert_(me.IEngineMultiplexer.providedBy(self.multiengine))
96 self.assert_(me.IEngineMultiplexer.providedBy(self.multiengine))
98 self.assert_(me.IMultiEngine.providedBy(self.multiengine))
97 self.assert_(me.IMultiEngine.providedBy(self.multiengine))
99
98
100 def testIEngineMultiplexerInterfaceMethods(self):
99 def testIEngineMultiplexerInterfaceMethods(self):
101 """Does self.engine have the methods and attributes in IEngineCore."""
100 """Does self.engine have the methods and attributes in IEngineCore."""
102 for m in list(me.IEngineMultiplexer):
101 for m in list(me.IEngineMultiplexer):
103 self.assert_(hasattr(self.multiengine, m))
102 self.assert_(hasattr(self.multiengine, m))
104
103
105 def testIEngineMultiplexerDeferreds(self):
104 def testIEngineMultiplexerDeferreds(self):
106 self.addEngine(1)
105 self.addEngine(1)
107 d= self.multiengine.execute('a=5', targets=0)
106 d= self.multiengine.execute('a=5', targets=0)
108 d.addCallback(lambda _: self.multiengine.push(dict(a=5),targets=0))
107 d.addCallback(lambda _: self.multiengine.push(dict(a=5),targets=0))
109 d.addCallback(lambda _: self.multiengine.push(dict(a=5, b='asdf', c=[1,2,3]),targets=0))
108 d.addCallback(lambda _: self.multiengine.push(dict(a=5, b='asdf', c=[1,2,3]),targets=0))
110 d.addCallback(lambda _: self.multiengine.pull(('a','b','c'),targets=0))
109 d.addCallback(lambda _: self.multiengine.pull(('a','b','c'),targets=0))
111 d.addCallback(lambda _: self.multiengine.get_result(targets=0))
110 d.addCallback(lambda _: self.multiengine.get_result(targets=0))
112 d.addCallback(lambda _: self.multiengine.reset(targets=0))
111 d.addCallback(lambda _: self.multiengine.reset(targets=0))
113 d.addCallback(lambda _: self.multiengine.keys(targets=0))
112 d.addCallback(lambda _: self.multiengine.keys(targets=0))
114 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)),targets=0))
113 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)),targets=0))
115 d.addCallback(lambda _: self.multiengine.pull_serialized('a',targets=0))
114 d.addCallback(lambda _: self.multiengine.pull_serialized('a',targets=0))
116 d.addCallback(lambda _: self.multiengine.clear_queue(targets=0))
115 d.addCallback(lambda _: self.multiengine.clear_queue(targets=0))
117 d.addCallback(lambda _: self.multiengine.queue_status(targets=0))
116 d.addCallback(lambda _: self.multiengine.queue_status(targets=0))
118 return d
117 return d
119
118
120 def testInvalidEngineID(self):
119 def testInvalidEngineID(self):
121 self.addEngine(1)
120 self.addEngine(1)
122 badID = 100
121 badID = 100
123 d = self.multiengine.execute('a=5', targets=badID)
122 d = self.multiengine.execute('a=5', targets=badID)
124 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
123 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
125 d.addCallback(lambda _: self.multiengine.push(dict(a=5), targets=badID))
124 d.addCallback(lambda _: self.multiengine.push(dict(a=5), targets=badID))
126 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
125 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
127 d.addCallback(lambda _: self.multiengine.pull('a', targets=badID))
126 d.addCallback(lambda _: self.multiengine.pull('a', targets=badID))
128 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
127 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
129 d.addCallback(lambda _: self.multiengine.reset(targets=badID))
128 d.addCallback(lambda _: self.multiengine.reset(targets=badID))
130 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
129 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
131 d.addCallback(lambda _: self.multiengine.keys(targets=badID))
130 d.addCallback(lambda _: self.multiengine.keys(targets=badID))
132 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
131 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
133 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)), targets=badID))
132 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)), targets=badID))
134 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
133 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
135 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=badID))
134 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=badID))
136 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
135 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
137 d.addCallback(lambda _: self.multiengine.queue_status(targets=badID))
136 d.addCallback(lambda _: self.multiengine.queue_status(targets=badID))
138 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
137 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
139 return d
138 return d
140
139
141 def testNoEnginesRegistered(self):
140 def testNoEnginesRegistered(self):
142 badID = 'all'
141 badID = 'all'
143 d= self.multiengine.execute('a=5', targets=badID)
142 d= self.multiengine.execute('a=5', targets=badID)
144 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
143 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
145 d.addCallback(lambda _: self.multiengine.push(dict(a=5), targets=badID))
144 d.addCallback(lambda _: self.multiengine.push(dict(a=5), targets=badID))
146 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
145 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
147 d.addCallback(lambda _: self.multiengine.pull('a', targets=badID))
146 d.addCallback(lambda _: self.multiengine.pull('a', targets=badID))
148 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
147 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
149 d.addCallback(lambda _: self.multiengine.get_result(targets=badID))
148 d.addCallback(lambda _: self.multiengine.get_result(targets=badID))
150 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
149 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
151 d.addCallback(lambda _: self.multiengine.reset(targets=badID))
150 d.addCallback(lambda _: self.multiengine.reset(targets=badID))
152 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
151 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
153 d.addCallback(lambda _: self.multiengine.keys(targets=badID))
152 d.addCallback(lambda _: self.multiengine.keys(targets=badID))
154 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
153 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
155 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)), targets=badID))
154 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)), targets=badID))
156 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
155 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
157 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=badID))
156 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=badID))
158 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
157 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
159 d.addCallback(lambda _: self.multiengine.queue_status(targets=badID))
158 d.addCallback(lambda _: self.multiengine.queue_status(targets=badID))
160 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
159 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
161 return d
160 return d
162
161
163 def runExecuteAll(self, d, cmd, shell):
162 def runExecuteAll(self, d, cmd, shell):
164 actual = shell.execute(cmd)
163 actual = shell.execute(cmd)
165 d.addCallback(lambda _: self.multiengine.execute(cmd))
164 d.addCallback(lambda _: self.multiengine.execute(cmd))
166 def compare(result):
165 def compare(result):
167 for r in result:
166 for r in result:
168 actual['id'] = r['id']
167 actual['id'] = r['id']
169 self.assertEquals(r, actual)
168 self.assertEquals(r, actual)
170 d.addCallback(compare)
169 d.addCallback(compare)
171
170
172 def testExecuteAll(self):
171 def testExecuteAll(self):
173 self.addEngine(4)
172 self.addEngine(4)
174 d= defer.Deferred()
173 d= defer.Deferred()
175 shell = Interpreter()
174 shell = Interpreter()
176 for cmd in validCommands:
175 for cmd in validCommands:
177 self.runExecuteAll(d, cmd, shell)
176 self.runExecuteAll(d, cmd, shell)
178 d.callback(None)
177 d.callback(None)
179 return d
178 return d
180
179
181 # The following two methods show how to do parametrized
180 # The following two methods show how to do parametrized
182 # tests. This is really slick! Same is used above.
181 # tests. This is really slick! Same is used above.
183 def runExecuteFailures(self, cmd, exc):
182 def runExecuteFailures(self, cmd, exc):
184 self.addEngine(4)
183 self.addEngine(4)
185 d= self.multiengine.execute(cmd)
184 d= self.multiengine.execute(cmd)
186 d.addErrback(lambda f: self.assertRaises(exc, _raise_it, f))
185 d.addErrback(lambda f: self.assertRaises(exc, _raise_it, f))
187 return d
186 return d
188
187
189 @parametric
188 @parametric
190 def testExecuteFailuresMultiEng(cls):
189 def testExecuteFailuresMultiEng(cls):
191 return [(cls.runExecuteFailures,cmd,exc) for
190 return [(cls.runExecuteFailures,cmd,exc) for
192 cmd,exc in invalidCommands]
191 cmd,exc in invalidCommands]
193
192
194 def testPushPull(self):
193 def testPushPull(self):
195 self.addEngine(1)
194 self.addEngine(1)
196 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
195 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
197 d= self.multiengine.push(dict(key=objs[0]), targets=0)
196 d= self.multiengine.push(dict(key=objs[0]), targets=0)
198 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
197 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
199 d.addCallback(lambda r: self.assertEquals(r, [objs[0]]))
198 d.addCallback(lambda r: self.assertEquals(r, [objs[0]]))
200 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[1]), targets=0))
199 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[1]), targets=0))
201 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
200 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
202 d.addCallback(lambda r: self.assertEquals(r, [objs[1]]))
201 d.addCallback(lambda r: self.assertEquals(r, [objs[1]]))
203 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[2]), targets=0))
202 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[2]), targets=0))
204 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
203 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
205 d.addCallback(lambda r: self.assertEquals(r, [objs[2]]))
204 d.addCallback(lambda r: self.assertEquals(r, [objs[2]]))
206 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[3]), targets=0))
205 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[3]), targets=0))
207 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
206 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
208 d.addCallback(lambda r: self.assertEquals(r, [objs[3]]))
207 d.addCallback(lambda r: self.assertEquals(r, [objs[3]]))
209 d.addCallback(lambda _: self.multiengine.reset(targets=0))
208 d.addCallback(lambda _: self.multiengine.reset(targets=0))
210 d.addCallback(lambda _: self.multiengine.pull('a', targets=0))
209 d.addCallback(lambda _: self.multiengine.pull('a', targets=0))
211 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
210 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
212 d.addCallback(lambda _: self.multiengine.push(dict(a=10,b=20)))
211 d.addCallback(lambda _: self.multiengine.push(dict(a=10,b=20)))
213 d.addCallback(lambda _: self.multiengine.pull(('a','b')))
212 d.addCallback(lambda _: self.multiengine.pull(('a','b')))
214 d.addCallback(lambda r: self.assertEquals(r, [[10,20]]))
213 d.addCallback(lambda r: self.assertEquals(r, [[10,20]]))
215 return d
214 return d
216
215
217 def testPushPullAll(self):
216 def testPushPullAll(self):
218 self.addEngine(4)
217 self.addEngine(4)
219 d= self.multiengine.push(dict(a=10))
218 d= self.multiengine.push(dict(a=10))
220 d.addCallback(lambda _: self.multiengine.pull('a'))
219 d.addCallback(lambda _: self.multiengine.pull('a'))
221 d.addCallback(lambda r: self.assert_(r==[10,10,10,10]))
220 d.addCallback(lambda r: self.assert_(r==[10,10,10,10]))
222 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20)))
221 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20)))
223 d.addCallback(lambda _: self.multiengine.pull(('a','b')))
222 d.addCallback(lambda _: self.multiengine.pull(('a','b')))
224 d.addCallback(lambda r: self.assert_(r==4*[[10,20]]))
223 d.addCallback(lambda r: self.assert_(r==4*[[10,20]]))
225 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20), targets=0))
224 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20), targets=0))
226 d.addCallback(lambda _: self.multiengine.pull(('a','b'), targets=0))
225 d.addCallback(lambda _: self.multiengine.pull(('a','b'), targets=0))
227 d.addCallback(lambda r: self.assert_(r==[[10,20]]))
226 d.addCallback(lambda r: self.assert_(r==[[10,20]]))
228 d.addCallback(lambda _: self.multiengine.push(dict(a=None, b=None), targets=0))
227 d.addCallback(lambda _: self.multiengine.push(dict(a=None, b=None), targets=0))
229 d.addCallback(lambda _: self.multiengine.pull(('a','b'), targets=0))
228 d.addCallback(lambda _: self.multiengine.pull(('a','b'), targets=0))
230 d.addCallback(lambda r: self.assert_(r==[[None,None]]))
229 d.addCallback(lambda r: self.assert_(r==[[None,None]]))
231 return d
230 return d
232
231
233 def testPushPullSerialized(self):
232 def testPushPullSerialized(self):
234 self.addEngine(1)
233 self.addEngine(1)
235 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
234 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
236 d= self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[0])), targets=0)
235 d= self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[0])), targets=0)
237 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
236 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
238 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
237 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
239 d.addCallback(lambda r: self.assertEquals(r, objs[0]))
238 d.addCallback(lambda r: self.assertEquals(r, objs[0]))
240 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[1])), targets=0))
239 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[1])), targets=0))
241 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
240 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
242 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
241 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
243 d.addCallback(lambda r: self.assertEquals(r, objs[1]))
242 d.addCallback(lambda r: self.assertEquals(r, objs[1]))
244 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[2])), targets=0))
243 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[2])), targets=0))
245 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
244 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
246 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
245 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
247 d.addCallback(lambda r: self.assertEquals(r, objs[2]))
246 d.addCallback(lambda r: self.assertEquals(r, objs[2]))
248 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[3])), targets=0))
247 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[3])), targets=0))
249 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
248 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
250 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
249 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
251 d.addCallback(lambda r: self.assertEquals(r, objs[3]))
250 d.addCallback(lambda r: self.assertEquals(r, objs[3]))
252 d.addCallback(lambda _: self.multiengine.push(dict(a=10,b=range(5)), targets=0))
251 d.addCallback(lambda _: self.multiengine.push(dict(a=10,b=range(5)), targets=0))
253 d.addCallback(lambda _: self.multiengine.pull_serialized(('a','b'), targets=0))
252 d.addCallback(lambda _: self.multiengine.pull_serialized(('a','b'), targets=0))
254 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
253 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
255 d.addCallback(lambda r: self.assertEquals(r, [10, range(5)]))
254 d.addCallback(lambda r: self.assertEquals(r, [10, range(5)]))
256 d.addCallback(lambda _: self.multiengine.reset(targets=0))
255 d.addCallback(lambda _: self.multiengine.reset(targets=0))
257 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
256 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
258 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
257 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
259 return d
258 return d
260
259
261 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
260 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
262 d= defer.succeed(None)
261 d= defer.succeed(None)
263 for o in objs:
262 for o in objs:
264 self.multiengine.push_serialized(0, key=newserialized.serialize(o))
263 self.multiengine.push_serialized(0, key=newserialized.serialize(o))
265 value = self.multiengine.pull_serialized(0, 'key')
264 value = self.multiengine.pull_serialized(0, 'key')
266 value.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
265 value.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
267 d = self.assertDeferredEquals(value,o,d)
266 d = self.assertDeferredEquals(value,o,d)
268 return d
267 return d
269
268
270 def runGetResultAll(self, d, cmd, shell):
269 def runGetResultAll(self, d, cmd, shell):
271 actual = shell.execute(cmd)
270 actual = shell.execute(cmd)
272 d.addCallback(lambda _: self.multiengine.execute(cmd))
271 d.addCallback(lambda _: self.multiengine.execute(cmd))
273 d.addCallback(lambda _: self.multiengine.get_result())
272 d.addCallback(lambda _: self.multiengine.get_result())
274 def compare(result):
273 def compare(result):
275 for r in result:
274 for r in result:
276 actual['id'] = r['id']
275 actual['id'] = r['id']
277 self.assertEquals(r, actual)
276 self.assertEquals(r, actual)
278 d.addCallback(compare)
277 d.addCallback(compare)
279
278
280 def testGetResultAll(self):
279 def testGetResultAll(self):
281 self.addEngine(4)
280 self.addEngine(4)
282 d= defer.Deferred()
281 d= defer.Deferred()
283 shell = Interpreter()
282 shell = Interpreter()
284 for cmd in validCommands:
283 for cmd in validCommands:
285 self.runGetResultAll(d, cmd, shell)
284 self.runGetResultAll(d, cmd, shell)
286 d.callback(None)
285 d.callback(None)
287 return d
286 return d
288
287
289 def testGetResultDefault(self):
288 def testGetResultDefault(self):
290 self.addEngine(1)
289 self.addEngine(1)
291 target = 0
290 target = 0
292 cmd = 'a=5'
291 cmd = 'a=5'
293 shell = self.createShell()
292 shell = self.createShell()
294 shellResult = shell.execute(cmd)
293 shellResult = shell.execute(cmd)
295 def popit(dikt, key):
294 def popit(dikt, key):
296 dikt.pop(key)
295 dikt.pop(key)
297 return dikt
296 return dikt
298 d= self.multiengine.execute(cmd, targets=target)
297 d= self.multiengine.execute(cmd, targets=target)
299 d.addCallback(lambda _: self.multiengine.get_result(targets=target))
298 d.addCallback(lambda _: self.multiengine.get_result(targets=target))
300 d.addCallback(lambda r: self.assertEquals(shellResult, popit(r[0],'id')))
299 d.addCallback(lambda r: self.assertEquals(shellResult, popit(r[0],'id')))
301 return d
300 return d
302
301
303 def testGetResultFailure(self):
302 def testGetResultFailure(self):
304 self.addEngine(1)
303 self.addEngine(1)
305 d= self.multiengine.get_result(None, targets=0)
304 d= self.multiengine.get_result(None, targets=0)
306 d.addErrback(lambda f: self.assertRaises(IndexError, _raise_it, f))
305 d.addErrback(lambda f: self.assertRaises(IndexError, _raise_it, f))
307 d.addCallback(lambda _: self.multiengine.get_result(10, targets=0))
306 d.addCallback(lambda _: self.multiengine.get_result(10, targets=0))
308 d.addErrback(lambda f: self.assertRaises(IndexError, _raise_it, f))
307 d.addErrback(lambda f: self.assertRaises(IndexError, _raise_it, f))
309 return d
308 return d
310
309
311 def testPushFunction(self):
310 def testPushFunction(self):
312 self.addEngine(1)
311 self.addEngine(1)
313 d= self.multiengine.push_function(dict(f=testf), targets=0)
312 d= self.multiengine.push_function(dict(f=testf), targets=0)
314 d.addCallback(lambda _: self.multiengine.execute('result = f(10)', targets=0))
313 d.addCallback(lambda _: self.multiengine.execute('result = f(10)', targets=0))
315 d.addCallback(lambda _: self.multiengine.pull('result', targets=0))
314 d.addCallback(lambda _: self.multiengine.pull('result', targets=0))
316 d.addCallback(lambda r: self.assertEquals(r[0], testf(10)))
315 d.addCallback(lambda r: self.assertEquals(r[0], testf(10)))
317 d.addCallback(lambda _: self.multiengine.push(dict(globala=globala), targets=0))
316 d.addCallback(lambda _: self.multiengine.push(dict(globala=globala), targets=0))
318 d.addCallback(lambda _: self.multiengine.push_function(dict(g=testg), targets=0))
317 d.addCallback(lambda _: self.multiengine.push_function(dict(g=testg), targets=0))
319 d.addCallback(lambda _: self.multiengine.execute('result = g(10)', targets=0))
318 d.addCallback(lambda _: self.multiengine.execute('result = g(10)', targets=0))
320 d.addCallback(lambda _: self.multiengine.pull('result', targets=0))
319 d.addCallback(lambda _: self.multiengine.pull('result', targets=0))
321 d.addCallback(lambda r: self.assertEquals(r[0], testg(10)))
320 d.addCallback(lambda r: self.assertEquals(r[0], testg(10)))
322 return d
321 return d
323
322
324 def testPullFunction(self):
323 def testPullFunction(self):
325 self.addEngine(1)
324 self.addEngine(1)
326 d= self.multiengine.push(dict(a=globala), targets=0)
325 d= self.multiengine.push(dict(a=globala), targets=0)
327 d.addCallback(lambda _: self.multiengine.push_function(dict(f=testf), targets=0))
326 d.addCallback(lambda _: self.multiengine.push_function(dict(f=testf), targets=0))
328 d.addCallback(lambda _: self.multiengine.pull_function('f', targets=0))
327 d.addCallback(lambda _: self.multiengine.pull_function('f', targets=0))
329 d.addCallback(lambda r: self.assertEquals(r[0](10), testf(10)))
328 d.addCallback(lambda r: self.assertEquals(r[0](10), testf(10)))
330 d.addCallback(lambda _: self.multiengine.execute("def g(x): return x*x", targets=0))
329 d.addCallback(lambda _: self.multiengine.execute("def g(x): return x*x", targets=0))
331 d.addCallback(lambda _: self.multiengine.pull_function(('f','g'),targets=0))
330 d.addCallback(lambda _: self.multiengine.pull_function(('f','g'),targets=0))
332 d.addCallback(lambda r: self.assertEquals((r[0][0](10),r[0][1](10)), (testf(10), 100)))
331 d.addCallback(lambda r: self.assertEquals((r[0][0](10),r[0][1](10)), (testf(10), 100)))
333 return d
332 return d
334
333
335 def testPushFunctionAll(self):
334 def testPushFunctionAll(self):
336 self.addEngine(4)
335 self.addEngine(4)
337 d= self.multiengine.push_function(dict(f=testf))
336 d= self.multiengine.push_function(dict(f=testf))
338 d.addCallback(lambda _: self.multiengine.execute('result = f(10)'))
337 d.addCallback(lambda _: self.multiengine.execute('result = f(10)'))
339 d.addCallback(lambda _: self.multiengine.pull('result'))
338 d.addCallback(lambda _: self.multiengine.pull('result'))
340 d.addCallback(lambda r: self.assertEquals(r, 4*[testf(10)]))
339 d.addCallback(lambda r: self.assertEquals(r, 4*[testf(10)]))
341 d.addCallback(lambda _: self.multiengine.push(dict(globala=globala)))
340 d.addCallback(lambda _: self.multiengine.push(dict(globala=globala)))
342 d.addCallback(lambda _: self.multiengine.push_function(dict(testg=testg)))
341 d.addCallback(lambda _: self.multiengine.push_function(dict(testg=testg)))
343 d.addCallback(lambda _: self.multiengine.execute('result = testg(10)'))
342 d.addCallback(lambda _: self.multiengine.execute('result = testg(10)'))
344 d.addCallback(lambda _: self.multiengine.pull('result'))
343 d.addCallback(lambda _: self.multiengine.pull('result'))
345 d.addCallback(lambda r: self.assertEquals(r, 4*[testg(10)]))
344 d.addCallback(lambda r: self.assertEquals(r, 4*[testg(10)]))
346 return d
345 return d
347
346
348 def testPullFunctionAll(self):
347 def testPullFunctionAll(self):
349 self.addEngine(4)
348 self.addEngine(4)
350 d= self.multiengine.push_function(dict(f=testf))
349 d= self.multiengine.push_function(dict(f=testf))
351 d.addCallback(lambda _: self.multiengine.pull_function('f'))
350 d.addCallback(lambda _: self.multiengine.pull_function('f'))
352 d.addCallback(lambda r: self.assertEquals([func(10) for func in r], 4*[testf(10)]))
351 d.addCallback(lambda r: self.assertEquals([func(10) for func in r], 4*[testf(10)]))
353 return d
352 return d
354
353
355 def testGetIDs(self):
354 def testGetIDs(self):
356 self.addEngine(1)
355 self.addEngine(1)
357 d= self.multiengine.get_ids()
356 d= self.multiengine.get_ids()
358 d.addCallback(lambda r: self.assertEquals(r, [0]))
357 d.addCallback(lambda r: self.assertEquals(r, [0]))
359 d.addCallback(lambda _: self.addEngine(3))
358 d.addCallback(lambda _: self.addEngine(3))
360 d.addCallback(lambda _: self.multiengine.get_ids())
359 d.addCallback(lambda _: self.multiengine.get_ids())
361 d.addCallback(lambda r: self.assertEquals(r, [0,1,2,3]))
360 d.addCallback(lambda r: self.assertEquals(r, [0,1,2,3]))
362 return d
361 return d
363
362
364 def testClearQueue(self):
363 def testClearQueue(self):
365 self.addEngine(4)
364 self.addEngine(4)
366 d= self.multiengine.clear_queue()
365 d= self.multiengine.clear_queue()
367 d.addCallback(lambda r: self.assertEquals(r,4*[None]))
366 d.addCallback(lambda r: self.assertEquals(r,4*[None]))
368 return d
367 return d
369
368
370 def testQueueStatus(self):
369 def testQueueStatus(self):
371 self.addEngine(4)
370 self.addEngine(4)
372 d= self.multiengine.queue_status(targets=0)
371 d= self.multiengine.queue_status(targets=0)
373 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
372 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
374 return d
373 return d
375
374
376 def testGetSetProperties(self):
375 def testGetSetProperties(self):
377 self.addEngine(4)
376 self.addEngine(4)
378 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
377 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
379 d= self.multiengine.set_properties(dikt)
378 d= self.multiengine.set_properties(dikt)
380 d.addCallback(lambda r: self.multiengine.get_properties())
379 d.addCallback(lambda r: self.multiengine.get_properties())
381 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
380 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
382 d.addCallback(lambda r: self.multiengine.get_properties(('c',)))
381 d.addCallback(lambda r: self.multiengine.get_properties(('c',)))
383 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
382 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
384 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False)))
383 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False)))
385 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd')))
384 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd')))
386 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
385 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
387 return d
386 return d
388
387
389 def testClearProperties(self):
388 def testClearProperties(self):
390 self.addEngine(4)
389 self.addEngine(4)
391 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
390 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
392 d= self.multiengine.set_properties(dikt)
391 d= self.multiengine.set_properties(dikt)
393 d.addCallback(lambda r: self.multiengine.clear_properties())
392 d.addCallback(lambda r: self.multiengine.clear_properties())
394 d.addCallback(lambda r: self.multiengine.get_properties())
393 d.addCallback(lambda r: self.multiengine.get_properties())
395 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
394 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
396 return d
395 return d
397
396
398 def testDelHasProperties(self):
397 def testDelHasProperties(self):
399 self.addEngine(4)
398 self.addEngine(4)
400 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
399 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
401 d= self.multiengine.set_properties(dikt)
400 d= self.multiengine.set_properties(dikt)
402 d.addCallback(lambda r: self.multiengine.del_properties(('b','e')))
401 d.addCallback(lambda r: self.multiengine.del_properties(('b','e')))
403 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e')))
402 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e')))
404 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
403 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
405 return d
404 return d
406
405
407 Parametric(IMultiEngineTestCase)
406 Parametric(IMultiEngineTestCase)
408
407
409 #-------------------------------------------------------------------------------
408 #-------------------------------------------------------------------------------
410 # ISynchronousMultiEngineTestCase
409 # ISynchronousMultiEngineTestCase
411 #-------------------------------------------------------------------------------
410 #-------------------------------------------------------------------------------
412
411
413 class ISynchronousMultiEngineTestCase(IMultiEngineBaseTestCase):
412 class ISynchronousMultiEngineTestCase(IMultiEngineBaseTestCase):
414
413
415 def testISynchronousMultiEngineInterface(self):
414 def testISynchronousMultiEngineInterface(self):
416 """Does self.engine claim to implement IEngineCore?"""
415 """Does self.engine claim to implement IEngineCore?"""
417 self.assert_(me.ISynchronousEngineMultiplexer.providedBy(self.multiengine))
416 self.assert_(me.ISynchronousEngineMultiplexer.providedBy(self.multiengine))
418 self.assert_(me.ISynchronousMultiEngine.providedBy(self.multiengine))
417 self.assert_(me.ISynchronousMultiEngine.providedBy(self.multiengine))
419
418
420 def testExecute(self):
419 def testExecute(self):
421 self.addEngine(4)
420 self.addEngine(4)
422 execute = self.multiengine.execute
421 execute = self.multiengine.execute
423 d= execute('a=5', targets=0, block=True)
422 d= execute('a=5', targets=0, block=True)
424 d.addCallback(lambda r: self.assert_(len(r)==1))
423 d.addCallback(lambda r: self.assert_(len(r)==1))
425 d.addCallback(lambda _: execute('b=10'))
424 d.addCallback(lambda _: execute('b=10'))
426 d.addCallback(lambda r: self.assert_(len(r)==4))
425 d.addCallback(lambda r: self.assert_(len(r)==4))
427 d.addCallback(lambda _: execute('c=30', block=False))
426 d.addCallback(lambda _: execute('c=30', block=False))
428 d.addCallback(lambda did: self.assert_(isdid(did)))
427 d.addCallback(lambda did: self.assert_(isdid(did)))
429 d.addCallback(lambda _: execute('d=[0,1,2]', block=False))
428 d.addCallback(lambda _: execute('d=[0,1,2]', block=False))
430 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
429 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
431 d.addCallback(lambda r: self.assert_(len(r)==4))
430 d.addCallback(lambda r: self.assert_(len(r)==4))
432 return d
431 return d
433
432
434 def testPushPull(self):
433 def testPushPull(self):
435 data = dict(a=10, b=1.05, c=range(10), d={'e':(1,2),'f':'hi'})
434 data = dict(a=10, b=1.05, c=range(10), d={'e':(1,2),'f':'hi'})
436 self.addEngine(4)
435 self.addEngine(4)
437 push = self.multiengine.push
436 push = self.multiengine.push
438 pull = self.multiengine.pull
437 pull = self.multiengine.pull
439 d= push({'data':data}, targets=0)
438 d= push({'data':data}, targets=0)
440 d.addCallback(lambda r: pull('data', targets=0))
439 d.addCallback(lambda r: pull('data', targets=0))
441 d.addCallback(lambda r: self.assertEqual(r,[data]))
440 d.addCallback(lambda r: self.assertEqual(r,[data]))
442 d.addCallback(lambda _: push({'data':data}))
441 d.addCallback(lambda _: push({'data':data}))
443 d.addCallback(lambda r: pull('data'))
442 d.addCallback(lambda r: pull('data'))
444 d.addCallback(lambda r: self.assertEqual(r,4*[data]))
443 d.addCallback(lambda r: self.assertEqual(r,4*[data]))
445 d.addCallback(lambda _: push({'data':data}, block=False))
444 d.addCallback(lambda _: push({'data':data}, block=False))
446 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
445 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
447 d.addCallback(lambda _: pull('data', block=False))
446 d.addCallback(lambda _: pull('data', block=False))
448 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
447 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
449 d.addCallback(lambda r: self.assertEqual(r,4*[data]))
448 d.addCallback(lambda r: self.assertEqual(r,4*[data]))
450 d.addCallback(lambda _: push(dict(a=10,b=20)))
449 d.addCallback(lambda _: push(dict(a=10,b=20)))
451 d.addCallback(lambda _: pull(('a','b')))
450 d.addCallback(lambda _: pull(('a','b')))
452 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,20]]))
451 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,20]]))
453 return d
452 return d
454
453
455 def testPushPullFunction(self):
454 def testPushPullFunction(self):
456 self.addEngine(4)
455 self.addEngine(4)
457 pushf = self.multiengine.push_function
456 pushf = self.multiengine.push_function
458 pullf = self.multiengine.pull_function
457 pullf = self.multiengine.pull_function
459 push = self.multiengine.push
458 push = self.multiengine.push
460 pull = self.multiengine.pull
459 pull = self.multiengine.pull
461 execute = self.multiengine.execute
460 execute = self.multiengine.execute
462 d= pushf({'testf':testf}, targets=0)
461 d= pushf({'testf':testf}, targets=0)
463 d.addCallback(lambda r: pullf('testf', targets=0))
462 d.addCallback(lambda r: pullf('testf', targets=0))
464 d.addCallback(lambda r: self.assertEqual(r[0](1.0), testf(1.0)))
463 d.addCallback(lambda r: self.assertEqual(r[0](1.0), testf(1.0)))
465 d.addCallback(lambda _: execute('r = testf(10)', targets=0))
464 d.addCallback(lambda _: execute('r = testf(10)', targets=0))
466 d.addCallback(lambda _: pull('r', targets=0))
465 d.addCallback(lambda _: pull('r', targets=0))
467 d.addCallback(lambda r: self.assertEquals(r[0], testf(10)))
466 d.addCallback(lambda r: self.assertEquals(r[0], testf(10)))
468 d.addCallback(lambda _: pushf({'testf':testf}, block=False))
467 d.addCallback(lambda _: pushf({'testf':testf}, block=False))
469 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
468 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
470 d.addCallback(lambda _: pullf('testf', block=False))
469 d.addCallback(lambda _: pullf('testf', block=False))
471 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
470 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
472 d.addCallback(lambda r: self.assertEqual(r[0](1.0), testf(1.0)))
471 d.addCallback(lambda r: self.assertEqual(r[0](1.0), testf(1.0)))
473 d.addCallback(lambda _: execute("def g(x): return x*x", targets=0))
472 d.addCallback(lambda _: execute("def g(x): return x*x", targets=0))
474 d.addCallback(lambda _: pullf(('testf','g'),targets=0))
473 d.addCallback(lambda _: pullf(('testf','g'),targets=0))
475 d.addCallback(lambda r: self.assertEquals((r[0][0](10),r[0][1](10)), (testf(10), 100)))
474 d.addCallback(lambda r: self.assertEquals((r[0][0](10),r[0][1](10)), (testf(10), 100)))
476 return d
475 return d
477
476
478 def testGetResult(self):
477 def testGetResult(self):
479 shell = Interpreter()
478 shell = Interpreter()
480 result1 = shell.execute('a=10')
479 result1 = shell.execute('a=10')
481 result1['id'] = 0
480 result1['id'] = 0
482 result2 = shell.execute('b=20')
481 result2 = shell.execute('b=20')
483 result2['id'] = 0
482 result2['id'] = 0
484 execute= self.multiengine.execute
483 execute= self.multiengine.execute
485 get_result = self.multiengine.get_result
484 get_result = self.multiengine.get_result
486 self.addEngine(1)
485 self.addEngine(1)
487 d= execute('a=10')
486 d= execute('a=10')
488 d.addCallback(lambda _: get_result())
487 d.addCallback(lambda _: get_result())
489 d.addCallback(lambda r: self.assertEquals(r[0], result1))
488 d.addCallback(lambda r: self.assertEquals(r[0], result1))
490 d.addCallback(lambda _: execute('b=20'))
489 d.addCallback(lambda _: execute('b=20'))
491 d.addCallback(lambda _: get_result(1))
490 d.addCallback(lambda _: get_result(1))
492 d.addCallback(lambda r: self.assertEquals(r[0], result1))
491 d.addCallback(lambda r: self.assertEquals(r[0], result1))
493 d.addCallback(lambda _: get_result(2, block=False))
492 d.addCallback(lambda _: get_result(2, block=False))
494 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
493 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
495 d.addCallback(lambda r: self.assertEquals(r[0], result2))
494 d.addCallback(lambda r: self.assertEquals(r[0], result2))
496 return d
495 return d
497
496
498 def testResetAndKeys(self):
497 def testResetAndKeys(self):
499 self.addEngine(1)
498 self.addEngine(1)
500
499
501 #Blocking mode
500 #Blocking mode
502 d= self.multiengine.push(dict(a=10, b=20, c=range(10)), targets=0)
501 d= self.multiengine.push(dict(a=10, b=20, c=range(10)), targets=0)
503 d.addCallback(lambda _: self.multiengine.keys(targets=0))
502 d.addCallback(lambda _: self.multiengine.keys(targets=0))
504 def keys_found(keys):
503 def keys_found(keys):
505 self.assert_('a' in keys[0])
504 self.assert_('a' in keys[0])
506 self.assert_('b' in keys[0])
505 self.assert_('b' in keys[0])
507 self.assert_('b' in keys[0])
506 self.assert_('b' in keys[0])
508 d.addCallback(keys_found)
507 d.addCallback(keys_found)
509 d.addCallback(lambda _: self.multiengine.reset(targets=0))
508 d.addCallback(lambda _: self.multiengine.reset(targets=0))
510 d.addCallback(lambda _: self.multiengine.keys(targets=0))
509 d.addCallback(lambda _: self.multiengine.keys(targets=0))
511 def keys_not_found(keys):
510 def keys_not_found(keys):
512 self.assert_('a' not in keys[0])
511 self.assert_('a' not in keys[0])
513 self.assert_('b' not in keys[0])
512 self.assert_('b' not in keys[0])
514 self.assert_('b' not in keys[0])
513 self.assert_('b' not in keys[0])
515 d.addCallback(keys_not_found)
514 d.addCallback(keys_not_found)
516
515
517 #Non-blocking mode
516 #Non-blocking mode
518 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20, c=range(10)), targets=0))
517 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20, c=range(10)), targets=0))
519 d.addCallback(lambda _: self.multiengine.keys(targets=0, block=False))
518 d.addCallback(lambda _: self.multiengine.keys(targets=0, block=False))
520 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
519 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
521 def keys_found(keys):
520 def keys_found(keys):
522 self.assert_('a' in keys[0])
521 self.assert_('a' in keys[0])
523 self.assert_('b' in keys[0])
522 self.assert_('b' in keys[0])
524 self.assert_('b' in keys[0])
523 self.assert_('b' in keys[0])
525 d.addCallback(keys_found)
524 d.addCallback(keys_found)
526 d.addCallback(lambda _: self.multiengine.reset(targets=0, block=False))
525 d.addCallback(lambda _: self.multiengine.reset(targets=0, block=False))
527 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
526 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
528 d.addCallback(lambda _: self.multiengine.keys(targets=0, block=False))
527 d.addCallback(lambda _: self.multiengine.keys(targets=0, block=False))
529 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
528 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
530 def keys_not_found(keys):
529 def keys_not_found(keys):
531 self.assert_('a' not in keys[0])
530 self.assert_('a' not in keys[0])
532 self.assert_('b' not in keys[0])
531 self.assert_('b' not in keys[0])
533 self.assert_('b' not in keys[0])
532 self.assert_('b' not in keys[0])
534 d.addCallback(keys_not_found)
533 d.addCallback(keys_not_found)
535
534
536 return d
535 return d
537
536
538 def testPushPullSerialized(self):
537 def testPushPullSerialized(self):
539 self.addEngine(1)
538 self.addEngine(1)
540 dikt = dict(a=10,b='hi there',c=1.2345,d={'p':(1,2)})
539 dikt = dict(a=10,b='hi there',c=1.2345,d={'p':(1,2)})
541 sdikt = {}
540 sdikt = {}
542 for k,v in dikt.iteritems():
541 for k,v in dikt.iteritems():
543 sdikt[k] = newserialized.serialize(v)
542 sdikt[k] = newserialized.serialize(v)
544 d= self.multiengine.push_serialized(dict(a=sdikt['a']), targets=0)
543 d= self.multiengine.push_serialized(dict(a=sdikt['a']), targets=0)
545 d.addCallback(lambda _: self.multiengine.pull('a',targets=0))
544 d.addCallback(lambda _: self.multiengine.pull('a',targets=0))
546 d.addCallback(lambda r: self.assertEquals(r[0], dikt['a']))
545 d.addCallback(lambda r: self.assertEquals(r[0], dikt['a']))
547 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
546 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
548 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
547 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
549 d.addCallback(lambda r: self.assertEquals(r, dikt['a']))
548 d.addCallback(lambda r: self.assertEquals(r, dikt['a']))
550 d.addCallback(lambda _: self.multiengine.push_serialized(sdikt, targets=0))
549 d.addCallback(lambda _: self.multiengine.push_serialized(sdikt, targets=0))
551 d.addCallback(lambda _: self.multiengine.pull_serialized(sdikt.keys(), targets=0))
550 d.addCallback(lambda _: self.multiengine.pull_serialized(sdikt.keys(), targets=0))
552 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
551 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
553 d.addCallback(lambda r: self.assertEquals(r, dikt.values()))
552 d.addCallback(lambda r: self.assertEquals(r, dikt.values()))
554 d.addCallback(lambda _: self.multiengine.reset(targets=0))
553 d.addCallback(lambda _: self.multiengine.reset(targets=0))
555 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
554 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
556 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
555 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
557
556
558 #Non-blocking mode
557 #Non-blocking mode
559 d.addCallback(lambda r: self.multiengine.push_serialized(dict(a=sdikt['a']), targets=0, block=False))
558 d.addCallback(lambda r: self.multiengine.push_serialized(dict(a=sdikt['a']), targets=0, block=False))
560 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
559 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
561 d.addCallback(lambda _: self.multiengine.pull('a',targets=0))
560 d.addCallback(lambda _: self.multiengine.pull('a',targets=0))
562 d.addCallback(lambda r: self.assertEquals(r[0], dikt['a']))
561 d.addCallback(lambda r: self.assertEquals(r[0], dikt['a']))
563 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0, block=False))
562 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0, block=False))
564 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
563 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
565 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
564 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
566 d.addCallback(lambda r: self.assertEquals(r, dikt['a']))
565 d.addCallback(lambda r: self.assertEquals(r, dikt['a']))
567 d.addCallback(lambda _: self.multiengine.push_serialized(sdikt, targets=0, block=False))
566 d.addCallback(lambda _: self.multiengine.push_serialized(sdikt, targets=0, block=False))
568 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
567 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
569 d.addCallback(lambda _: self.multiengine.pull_serialized(sdikt.keys(), targets=0, block=False))
568 d.addCallback(lambda _: self.multiengine.pull_serialized(sdikt.keys(), targets=0, block=False))
570 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
569 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
571 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
570 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
572 d.addCallback(lambda r: self.assertEquals(r, dikt.values()))
571 d.addCallback(lambda r: self.assertEquals(r, dikt.values()))
573 d.addCallback(lambda _: self.multiengine.reset(targets=0))
572 d.addCallback(lambda _: self.multiengine.reset(targets=0))
574 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0, block=False))
573 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0, block=False))
575 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
574 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
576 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
575 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
577 return d
576 return d
578
577
579 def testClearQueue(self):
578 def testClearQueue(self):
580 self.addEngine(4)
579 self.addEngine(4)
581 d= self.multiengine.clear_queue()
580 d= self.multiengine.clear_queue()
582 d.addCallback(lambda r: self.multiengine.clear_queue(block=False))
581 d.addCallback(lambda r: self.multiengine.clear_queue(block=False))
583 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
582 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
584 d.addCallback(lambda r: self.assertEquals(r,4*[None]))
583 d.addCallback(lambda r: self.assertEquals(r,4*[None]))
585 return d
584 return d
586
585
587 def testQueueStatus(self):
586 def testQueueStatus(self):
588 self.addEngine(4)
587 self.addEngine(4)
589 d= self.multiengine.queue_status(targets=0)
588 d= self.multiengine.queue_status(targets=0)
590 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
589 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
591 d.addCallback(lambda r: self.multiengine.queue_status(targets=0, block=False))
590 d.addCallback(lambda r: self.multiengine.queue_status(targets=0, block=False))
592 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
591 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
593 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
592 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
594 return d
593 return d
595
594
596 def testGetIDs(self):
595 def testGetIDs(self):
597 self.addEngine(1)
596 self.addEngine(1)
598 d= self.multiengine.get_ids()
597 d= self.multiengine.get_ids()
599 d.addCallback(lambda r: self.assertEquals(r, [0]))
598 d.addCallback(lambda r: self.assertEquals(r, [0]))
600 d.addCallback(lambda _: self.addEngine(3))
599 d.addCallback(lambda _: self.addEngine(3))
601 d.addCallback(lambda _: self.multiengine.get_ids())
600 d.addCallback(lambda _: self.multiengine.get_ids())
602 d.addCallback(lambda r: self.assertEquals(r, [0,1,2,3]))
601 d.addCallback(lambda r: self.assertEquals(r, [0,1,2,3]))
603 return d
602 return d
604
603
605 def testGetSetProperties(self):
604 def testGetSetProperties(self):
606 self.addEngine(4)
605 self.addEngine(4)
607 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
606 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
608 d= self.multiengine.set_properties(dikt)
607 d= self.multiengine.set_properties(dikt)
609 d.addCallback(lambda r: self.multiengine.get_properties())
608 d.addCallback(lambda r: self.multiengine.get_properties())
610 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
609 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
611 d.addCallback(lambda r: self.multiengine.get_properties(('c',)))
610 d.addCallback(lambda r: self.multiengine.get_properties(('c',)))
612 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
611 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
613 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False)))
612 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False)))
614 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd')))
613 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd')))
615 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
614 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
616
615
617 #Non-blocking
616 #Non-blocking
618 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
617 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
619 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
618 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
620 d.addCallback(lambda r: self.multiengine.get_properties(block=False))
619 d.addCallback(lambda r: self.multiengine.get_properties(block=False))
621 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
620 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
622 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
621 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
623 d.addCallback(lambda r: self.multiengine.get_properties(('c',), block=False))
622 d.addCallback(lambda r: self.multiengine.get_properties(('c',), block=False))
624 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
623 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
625 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
624 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
626 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False), block=False))
625 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False), block=False))
627 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
626 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
628 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd'), block=False))
627 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd'), block=False))
629 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
628 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
630 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
629 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
631 return d
630 return d
632
631
633 def testClearProperties(self):
632 def testClearProperties(self):
634 self.addEngine(4)
633 self.addEngine(4)
635 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
634 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
636 d= self.multiengine.set_properties(dikt)
635 d= self.multiengine.set_properties(dikt)
637 d.addCallback(lambda r: self.multiengine.clear_properties())
636 d.addCallback(lambda r: self.multiengine.clear_properties())
638 d.addCallback(lambda r: self.multiengine.get_properties())
637 d.addCallback(lambda r: self.multiengine.get_properties())
639 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
638 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
640
639
641 #Non-blocking
640 #Non-blocking
642 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
641 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
643 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
642 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
644 d.addCallback(lambda r: self.multiengine.clear_properties(block=False))
643 d.addCallback(lambda r: self.multiengine.clear_properties(block=False))
645 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
644 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
646 d.addCallback(lambda r: self.multiengine.get_properties(block=False))
645 d.addCallback(lambda r: self.multiengine.get_properties(block=False))
647 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
646 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
648 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
647 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
649 return d
648 return d
650
649
651 def testDelHasProperties(self):
650 def testDelHasProperties(self):
652 self.addEngine(4)
651 self.addEngine(4)
653 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
652 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
654 d= self.multiengine.set_properties(dikt)
653 d= self.multiengine.set_properties(dikt)
655 d.addCallback(lambda r: self.multiengine.del_properties(('b','e')))
654 d.addCallback(lambda r: self.multiengine.del_properties(('b','e')))
656 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e')))
655 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e')))
657 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
656 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
658
657
659 #Non-blocking
658 #Non-blocking
660 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
659 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
661 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
660 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
662 d.addCallback(lambda r: self.multiengine.del_properties(('b','e'), block=False))
661 d.addCallback(lambda r: self.multiengine.del_properties(('b','e'), block=False))
663 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
662 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
664 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e'), block=False))
663 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e'), block=False))
665 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
664 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
666 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
665 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
667 return d
666 return d
668
667
669 def test_clear_pending_deferreds(self):
668 def test_clear_pending_deferreds(self):
670 self.addEngine(4)
669 self.addEngine(4)
671 did_list = []
670 did_list = []
672 d= self.multiengine.execute('a=10',block=False)
671 d= self.multiengine.execute('a=10',block=False)
673 d.addCallback(lambda did: did_list.append(did))
672 d.addCallback(lambda did: did_list.append(did))
674 d.addCallback(lambda _: self.multiengine.push(dict(b=10),block=False))
673 d.addCallback(lambda _: self.multiengine.push(dict(b=10),block=False))
675 d.addCallback(lambda did: did_list.append(did))
674 d.addCallback(lambda did: did_list.append(did))
676 d.addCallback(lambda _: self.multiengine.pull(('a','b'),block=False))
675 d.addCallback(lambda _: self.multiengine.pull(('a','b'),block=False))
677 d.addCallback(lambda did: did_list.append(did))
676 d.addCallback(lambda did: did_list.append(did))
678 d.addCallback(lambda _: self.multiengine.clear_pending_deferreds())
677 d.addCallback(lambda _: self.multiengine.clear_pending_deferreds())
679 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[0],True))
678 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[0],True))
680 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
679 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
681 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[1],True))
680 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[1],True))
682 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
681 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
683 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[2],True))
682 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[2],True))
684 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
683 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
685 return d
684 return d
686
685
687 #-------------------------------------------------------------------------------
686 #-------------------------------------------------------------------------------
688 # Coordinator test cases
687 # Coordinator test cases
689 #-------------------------------------------------------------------------------
688 #-------------------------------------------------------------------------------
690
689
691 class IMultiEngineCoordinatorTestCase(object):
690 class IMultiEngineCoordinatorTestCase(object):
692
691
693 def testScatterGather(self):
692 def testScatterGather(self):
694 self.addEngine(4)
693 self.addEngine(4)
695 d= self.multiengine.scatter('a', range(16))
694 d= self.multiengine.scatter('a', range(16))
696 d.addCallback(lambda r: self.multiengine.gather('a'))
695 d.addCallback(lambda r: self.multiengine.gather('a'))
697 d.addCallback(lambda r: self.assertEquals(r, range(16)))
696 d.addCallback(lambda r: self.assertEquals(r, range(16)))
698 d.addCallback(lambda _: self.multiengine.gather('asdf'))
697 d.addCallback(lambda _: self.multiengine.gather('asdf'))
699 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
698 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
700 return d
699 return d
701
700
702 def testScatterGatherNumpy(self):
701 def testScatterGatherNumpy(self):
703 try:
702 try:
704 import numpy
703 import numpy
705 from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
704 from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
706 except:
705 except:
707 return
706 return
708 else:
707 else:
709 self.addEngine(4)
708 self.addEngine(4)
710 a = numpy.arange(16)
709 a = numpy.arange(16)
711 d = self.multiengine.scatter('a', a)
710 d = self.multiengine.scatter('a', a)
712 d.addCallback(lambda r: self.multiengine.gather('a'))
711 d.addCallback(lambda r: self.multiengine.gather('a'))
713 d.addCallback(lambda r: assert_array_equal(r, a))
712 d.addCallback(lambda r: assert_array_equal(r, a))
714 return d
713 return d
715
714
716 def testMap(self):
715 def testMap(self):
717 self.addEngine(4)
716 self.addEngine(4)
718 def f(x):
717 def f(x):
719 return x**2
718 return x**2
720 data = range(16)
719 data = range(16)
721 d= self.multiengine.map(f, data)
720 d= self.multiengine.map(f, data)
722 d.addCallback(lambda r: self.assertEquals(r,[f(x) for x in data]))
721 d.addCallback(lambda r: self.assertEquals(r,[f(x) for x in data]))
723 return d
722 return d
724
723
725
724
726 class ISynchronousMultiEngineCoordinatorTestCase(IMultiEngineCoordinatorTestCase):
725 class ISynchronousMultiEngineCoordinatorTestCase(IMultiEngineCoordinatorTestCase):
727
726
728 def testScatterGatherNonblocking(self):
727 def testScatterGatherNonblocking(self):
729 self.addEngine(4)
728 self.addEngine(4)
730 d= self.multiengine.scatter('a', range(16), block=False)
729 d= self.multiengine.scatter('a', range(16), block=False)
731 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
730 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
732 d.addCallback(lambda r: self.multiengine.gather('a', block=False))
731 d.addCallback(lambda r: self.multiengine.gather('a', block=False))
733 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
732 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
734 d.addCallback(lambda r: self.assertEquals(r, range(16)))
733 d.addCallback(lambda r: self.assertEquals(r, range(16)))
735 return d
734 return d
736
735
737 def testScatterGatherNumpyNonblocking(self):
736 def testScatterGatherNumpyNonblocking(self):
738 try:
737 try:
739 import numpy
738 import numpy
740 from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
739 from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
741 except:
740 except:
742 return
741 return
743 else:
742 else:
744 self.addEngine(4)
743 self.addEngine(4)
745 a = numpy.arange(16)
744 a = numpy.arange(16)
746 d = self.multiengine.scatter('a', a, block=False)
745 d = self.multiengine.scatter('a', a, block=False)
747 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
746 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
748 d.addCallback(lambda r: self.multiengine.gather('a', block=False))
747 d.addCallback(lambda r: self.multiengine.gather('a', block=False))
749 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
748 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
750 d.addCallback(lambda r: assert_array_equal(r, a))
749 d.addCallback(lambda r: assert_array_equal(r, a))
751 return d
750 return d
752
751
753 def test_clear_pending_deferreds(self):
752 def test_clear_pending_deferreds(self):
754 self.addEngine(4)
753 self.addEngine(4)
755 did_list = []
754 did_list = []
756 d= self.multiengine.scatter('a',range(16),block=False)
755 d= self.multiengine.scatter('a',range(16),block=False)
757 d.addCallback(lambda did: did_list.append(did))
756 d.addCallback(lambda did: did_list.append(did))
758 d.addCallback(lambda _: self.multiengine.gather('a',block=False))
757 d.addCallback(lambda _: self.multiengine.gather('a',block=False))
759 d.addCallback(lambda did: did_list.append(did))
758 d.addCallback(lambda did: did_list.append(did))
760 d.addCallback(lambda _: self.multiengine.map(lambda x: x, range(16),block=False))
759 d.addCallback(lambda _: self.multiengine.map(lambda x: x, range(16),block=False))
761 d.addCallback(lambda did: did_list.append(did))
760 d.addCallback(lambda did: did_list.append(did))
762 d.addCallback(lambda _: self.multiengine.clear_pending_deferreds())
761 d.addCallback(lambda _: self.multiengine.clear_pending_deferreds())
763 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[0],True))
762 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[0],True))
764 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
763 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
765 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[1],True))
764 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[1],True))
766 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
765 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
767 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[2],True))
766 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[2],True))
768 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
767 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
769 return d
768 return d
770
769
771 #-------------------------------------------------------------------------------
770 #-------------------------------------------------------------------------------
772 # Extras test cases
771 # Extras test cases
773 #-------------------------------------------------------------------------------
772 #-------------------------------------------------------------------------------
774
773
775 class IMultiEngineExtrasTestCase(object):
774 class IMultiEngineExtrasTestCase(object):
776
775
777 def testZipPull(self):
776 def testZipPull(self):
778 self.addEngine(4)
777 self.addEngine(4)
779 d= self.multiengine.push(dict(a=10,b=20))
778 d= self.multiengine.push(dict(a=10,b=20))
780 d.addCallback(lambda r: self.multiengine.zip_pull(('a','b')))
779 d.addCallback(lambda r: self.multiengine.zip_pull(('a','b')))
781 d.addCallback(lambda r: self.assert_(r, [4*[10],4*[20]]))
780 d.addCallback(lambda r: self.assert_(r, [4*[10],4*[20]]))
782 return d
781 return d
783
782
784 def testRun(self):
783 def testRun(self):
785 self.addEngine(4)
784 self.addEngine(4)
786 import tempfile
785 import tempfile
787 fname = tempfile.mktemp('foo.py')
786 fname = tempfile.mktemp('foo.py')
788 f= open(fname, 'w')
787 f= open(fname, 'w')
789 f.write('a = 10\nb=30')
788 f.write('a = 10\nb=30')
790 f.close()
789 f.close()
791 d= self.multiengine.run(fname)
790 d= self.multiengine.run(fname)
792 d.addCallback(lambda r: self.multiengine.pull(('a','b')))
791 d.addCallback(lambda r: self.multiengine.pull(('a','b')))
793 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,30]]))
792 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,30]]))
794 return d
793 return d
795
794
796
795
797 class ISynchronousMultiEngineExtrasTestCase(IMultiEngineExtrasTestCase):
796 class ISynchronousMultiEngineExtrasTestCase(IMultiEngineExtrasTestCase):
798
797
799 def testZipPullNonblocking(self):
798 def testZipPullNonblocking(self):
800 self.addEngine(4)
799 self.addEngine(4)
801 d= self.multiengine.push(dict(a=10,b=20))
800 d= self.multiengine.push(dict(a=10,b=20))
802 d.addCallback(lambda r: self.multiengine.zip_pull(('a','b'), block=False))
801 d.addCallback(lambda r: self.multiengine.zip_pull(('a','b'), block=False))
803 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
802 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
804 d.addCallback(lambda r: self.assert_(r, [4*[10],4*[20]]))
803 d.addCallback(lambda r: self.assert_(r, [4*[10],4*[20]]))
805 return d
804 return d
806
805
807 def testRunNonblocking(self):
806 def testRunNonblocking(self):
808 self.addEngine(4)
807 self.addEngine(4)
809 import tempfile
808 import tempfile
810 fname = tempfile.mktemp('foo.py')
809 fname = tempfile.mktemp('foo.py')
811 f= open(fname, 'w')
810 f= open(fname, 'w')
812 f.write('a = 10\nb=30')
811 f.write('a = 10\nb=30')
813 f.close()
812 f.close()
814 d= self.multiengine.run(fname, block=False)
813 d= self.multiengine.run(fname, block=False)
815 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
814 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
816 d.addCallback(lambda r: self.multiengine.pull(('a','b')))
815 d.addCallback(lambda r: self.multiengine.pull(('a','b')))
817 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,30]]))
816 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,30]]))
818 return d
817 return d
819
818
820
819
821 #-------------------------------------------------------------------------------
820 #-------------------------------------------------------------------------------
822 # IFullSynchronousMultiEngineTestCase
821 # IFullSynchronousMultiEngineTestCase
823 #-------------------------------------------------------------------------------
822 #-------------------------------------------------------------------------------
824
823
825 class IFullSynchronousMultiEngineTestCase(ISynchronousMultiEngineTestCase,
824 class IFullSynchronousMultiEngineTestCase(ISynchronousMultiEngineTestCase,
826 ISynchronousMultiEngineCoordinatorTestCase,
825 ISynchronousMultiEngineCoordinatorTestCase,
827 ISynchronousMultiEngineExtrasTestCase):
826 ISynchronousMultiEngineExtrasTestCase):
828 pass
827 pass
@@ -1,43 +1,44 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the kernel.engineservice.py module.
3 """This file contains unittests for the kernel.engineservice.py module.
4
4
5 Things that should be tested:
5 Things that should be tested:
6
6
7 - Should the EngineService return Deferred objects?
7 - Should the EngineService return Deferred objects?
8 - Run the same tests that are run in shell.py.
8 - Run the same tests that are run in shell.py.
9 - Make sure that the Interface is really implemented.
9 - Make sure that the Interface is really implemented.
10 - The startService and stopService methods.
10 - The startService and stopService methods.
11 """
11 """
12
12
13 __docformat__ = "restructuredtext en"
13 __docformat__ = "restructuredtext en"
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 The IPython Development Team
16 # Copyright (C) 2008 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25
25
26 try:
26 try:
27 from twisted.application.service import IService
27 from twisted.application.service import IService
28 from IPython.kernel.controllerservice import ControllerService
28 from IPython.kernel.controllerservice import ControllerService
29 from IPython.kernel.tests import multienginetest as met
29 from IPython.kernel.tests import multienginetest as met
30 from controllertest import IControllerCoreTestCase
30 from controllertest import IControllerCoreTestCase
31 from IPython.testing.util import DeferredTestCase
31 from IPython.testing.util import DeferredTestCase
32 except ImportError:
32 except ImportError:
33 pass
33 import nose
34 else:
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
35 class BasicControllerServiceTest(DeferredTestCase,
35
36 IControllerCoreTestCase):
36 class BasicControllerServiceTest(DeferredTestCase,
37
37 IControllerCoreTestCase):
38 def setUp(self):
38
39 self.controller = ControllerService()
39 def setUp(self):
40 self.controller.startService()
40 self.controller = ControllerService()
41
41 self.controller.startService()
42 def tearDown(self):
42
43 self.controller.stopService()
43 def tearDown(self):
44 self.controller.stopService()
@@ -1,92 +1,93 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the enginepb.py module."""
3 """This file contains unittests for the enginepb.py module."""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 try:
18 try:
19 from twisted.python import components
19 from twisted.python import components
20 from twisted.internet import reactor, defer
20 from twisted.internet import reactor, defer
21 from twisted.spread import pb
21 from twisted.spread import pb
22 from twisted.internet.base import DelayedCall
22 from twisted.internet.base import DelayedCall
23 DelayedCall.debug = True
23 DelayedCall.debug = True
24
24
25 import zope.interface as zi
25 import zope.interface as zi
26
26
27 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
27 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
28 from IPython.kernel import engineservice as es
28 from IPython.kernel import engineservice as es
29 from IPython.testing.util import DeferredTestCase
29 from IPython.testing.util import DeferredTestCase
30 from IPython.kernel.controllerservice import IControllerBase
30 from IPython.kernel.controllerservice import IControllerBase
31 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
31 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
32 from IPython.kernel.engineservice import IEngineQueued
32 from IPython.kernel.engineservice import IEngineQueued
33 from IPython.kernel.engineconnector import EngineConnector
33 from IPython.kernel.engineconnector import EngineConnector
34
34
35 from IPython.kernel.tests.engineservicetest import \
35 from IPython.kernel.tests.engineservicetest import \
36 IEngineCoreTestCase, \
36 IEngineCoreTestCase, \
37 IEngineSerializedTestCase, \
37 IEngineSerializedTestCase, \
38 IEngineQueuedTestCase
38 IEngineQueuedTestCase
39 except ImportError:
39 except ImportError:
40 print "we got an error!!!"
40 import nose
41 raise
41 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
42 else:
42
43 class EngineFCTest(DeferredTestCase,
43
44 IEngineCoreTestCase,
44 class EngineFCTest(DeferredTestCase,
45 IEngineSerializedTestCase,
45 IEngineCoreTestCase,
46 IEngineQueuedTestCase
46 IEngineSerializedTestCase,
47 ):
47 IEngineQueuedTestCase
48
48 ):
49 zi.implements(IControllerBase)
49
50
50 zi.implements(IControllerBase)
51 def setUp(self):
51
52
52 def setUp(self):
53 # Start a server and append to self.servers
53
54 self.controller_reference = FCRemoteEngineRefFromService(self)
54 # Start a server and append to self.servers
55 self.controller_tub = Tub()
55 self.controller_reference = FCRemoteEngineRefFromService(self)
56 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub = Tub()
57 self.controller_tub.setLocation('127.0.0.1:10105')
57 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
58
58 self.controller_tub.setLocation('127.0.0.1:10105')
59 furl = self.controller_tub.registerReference(self.controller_reference)
59
60 self.controller_tub.startService()
60 furl = self.controller_tub.registerReference(self.controller_reference)
61
61 self.controller_tub.startService()
62 # Start an EngineService and append to services/client
62
63 self.engine_service = es.EngineService()
63 # Start an EngineService and append to services/client
64 self.engine_service.startService()
64 self.engine_service = es.EngineService()
65 self.engine_tub = Tub()
65 self.engine_service.startService()
66 self.engine_tub.startService()
66 self.engine_tub = Tub()
67 engine_connector = EngineConnector(self.engine_tub)
67 self.engine_tub.startService()
68 d = engine_connector.connect_to_controller(self.engine_service, furl)
68 engine_connector = EngineConnector(self.engine_tub)
69 # This deferred doesn't fire until after register_engine has returned and
69 d = engine_connector.connect_to_controller(self.engine_service, furl)
70 # thus, self.engine has been defined and the tets can proceed.
70 # This deferred doesn't fire until after register_engine has returned and
71 return d
71 # thus, self.engine has been defined and the tets can proceed.
72
72 return d
73 def tearDown(self):
73
74 dlist = []
74 def tearDown(self):
75 # Shut down the engine
75 dlist = []
76 d = self.engine_tub.stopService()
76 # Shut down the engine
77 dlist.append(d)
77 d = self.engine_tub.stopService()
78 # Shut down the controller
78 dlist.append(d)
79 d = self.controller_tub.stopService()
79 # Shut down the controller
80 dlist.append(d)
80 d = self.controller_tub.stopService()
81 return defer.DeferredList(dlist)
81 dlist.append(d)
82
82 return defer.DeferredList(dlist)
83 #---------------------------------------------------------------------------
83
84 # Make me look like a basic controller
84 #---------------------------------------------------------------------------
85 #---------------------------------------------------------------------------
85 # Make me look like a basic controller
86
86 #---------------------------------------------------------------------------
87 def register_engine(self, engine_ref, id=None, ip=None, port=None, pid=None):
87
88 self.engine = IEngineQueued(IEngineBase(engine_ref))
88 def register_engine(self, engine_ref, id=None, ip=None, port=None, pid=None):
89 return {'id':id}
89 self.engine = IEngineQueued(IEngineBase(engine_ref))
90
90 return {'id':id}
91 def unregister_engine(self, id):
91
92 pass No newline at end of file
92 def unregister_engine(self, id):
93 pass No newline at end of file
@@ -1,78 +1,80 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the kernel.engineservice.py module.
3 """This file contains unittests for the kernel.engineservice.py module.
4
4
5 Things that should be tested:
5 Things that should be tested:
6
6
7 - Should the EngineService return Deferred objects?
7 - Should the EngineService return Deferred objects?
8 - Run the same tests that are run in shell.py.
8 - Run the same tests that are run in shell.py.
9 - Make sure that the Interface is really implemented.
9 - Make sure that the Interface is really implemented.
10 - The startService and stopService methods.
10 - The startService and stopService methods.
11 """
11 """
12
12
13 __docformat__ = "restructuredtext en"
13 __docformat__ = "restructuredtext en"
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 The IPython Development Team
16 # Copyright (C) 2008 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25
25
26 try:
26 try:
27 from twisted.internet import defer
27 from twisted.internet import defer
28 from twisted.application.service import IService
28 from twisted.application.service import IService
29
29
30 from IPython.kernel import engineservice as es
30 from IPython.kernel import engineservice as es
31 from IPython.testing.util import DeferredTestCase
31 from IPython.testing.util import DeferredTestCase
32 from IPython.kernel.tests.engineservicetest import \
32 from IPython.kernel.tests.engineservicetest import \
33 IEngineCoreTestCase, \
33 IEngineCoreTestCase, \
34 IEngineSerializedTestCase, \
34 IEngineSerializedTestCase, \
35 IEngineQueuedTestCase, \
35 IEngineQueuedTestCase, \
36 IEnginePropertiesTestCase
36 IEnginePropertiesTestCase
37 except ImportError:
37 except ImportError:
38 pass
38 import nose
39 else:
39 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
40 class BasicEngineServiceTest(DeferredTestCase,
40
41 IEngineCoreTestCase,
41
42 IEngineSerializedTestCase,
42 class BasicEngineServiceTest(DeferredTestCase,
43 IEnginePropertiesTestCase):
43 IEngineCoreTestCase,
44
44 IEngineSerializedTestCase,
45 def setUp(self):
45 IEnginePropertiesTestCase):
46 self.engine = es.EngineService()
46
47 self.engine.startService()
47 def setUp(self):
48
48 self.engine = es.EngineService()
49 def tearDown(self):
49 self.engine.startService()
50 return self.engine.stopService()
50
51
51 def tearDown(self):
52 class ThreadedEngineServiceTest(DeferredTestCase,
52 return self.engine.stopService()
53 IEngineCoreTestCase,
53
54 IEngineSerializedTestCase,
54 class ThreadedEngineServiceTest(DeferredTestCase,
55 IEnginePropertiesTestCase):
55 IEngineCoreTestCase,
56
56 IEngineSerializedTestCase,
57 def setUp(self):
57 IEnginePropertiesTestCase):
58 self.engine = es.ThreadedEngineService()
58
59 self.engine.startService()
59 def setUp(self):
60 self.engine = es.ThreadedEngineService()
61 self.engine.startService()
62
63 def tearDown(self):
64 return self.engine.stopService()
65
66 class QueuedEngineServiceTest(DeferredTestCase,
67 IEngineCoreTestCase,
68 IEngineSerializedTestCase,
69 IEnginePropertiesTestCase,
70 IEngineQueuedTestCase):
71
72 def setUp(self):
73 self.rawEngine = es.EngineService()
74 self.rawEngine.startService()
75 self.engine = es.IEngineQueued(self.rawEngine)
60
76
61 def tearDown(self):
77 def tearDown(self):
62 return self.engine.stopService()
78 return self.rawEngine.stopService()
63
79
64 class QueuedEngineServiceTest(DeferredTestCase,
80
65 IEngineCoreTestCase,
66 IEngineSerializedTestCase,
67 IEnginePropertiesTestCase,
68 IEngineQueuedTestCase):
69
70 def setUp(self):
71 self.rawEngine = es.EngineService()
72 self.rawEngine.startService()
73 self.engine = es.IEngineQueued(self.rawEngine)
74
75 def tearDown(self):
76 return self.rawEngine.stopService()
77
78
@@ -1,54 +1,56 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """"""
3 """"""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 try:
18 try:
19 from twisted.internet import defer
19 from twisted.internet import defer
20 from IPython.testing.util import DeferredTestCase
20 from IPython.testing.util import DeferredTestCase
21 from IPython.kernel.controllerservice import ControllerService
21 from IPython.kernel.controllerservice import ControllerService
22 from IPython.kernel import multiengine as me
22 from IPython.kernel import multiengine as me
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
24 ISynchronousMultiEngineTestCase)
24 ISynchronousMultiEngineTestCase)
25 except ImportError:
25 except ImportError:
26 pass
26 import nose
27 else:
27 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
28 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
28
29
30 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
31
32 def setUp(self):
33 self.controller = ControllerService()
34 self.controller.startService()
35 self.multiengine = me.IMultiEngine(self.controller)
36 self.engines = []
29
37
30 def setUp(self):
38 def tearDown(self):
31 self.controller = ControllerService()
39 self.controller.stopService()
32 self.controller.startService()
40 for e in self.engines:
33 self.multiengine = me.IMultiEngine(self.controller)
41 e.stopService()
34 self.engines = []
42
35
43
36 def tearDown(self):
44 class SynchronousMultiEngineTestCase(DeferredTestCase, ISynchronousMultiEngineTestCase):
37 self.controller.stopService()
45
38 for e in self.engines:
46 def setUp(self):
39 e.stopService()
47 self.controller = ControllerService()
40
48 self.controller.startService()
41
49 self.multiengine = me.ISynchronousMultiEngine(me.IMultiEngine(self.controller))
42 class SynchronousMultiEngineTestCase(DeferredTestCase, ISynchronousMultiEngineTestCase):
50 self.engines = []
43
51
44 def setUp(self):
52 def tearDown(self):
45 self.controller = ControllerService()
53 self.controller.stopService()
46 self.controller.startService()
54 for e in self.engines:
47 self.multiengine = me.ISynchronousMultiEngine(me.IMultiEngine(self.controller))
55 e.stopService()
48 self.engines = []
49
50 def tearDown(self):
51 self.controller.stopService()
52 for e in self.engines:
53 e.stopService()
54
56
@@ -1,144 +1,144 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 __docformat__ = "restructuredtext en"
4 __docformat__ = "restructuredtext en"
5
5
6 #-------------------------------------------------------------------------------
6 #-------------------------------------------------------------------------------
7 # Copyright (C) 2008 The IPython Development Team
7 # Copyright (C) 2008 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16
16
17 try:
17 try:
18 from twisted.internet import defer, reactor
18 from twisted.internet import defer, reactor
19
19
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
21
21
22 from IPython.testing.util import DeferredTestCase
22 from IPython.testing.util import DeferredTestCase
23 from IPython.kernel.controllerservice import ControllerService
23 from IPython.kernel.controllerservice import ControllerService
24 from IPython.kernel.multiengine import IMultiEngine
24 from IPython.kernel.multiengine import IMultiEngine
25 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
25 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
26 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
26 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
27 from IPython.kernel import multiengine as me
27 from IPython.kernel import multiengine as me
28 from IPython.kernel.clientconnector import ClientConnector
28 from IPython.kernel.clientconnector import ClientConnector
29 from IPython.kernel.parallelfunction import ParallelFunction
29 from IPython.kernel.parallelfunction import ParallelFunction
30 from IPython.kernel.error import CompositeError
30 from IPython.kernel.error import CompositeError
31 from IPython.kernel.util import printer
31 from IPython.kernel.util import printer
32 except ImportError:
32 except ImportError:
33 pass
33 import nose
34 else:
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
35
35
36 def _raise_it(f):
36 def _raise_it(f):
37 try:
37 try:
38 f.raiseException()
38 f.raiseException()
39 except CompositeError, e:
39 except CompositeError, e:
40 e.raise_exception()
40 e.raise_exception()
41
42
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
44
45 def setUp(self):
41
46
47 self.engines = []
48
49 self.controller = ControllerService()
50 self.controller.startService()
51 self.imultiengine = IMultiEngine(self.controller)
52 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
53
54 self.controller_tub = Tub()
55 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub.setLocation('127.0.0.1:10105')
42
57
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
58 furl = self.controller_tub.registerReference(self.mec_referenceable)
59 self.controller_tub.startService()
44
60
45 def setUp(self):
61 self.client_tub = ClientConnector()
46
62 d = self.client_tub.get_multiengine_client(furl)
47 self.engines = []
63 d.addCallback(self.handle_got_client)
48
64 return d
49 self.controller = ControllerService()
50 self.controller.startService()
51 self.imultiengine = IMultiEngine(self.controller)
52 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
53
54 self.controller_tub = Tub()
55 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub.setLocation('127.0.0.1:10105')
57
58 furl = self.controller_tub.registerReference(self.mec_referenceable)
59 self.controller_tub.startService()
60
61 self.client_tub = ClientConnector()
62 d = self.client_tub.get_multiengine_client(furl)
63 d.addCallback(self.handle_got_client)
64 return d
65
66 def handle_got_client(self, client):
67 self.multiengine = client
68
65
69 def tearDown(self):
66 def handle_got_client(self, client):
70 dlist = []
67 self.multiengine = client
71 # Shut down the multiengine client
68
72 d = self.client_tub.tub.stopService()
69 def tearDown(self):
73 dlist.append(d)
70 dlist = []
74 # Shut down the engines
71 # Shut down the multiengine client
75 for e in self.engines:
72 d = self.client_tub.tub.stopService()
76 e.stopService()
73 dlist.append(d)
77 # Shut down the controller
74 # Shut down the engines
78 d = self.controller_tub.stopService()
75 for e in self.engines:
79 d.addBoth(lambda _: self.controller.stopService())
76 e.stopService()
80 dlist.append(d)
77 # Shut down the controller
81 return defer.DeferredList(dlist)
78 d = self.controller_tub.stopService()
79 d.addBoth(lambda _: self.controller.stopService())
80 dlist.append(d)
81 return defer.DeferredList(dlist)
82
82
83 def test_mapper(self):
83 def test_mapper(self):
84 self.addEngine(4)
84 self.addEngine(4)
85 m = self.multiengine.mapper()
85 m = self.multiengine.mapper()
86 self.assertEquals(m.multiengine,self.multiengine)
86 self.assertEquals(m.multiengine,self.multiengine)
87 self.assertEquals(m.dist,'b')
87 self.assertEquals(m.dist,'b')
88 self.assertEquals(m.targets,'all')
88 self.assertEquals(m.targets,'all')
89 self.assertEquals(m.block,True)
89 self.assertEquals(m.block,True)
90
90
91 def test_map_default(self):
91 def test_map_default(self):
92 self.addEngine(4)
92 self.addEngine(4)
93 m = self.multiengine.mapper()
93 m = self.multiengine.mapper()
94 d = m.map(lambda x: 2*x, range(10))
94 d = m.map(lambda x: 2*x, range(10))
95 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
95 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
96 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
96 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
97 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
97 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
98 return d
98 return d
99
99
100 def test_map_noblock(self):
100 def test_map_noblock(self):
101 self.addEngine(4)
101 self.addEngine(4)
102 m = self.multiengine.mapper(block=False)
102 m = self.multiengine.mapper(block=False)
103 d = m.map(lambda x: 2*x, range(10))
103 d = m.map(lambda x: 2*x, range(10))
104 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
104 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
105 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
105 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
106 return d
106 return d
107
107
108 def test_mapper_fail(self):
108 def test_mapper_fail(self):
109 self.addEngine(4)
109 self.addEngine(4)
110 m = self.multiengine.mapper()
110 m = self.multiengine.mapper()
111 d = m.map(lambda x: 1/0, range(10))
111 d = m.map(lambda x: 1/0, range(10))
112 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
112 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
113 return d
113 return d
114
114
115 def test_parallel(self):
115 def test_parallel(self):
116 self.addEngine(4)
116 self.addEngine(4)
117 p = self.multiengine.parallel()
117 p = self.multiengine.parallel()
118 self.assert_(isinstance(p, ParallelFunction))
118 self.assert_(isinstance(p, ParallelFunction))
119 @p
119 @p
120 def f(x): return 2*x
120 def f(x): return 2*x
121 d = f(range(10))
121 d = f(range(10))
122 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
122 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
123 return d
123 return d
124
124
125 def test_parallel_noblock(self):
125 def test_parallel_noblock(self):
126 self.addEngine(1)
126 self.addEngine(1)
127 p = self.multiengine.parallel(block=False)
127 p = self.multiengine.parallel(block=False)
128 self.assert_(isinstance(p, ParallelFunction))
128 self.assert_(isinstance(p, ParallelFunction))
129 @p
129 @p
130 def f(x): return 2*x
130 def f(x): return 2*x
131 d = f(range(10))
131 d = f(range(10))
132 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
132 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
133 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
133 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
134 return d
134 return d
135
135
136 def test_parallel_fail(self):
136 def test_parallel_fail(self):
137 self.addEngine(4)
137 self.addEngine(4)
138 p = self.multiengine.parallel()
138 p = self.multiengine.parallel()
139 self.assert_(isinstance(p, ParallelFunction))
139 self.assert_(isinstance(p, ParallelFunction))
140 @p
140 @p
141 def f(x): return 1/0
141 def f(x): return 1/0
142 d = f(range(10))
142 d = f(range(10))
143 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
143 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
144 return d No newline at end of file
144 return d No newline at end of file
@@ -1,102 +1,102 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the shell.py module."""
3 """This file contains unittests for the shell.py module."""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 try:
18 try:
19 import zope.interface as zi
19 import zope.interface as zi
20 from twisted.trial import unittest
20 from twisted.trial import unittest
21 from IPython.testing.util import DeferredTestCase
21 from IPython.testing.util import DeferredTestCase
22
22
23 from IPython.kernel.newserialized import \
23 from IPython.kernel.newserialized import \
24 ISerialized, \
24 ISerialized, \
25 IUnSerialized, \
25 IUnSerialized, \
26 Serialized, \
26 Serialized, \
27 UnSerialized, \
27 UnSerialized, \
28 SerializeIt, \
28 SerializeIt, \
29 UnSerializeIt
29 UnSerializeIt
30 except ImportError:
30 except ImportError:
31 pass
31 import nose
32 else:
32 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
33 #-------------------------------------------------------------------------------
33
34 # Tests
34 #-------------------------------------------------------------------------------
35 #-------------------------------------------------------------------------------
35 # Tests
36 #-------------------------------------------------------------------------------
37
38 class SerializedTestCase(unittest.TestCase):
39
40 def setUp(self):
41 pass
36
42
37 class SerializedTestCase(unittest.TestCase):
43 def tearDown(self):
44 pass
38
45
39 def setUp(self):
46 def testSerializedInterfaces(self):
40 pass
41
42 def tearDown(self):
43 pass
44
45 def testSerializedInterfaces(self):
46
47
47 us = UnSerialized({'a':10, 'b':range(10)})
48 us = UnSerialized({'a':10, 'b':range(10)})
48 s = ISerialized(us)
49 s = ISerialized(us)
49 uss = IUnSerialized(s)
50 uss = IUnSerialized(s)
50 self.assert_(ISerialized.providedBy(s))
51 self.assert_(ISerialized.providedBy(s))
51 self.assert_(IUnSerialized.providedBy(us))
52 self.assert_(IUnSerialized.providedBy(us))
52 self.assert_(IUnSerialized.providedBy(uss))
53 self.assert_(IUnSerialized.providedBy(uss))
53 for m in list(ISerialized):
54 for m in list(ISerialized):
54 self.assert_(hasattr(s, m))
55 self.assert_(hasattr(s, m))
55 for m in list(IUnSerialized):
56 for m in list(IUnSerialized):
56 self.assert_(hasattr(us, m))
57 self.assert_(hasattr(us, m))
57 for m in list(IUnSerialized):
58 for m in list(IUnSerialized):
58 self.assert_(hasattr(uss, m))
59 self.assert_(hasattr(uss, m))
59
60
60 def testPickleSerialized(self):
61 def testPickleSerialized(self):
61 obj = {'a':1.45345, 'b':'asdfsdf', 'c':10000L}
62 obj = {'a':1.45345, 'b':'asdfsdf', 'c':10000L}
62 original = UnSerialized(obj)
63 original = UnSerialized(obj)
63 originalSer = ISerialized(original)
64 originalSer = ISerialized(original)
64 firstData = originalSer.getData()
65 firstData = originalSer.getData()
65 firstTD = originalSer.getTypeDescriptor()
66 firstTD = originalSer.getTypeDescriptor()
66 firstMD = originalSer.getMetadata()
67 firstMD = originalSer.getMetadata()
67 self.assert_(firstTD == 'pickle')
68 self.assert_(firstTD == 'pickle')
68 self.assert_(firstMD == {})
69 self.assert_(firstMD == {})
69 unSerialized = IUnSerialized(originalSer)
70 unSerialized = IUnSerialized(originalSer)
70 secondObj = unSerialized.getObject()
71 secondObj = unSerialized.getObject()
71 for k, v in secondObj.iteritems():
72 for k, v in secondObj.iteritems():
72 self.assert_(obj[k] == v)
73 self.assert_(obj[k] == v)
73 secondSer = ISerialized(UnSerialized(secondObj))
74 secondSer = ISerialized(UnSerialized(secondObj))
74 self.assert_(firstData == secondSer.getData())
75 self.assert_(firstData == secondSer.getData())
75 self.assert_(firstTD == secondSer.getTypeDescriptor() )
76 self.assert_(firstTD == secondSer.getTypeDescriptor() )
76 self.assert_(firstMD == secondSer.getMetadata())
77 self.assert_(firstMD == secondSer.getMetadata())
78
79 def testNDArraySerialized(self):
80 try:
81 import numpy
82 except ImportError:
83 pass
84 else:
85 a = numpy.linspace(0.0, 1.0, 1000)
86 unSer1 = UnSerialized(a)
87 ser1 = ISerialized(unSer1)
88 td = ser1.getTypeDescriptor()
89 self.assert_(td == 'ndarray')
90 md = ser1.getMetadata()
91 self.assert_(md['shape'] == a.shape)
92 self.assert_(md['dtype'] == a.dtype.str)
93 buff = ser1.getData()
94 self.assert_(buff == numpy.getbuffer(a))
95 s = Serialized(buff, td, md)
96 us = IUnSerialized(s)
97 final = us.getObject()
98 self.assert_(numpy.getbuffer(a) == numpy.getbuffer(final))
99 self.assert_(a.dtype.str == final.dtype.str)
100 self.assert_(a.shape == final.shape)
101
77
102
78 def testNDArraySerialized(self):
79 try:
80 import numpy
81 except ImportError:
82 pass
83 else:
84 a = numpy.linspace(0.0, 1.0, 1000)
85 unSer1 = UnSerialized(a)
86 ser1 = ISerialized(unSer1)
87 td = ser1.getTypeDescriptor()
88 self.assert_(td == 'ndarray')
89 md = ser1.getMetadata()
90 self.assert_(md['shape'] == a.shape)
91 self.assert_(md['dtype'] == a.dtype.str)
92 buff = ser1.getData()
93 self.assert_(buff == numpy.getbuffer(a))
94 s = Serialized(buff, td, md)
95 us = IUnSerialized(s)
96 final = us.getObject()
97 self.assert_(numpy.getbuffer(a) == numpy.getbuffer(final))
98 self.assert_(a.dtype.str == final.dtype.str)
99 self.assert_(a.shape == final.shape)
100
101
102 No newline at end of file
@@ -1,186 +1,186 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 """Tests for pendingdeferred.py"""
4 """Tests for pendingdeferred.py"""
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 try:
19 try:
20 from twisted.internet import defer
20 from twisted.internet import defer
21 from twisted.python import failure
21 from twisted.python import failure
22
22
23 from IPython.testing.util import DeferredTestCase
23 from IPython.testing.util import DeferredTestCase
24 import IPython.kernel.pendingdeferred as pd
24 import IPython.kernel.pendingdeferred as pd
25 from IPython.kernel import error
25 from IPython.kernel import error
26 from IPython.kernel.util import printer
26 from IPython.kernel.util import printer
27 except ImportError:
27 except ImportError:
28 pass
28 import nose
29 else:
29 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
30
30
31 class Foo(object):
31 class Foo(object):
32
33 def bar(self, bahz):
34 return defer.succeed('blahblah: %s' % bahz)
35
32
36 class TwoPhaseFoo(pd.PendingDeferredManager):
33 def bar(self, bahz):
37
34 return defer.succeed('blahblah: %s' % bahz)
38 def __init__(self, foo):
39 self.foo = foo
40 pd.PendingDeferredManager.__init__(self)
41
35
42 @pd.two_phase
36 class TwoPhaseFoo(pd.PendingDeferredManager):
43 def bar(self, bahz):
44 return self.foo.bar(bahz)
45
37
46 class PendingDeferredManagerTest(DeferredTestCase):
38 def __init__(self, foo):
47
39 self.foo = foo
48 def setUp(self):
40 pd.PendingDeferredManager.__init__(self)
49 self.pdm = pd.PendingDeferredManager()
50
51 def tearDown(self):
52 pass
53
54 def testBasic(self):
55 dDict = {}
56 # Create 10 deferreds and save them
57 for i in range(10):
58 d = defer.Deferred()
59 did = self.pdm.save_pending_deferred(d)
60 dDict[did] = d
61 # Make sure they are begin saved
62 for k in dDict.keys():
63 self.assert_(self.pdm.quick_has_id(k))
64 # Get the pending deferred (block=True), then callback with 'foo' and compare
65 for did in dDict.keys()[0:5]:
66 d = self.pdm.get_pending_deferred(did,block=True)
67 dDict[did].callback('foo')
68 d.addCallback(lambda r: self.assert_(r=='foo'))
69 # Get the pending deferreds with (block=False) and make sure ResultNotCompleted is raised
70 for did in dDict.keys()[5:10]:
71 d = self.pdm.get_pending_deferred(did,block=False)
72 d.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
73 # Now callback the last 5, get them and compare.
74 for did in dDict.keys()[5:10]:
75 dDict[did].callback('foo')
76 d = self.pdm.get_pending_deferred(did,block=False)
77 d.addCallback(lambda r: self.assert_(r=='foo'))
78
79 def test_save_then_delete(self):
80 d = defer.Deferred()
81 did = self.pdm.save_pending_deferred(d)
82 self.assert_(self.pdm.quick_has_id(did))
83 self.pdm.delete_pending_deferred(did)
84 self.assert_(not self.pdm.quick_has_id(did))
85
86 def test_save_get_delete(self):
87 d = defer.Deferred()
88 did = self.pdm.save_pending_deferred(d)
89 d2 = self.pdm.get_pending_deferred(did,True)
90 d2.addErrback(lambda f: self.assertRaises(error.AbortedPendingDeferredError, f.raiseException))
91 self.pdm.delete_pending_deferred(did)
92 return d2
93
94 def test_double_get(self):
95 d = defer.Deferred()
96 did = self.pdm.save_pending_deferred(d)
97 d2 = self.pdm.get_pending_deferred(did,True)
98 d3 = self.pdm.get_pending_deferred(did,True)
99 d3.addErrback(lambda f: self.assertRaises(error.InvalidDeferredID, f.raiseException))
100
101 def test_get_after_callback(self):
102 d = defer.Deferred()
103 did = self.pdm.save_pending_deferred(d)
104 d.callback('foo')
105 d2 = self.pdm.get_pending_deferred(did,True)
106 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
107 self.assert_(not self.pdm.quick_has_id(did))
108
41
109 def test_get_before_callback(self):
42 @pd.two_phase
110 d = defer.Deferred()
43 def bar(self, bahz):
111 did = self.pdm.save_pending_deferred(d)
44 return self.foo.bar(bahz)
112 d2 = self.pdm.get_pending_deferred(did,True)
113 d.callback('foo')
114 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
115 self.assert_(not self.pdm.quick_has_id(did))
116 d = defer.Deferred()
117 did = self.pdm.save_pending_deferred(d)
118 d2 = self.pdm.get_pending_deferred(did,True)
119 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
120 d.callback('foo')
121 self.assert_(not self.pdm.quick_has_id(did))
122
123 def test_get_after_errback(self):
124 class MyError(Exception):
125 pass
126 d = defer.Deferred()
127 did = self.pdm.save_pending_deferred(d)
128 d.errback(failure.Failure(MyError('foo')))
129 d2 = self.pdm.get_pending_deferred(did,True)
130 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
131 self.assert_(not self.pdm.quick_has_id(did))
132
133 def test_get_before_errback(self):
134 class MyError(Exception):
135 pass
136 d = defer.Deferred()
137 did = self.pdm.save_pending_deferred(d)
138 d2 = self.pdm.get_pending_deferred(did,True)
139 d.errback(failure.Failure(MyError('foo')))
140 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
141 self.assert_(not self.pdm.quick_has_id(did))
142 d = defer.Deferred()
143 did = self.pdm.save_pending_deferred(d)
144 d2 = self.pdm.get_pending_deferred(did,True)
145 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
146 d.errback(failure.Failure(MyError('foo')))
147 self.assert_(not self.pdm.quick_has_id(did))
148
149 def test_noresult_noblock(self):
150 d = defer.Deferred()
151 did = self.pdm.save_pending_deferred(d)
152 d2 = self.pdm.get_pending_deferred(did,False)
153 d2.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
154
45
155 def test_with_callbacks(self):
46 class PendingDeferredManagerTest(DeferredTestCase):
156 d = defer.Deferred()
47
157 d.addCallback(lambda r: r+' foo')
48 def setUp(self):
158 d.addCallback(lambda r: r+' bar')
49 self.pdm = pd.PendingDeferredManager()
159 did = self.pdm.save_pending_deferred(d)
160 d2 = self.pdm.get_pending_deferred(did,True)
161 d.callback('bam')
162 d2.addCallback(lambda r: self.assertEquals(r,'bam foo bar'))
163
50
164 def test_with_errbacks(self):
51 def tearDown(self):
165 class MyError(Exception):
52 pass
166 pass
53
54 def testBasic(self):
55 dDict = {}
56 # Create 10 deferreds and save them
57 for i in range(10):
167 d = defer.Deferred()
58 d = defer.Deferred()
168 d.addCallback(lambda r: 'foo')
169 d.addErrback(lambda f: 'caught error')
170 did = self.pdm.save_pending_deferred(d)
59 did = self.pdm.save_pending_deferred(d)
171 d2 = self.pdm.get_pending_deferred(did,True)
60 dDict[did] = d
172 d.errback(failure.Failure(MyError('bam')))
61 # Make sure they are begin saved
173 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
62 for k in dDict.keys():
63 self.assert_(self.pdm.quick_has_id(k))
64 # Get the pending deferred (block=True), then callback with 'foo' and compare
65 for did in dDict.keys()[0:5]:
66 d = self.pdm.get_pending_deferred(did,block=True)
67 dDict[did].callback('foo')
68 d.addCallback(lambda r: self.assert_(r=='foo'))
69 # Get the pending deferreds with (block=False) and make sure ResultNotCompleted is raised
70 for did in dDict.keys()[5:10]:
71 d = self.pdm.get_pending_deferred(did,block=False)
72 d.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
73 # Now callback the last 5, get them and compare.
74 for did in dDict.keys()[5:10]:
75 dDict[did].callback('foo')
76 d = self.pdm.get_pending_deferred(did,block=False)
77 d.addCallback(lambda r: self.assert_(r=='foo'))
78
79 def test_save_then_delete(self):
80 d = defer.Deferred()
81 did = self.pdm.save_pending_deferred(d)
82 self.assert_(self.pdm.quick_has_id(did))
83 self.pdm.delete_pending_deferred(did)
84 self.assert_(not self.pdm.quick_has_id(did))
85
86 def test_save_get_delete(self):
87 d = defer.Deferred()
88 did = self.pdm.save_pending_deferred(d)
89 d2 = self.pdm.get_pending_deferred(did,True)
90 d2.addErrback(lambda f: self.assertRaises(error.AbortedPendingDeferredError, f.raiseException))
91 self.pdm.delete_pending_deferred(did)
92 return d2
93
94 def test_double_get(self):
95 d = defer.Deferred()
96 did = self.pdm.save_pending_deferred(d)
97 d2 = self.pdm.get_pending_deferred(did,True)
98 d3 = self.pdm.get_pending_deferred(did,True)
99 d3.addErrback(lambda f: self.assertRaises(error.InvalidDeferredID, f.raiseException))
100
101 def test_get_after_callback(self):
102 d = defer.Deferred()
103 did = self.pdm.save_pending_deferred(d)
104 d.callback('foo')
105 d2 = self.pdm.get_pending_deferred(did,True)
106 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
107 self.assert_(not self.pdm.quick_has_id(did))
108
109 def test_get_before_callback(self):
110 d = defer.Deferred()
111 did = self.pdm.save_pending_deferred(d)
112 d2 = self.pdm.get_pending_deferred(did,True)
113 d.callback('foo')
114 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
115 self.assert_(not self.pdm.quick_has_id(did))
116 d = defer.Deferred()
117 did = self.pdm.save_pending_deferred(d)
118 d2 = self.pdm.get_pending_deferred(did,True)
119 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
120 d.callback('foo')
121 self.assert_(not self.pdm.quick_has_id(did))
122
123 def test_get_after_errback(self):
124 class MyError(Exception):
125 pass
126 d = defer.Deferred()
127 did = self.pdm.save_pending_deferred(d)
128 d.errback(failure.Failure(MyError('foo')))
129 d2 = self.pdm.get_pending_deferred(did,True)
130 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
131 self.assert_(not self.pdm.quick_has_id(did))
132
133 def test_get_before_errback(self):
134 class MyError(Exception):
135 pass
136 d = defer.Deferred()
137 did = self.pdm.save_pending_deferred(d)
138 d2 = self.pdm.get_pending_deferred(did,True)
139 d.errback(failure.Failure(MyError('foo')))
140 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
141 self.assert_(not self.pdm.quick_has_id(did))
142 d = defer.Deferred()
143 did = self.pdm.save_pending_deferred(d)
144 d2 = self.pdm.get_pending_deferred(did,True)
145 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
146 d.errback(failure.Failure(MyError('foo')))
147 self.assert_(not self.pdm.quick_has_id(did))
174
148
175 def test_nested_deferreds(self):
149 def test_noresult_noblock(self):
176 d = defer.Deferred()
150 d = defer.Deferred()
177 d2 = defer.Deferred()
151 did = self.pdm.save_pending_deferred(d)
178 d.addCallback(lambda r: d2)
152 d2 = self.pdm.get_pending_deferred(did,False)
179 did = self.pdm.save_pending_deferred(d)
153 d2.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
180 d.callback('foo')
154
181 d3 = self.pdm.get_pending_deferred(did,False)
155 def test_with_callbacks(self):
182 d3.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
156 d = defer.Deferred()
183 d2.callback('bar')
157 d.addCallback(lambda r: r+' foo')
184 d3 = self.pdm.get_pending_deferred(did,False)
158 d.addCallback(lambda r: r+' bar')
185 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
159 did = self.pdm.save_pending_deferred(d)
160 d2 = self.pdm.get_pending_deferred(did,True)
161 d.callback('bam')
162 d2.addCallback(lambda r: self.assertEquals(r,'bam foo bar'))
163
164 def test_with_errbacks(self):
165 class MyError(Exception):
166 pass
167 d = defer.Deferred()
168 d.addCallback(lambda r: 'foo')
169 d.addErrback(lambda f: 'caught error')
170 did = self.pdm.save_pending_deferred(d)
171 d2 = self.pdm.get_pending_deferred(did,True)
172 d.errback(failure.Failure(MyError('bam')))
173 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
174
175 def test_nested_deferreds(self):
176 d = defer.Deferred()
177 d2 = defer.Deferred()
178 d.addCallback(lambda r: d2)
179 did = self.pdm.save_pending_deferred(d)
180 d.callback('foo')
181 d3 = self.pdm.get_pending_deferred(did,False)
182 d3.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
183 d2.callback('bar')
184 d3 = self.pdm.get_pending_deferred(did,False)
185 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
186
186
@@ -1,50 +1,51 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the kernel.task.py module."""
3 """This file contains unittests for the kernel.task.py module."""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 try:
18 try:
19 import time
19 import time
20
20
21 from twisted.internet import defer
21 from twisted.internet import defer
22 from twisted.trial import unittest
22 from twisted.trial import unittest
23
23
24 from IPython.kernel import task, controllerservice as cs, engineservice as es
24 from IPython.kernel import task, controllerservice as cs, engineservice as es
25 from IPython.kernel.multiengine import IMultiEngine
25 from IPython.kernel.multiengine import IMultiEngine
26 from IPython.testing.util import DeferredTestCase
26 from IPython.testing.util import DeferredTestCase
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
28 except ImportError:
28 except ImportError:
29 pass
29 import nose
30 else:
30 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
31 #-------------------------------------------------------------------------------
32 # Tests
33 #-------------------------------------------------------------------------------
34
31
35 class BasicTaskControllerTestCase(DeferredTestCase, ITaskControllerTestCase):
32 #-------------------------------------------------------------------------------
33 # Tests
34 #-------------------------------------------------------------------------------
35
36 class BasicTaskControllerTestCase(DeferredTestCase, ITaskControllerTestCase):
37
38 def setUp(self):
39 self.controller = cs.ControllerService()
40 self.controller.startService()
41 self.multiengine = IMultiEngine(self.controller)
42 self.tc = task.ITaskController(self.controller)
43 self.tc.failurePenalty = 0
44 self.engines=[]
36
45
37 def setUp(self):
46 def tearDown(self):
38 self.controller = cs.ControllerService()
47 self.controller.stopService()
39 self.controller.startService()
48 for e in self.engines:
40 self.multiengine = IMultiEngine(self.controller)
49 e.stopService()
41 self.tc = task.ITaskController(self.controller)
42 self.tc.failurePenalty = 0
43 self.engines=[]
44
45 def tearDown(self):
46 self.controller.stopService()
47 for e in self.engines:
48 e.stopService()
49
50
50
51
@@ -1,161 +1,162 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 __docformat__ = "restructuredtext en"
4 __docformat__ = "restructuredtext en"
5
5
6 #-------------------------------------------------------------------------------
6 #-------------------------------------------------------------------------------
7 # Copyright (C) 2008 The IPython Development Team
7 # Copyright (C) 2008 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16
16
17 try:
17 try:
18 import time
18 import time
19
19
20 from twisted.internet import defer, reactor
20 from twisted.internet import defer, reactor
21
21
22 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
22 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
23
23
24 from IPython.kernel import task as taskmodule
24 from IPython.kernel import task as taskmodule
25 from IPython.kernel import controllerservice as cs
25 from IPython.kernel import controllerservice as cs
26 import IPython.kernel.multiengine as me
26 import IPython.kernel.multiengine as me
27 from IPython.testing.util import DeferredTestCase
27 from IPython.testing.util import DeferredTestCase
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
29 from IPython.kernel.taskfc import IFCTaskController
29 from IPython.kernel.taskfc import IFCTaskController
30 from IPython.kernel.util import printer
30 from IPython.kernel.util import printer
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
32 from IPython.kernel.clientconnector import ClientConnector
32 from IPython.kernel.clientconnector import ClientConnector
33 from IPython.kernel.error import CompositeError
33 from IPython.kernel.error import CompositeError
34 from IPython.kernel.parallelfunction import ParallelFunction
34 from IPython.kernel.parallelfunction import ParallelFunction
35 except ImportError:
35 except ImportError:
36 pass
36 import nose
37 else:
37 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
38
38
39 #-------------------------------------------------------------------------------
40 # Tests
41 #-------------------------------------------------------------------------------
42
39
43 def _raise_it(f):
40 #-------------------------------------------------------------------------------
44 try:
41 # Tests
45 f.raiseException()
42 #-------------------------------------------------------------------------------
46 except CompositeError, e:
47 e.raise_exception()
48
43
49 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
44 def _raise_it(f):
45 try:
46 f.raiseException()
47 except CompositeError, e:
48 e.raise_exception()
50
49
51 def setUp(self):
50 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
52
51
53 self.engines = []
52 def setUp(self):
54
53
55 self.controller = cs.ControllerService()
54 self.engines = []
56 self.controller.startService()
55
57 self.imultiengine = me.IMultiEngine(self.controller)
56 self.controller = cs.ControllerService()
58 self.itc = taskmodule.ITaskController(self.controller)
57 self.controller.startService()
59 self.itc.failurePenalty = 0
58 self.imultiengine = me.IMultiEngine(self.controller)
60
59 self.itc = taskmodule.ITaskController(self.controller)
61 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
60 self.itc.failurePenalty = 0
62 self.tc_referenceable = IFCTaskController(self.itc)
61
63
62 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
64 self.controller_tub = Tub()
63 self.tc_referenceable = IFCTaskController(self.itc)
65 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
64
66 self.controller_tub.setLocation('127.0.0.1:10105')
65 self.controller_tub = Tub()
67
66 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
68 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
67 self.controller_tub.setLocation('127.0.0.1:10105')
69 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
68
70 self.controller_tub.startService()
69 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
71
70 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
72 self.client_tub = ClientConnector()
71 self.controller_tub.startService()
73 d = self.client_tub.get_multiengine_client(mec_furl)
72
74 d.addCallback(self.handle_mec_client)
73 self.client_tub = ClientConnector()
75 d.addCallback(lambda _: self.client_tub.get_task_client(tc_furl))
74 d = self.client_tub.get_multiengine_client(mec_furl)
76 d.addCallback(self.handle_tc_client)
75 d.addCallback(self.handle_mec_client)
77 return d
76 d.addCallback(lambda _: self.client_tub.get_task_client(tc_furl))
78
77 d.addCallback(self.handle_tc_client)
79 def handle_mec_client(self, client):
78 return d
80 self.multiengine = client
79
80 def handle_mec_client(self, client):
81 self.multiengine = client
82
83 def handle_tc_client(self, client):
84 self.tc = client
85
86 def tearDown(self):
87 dlist = []
88 # Shut down the multiengine client
89 d = self.client_tub.tub.stopService()
90 dlist.append(d)
91 # Shut down the engines
92 for e in self.engines:
93 e.stopService()
94 # Shut down the controller
95 d = self.controller_tub.stopService()
96 d.addBoth(lambda _: self.controller.stopService())
97 dlist.append(d)
98 return defer.DeferredList(dlist)
99
100 def test_mapper(self):
101 self.addEngine(1)
102 m = self.tc.mapper()
103 self.assertEquals(m.task_controller,self.tc)
104 self.assertEquals(m.clear_before,False)
105 self.assertEquals(m.clear_after,False)
106 self.assertEquals(m.retries,0)
107 self.assertEquals(m.recovery_task,None)
108 self.assertEquals(m.depend,None)
109 self.assertEquals(m.block,True)
110
111 def test_map_default(self):
112 self.addEngine(1)
113 m = self.tc.mapper()
114 d = m.map(lambda x: 2*x, range(10))
115 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
116 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
117 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
118 return d
119
120 def test_map_noblock(self):
121 self.addEngine(1)
122 m = self.tc.mapper(block=False)
123 d = m.map(lambda x: 2*x, range(10))
124 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
125 return d
126
127 def test_mapper_fail(self):
128 self.addEngine(1)
129 m = self.tc.mapper()
130 d = m.map(lambda x: 1/0, range(10))
131 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
132 return d
133
134 def test_parallel(self):
135 self.addEngine(1)
136 p = self.tc.parallel()
137 self.assert_(isinstance(p, ParallelFunction))
138 @p
139 def f(x): return 2*x
140 d = f(range(10))
141 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
142 return d
81
143
82 def handle_tc_client(self, client):
144 def test_parallel_noblock(self):
83 self.tc = client
145 self.addEngine(1)
146 p = self.tc.parallel(block=False)
147 self.assert_(isinstance(p, ParallelFunction))
148 @p
149 def f(x): return 2*x
150 d = f(range(10))
151 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
152 return d
84
153
85 def tearDown(self):
154 def test_parallel_fail(self):
86 dlist = []
155 self.addEngine(1)
87 # Shut down the multiengine client
156 p = self.tc.parallel()
88 d = self.client_tub.tub.stopService()
157 self.assert_(isinstance(p, ParallelFunction))
89 dlist.append(d)
158 @p
90 # Shut down the engines
159 def f(x): return 1/0
91 for e in self.engines:
160 d = f(range(10))
92 e.stopService()
161 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
93 # Shut down the controller
162 return d No newline at end of file
94 d = self.controller_tub.stopService()
95 d.addBoth(lambda _: self.controller.stopService())
96 dlist.append(d)
97 return defer.DeferredList(dlist)
98
99 def test_mapper(self):
100 self.addEngine(1)
101 m = self.tc.mapper()
102 self.assertEquals(m.task_controller,self.tc)
103 self.assertEquals(m.clear_before,False)
104 self.assertEquals(m.clear_after,False)
105 self.assertEquals(m.retries,0)
106 self.assertEquals(m.recovery_task,None)
107 self.assertEquals(m.depend,None)
108 self.assertEquals(m.block,True)
109
110 def test_map_default(self):
111 self.addEngine(1)
112 m = self.tc.mapper()
113 d = m.map(lambda x: 2*x, range(10))
114 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
115 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
116 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
117 return d
118
119 def test_map_noblock(self):
120 self.addEngine(1)
121 m = self.tc.mapper(block=False)
122 d = m.map(lambda x: 2*x, range(10))
123 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
124 return d
125
126 def test_mapper_fail(self):
127 self.addEngine(1)
128 m = self.tc.mapper()
129 d = m.map(lambda x: 1/0, range(10))
130 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
131 return d
132
133 def test_parallel(self):
134 self.addEngine(1)
135 p = self.tc.parallel()
136 self.assert_(isinstance(p, ParallelFunction))
137 @p
138 def f(x): return 2*x
139 d = f(range(10))
140 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
141 return d
142
143 def test_parallel_noblock(self):
144 self.addEngine(1)
145 p = self.tc.parallel(block=False)
146 self.assert_(isinstance(p, ParallelFunction))
147 @p
148 def f(x): return 2*x
149 d = f(range(10))
150 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
151 return d
152
153 def test_parallel_fail(self):
154 self.addEngine(1)
155 p = self.tc.parallel()
156 self.assert_(isinstance(p, ParallelFunction))
157 @p
158 def f(x): return 1/0
159 d = f(range(10))
160 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
161 return d No newline at end of file
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
@@ -1,360 +1,391 b''
1 .. _development:
1 .. _development:
2
2
3 ==================================
3 ==================================
4 IPython development guidelines
4 IPython development guidelines
5 ==================================
5 ==================================
6
6
7 .. contents::
7 .. contents::
8
8
9
9
10 Overview
10 Overview
11 ========
11 ========
12
12
13 IPython is the next generation of IPython. It is named such for two reasons:
13 IPython is the next generation of IPython. It is named such for two reasons:
14
14
15 - Eventually, IPython will become IPython version 1.0.
15 - Eventually, IPython will become IPython version 1.0.
16 - This new code base needs to be able to co-exist with the existing IPython until
16 - This new code base needs to be able to co-exist with the existing IPython until
17 it is a full replacement for it. Thus we needed a different name. We couldn't
17 it is a full replacement for it. Thus we needed a different name. We couldn't
18 use ``ipython`` (lowercase) as some files systems are case insensitive.
18 use ``ipython`` (lowercase) as some files systems are case insensitive.
19
19
20 There are two, no three, main goals of the IPython effort:
20 There are two, no three, main goals of the IPython effort:
21
21
22 1. Clean up the existing codebase and write lots of tests.
22 1. Clean up the existing codebase and write lots of tests.
23 2. Separate the core functionality of IPython from the terminal to enable IPython
23 2. Separate the core functionality of IPython from the terminal to enable IPython
24 to be used from within a variety of GUI applications.
24 to be used from within a variety of GUI applications.
25 3. Implement a system for interactive parallel computing.
25 3. Implement a system for interactive parallel computing.
26
26
27 While the third goal may seem a bit unrelated to the main focus of IPython, it turns
27 While the third goal may seem a bit unrelated to the main focus of IPython, it turns
28 out that the technologies required for this goal are nearly identical with those
28 out that the technologies required for this goal are nearly identical with those
29 required for goal two. This is the main reason the interactive parallel computing
29 required for goal two. This is the main reason the interactive parallel computing
30 capabilities are being put into IPython proper. Currently the third of these goals is
30 capabilities are being put into IPython proper. Currently the third of these goals is
31 furthest along.
31 furthest along.
32
32
33 This document describes IPython from the perspective of developers.
33 This document describes IPython from the perspective of developers.
34
34
35
35
36 Project organization
36 Project organization
37 ====================
37 ====================
38
38
39 Subpackages
39 Subpackages
40 -----------
40 -----------
41
41
42 IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own:
42 IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own:
43
43
44 - **Dependencies**. One of the most important things to keep in mind in
44 - **Dependencies**. One of the most important things to keep in mind in
45 partitioning code amongst subpackages, is that they should be used to cleanly
45 partitioning code amongst subpackages, is that they should be used to cleanly
46 encapsulate dependencies.
46 encapsulate dependencies.
47 - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that
47 - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that
48 contains all of the tests for that package. For information about writing tests
48 contains all of the tests for that package. For information about writing tests
49 for IPython, see the `Testing System`_ section of this document.
49 for IPython, see the `Testing System`_ section of this document.
50 - **Configuration**. Each subpackage should have its own ``config`` subdirectory
50 - **Configuration**. Each subpackage should have its own ``config`` subdirectory
51 that contains the configuration information for the components of the
51 that contains the configuration information for the components of the
52 subpackage. For information about how the IPython configuration system
52 subpackage. For information about how the IPython configuration system
53 works, see the `Configuration System`_ section of this document.
53 works, see the `Configuration System`_ section of this document.
54 - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that
54 - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that
55 contains all of the command line scripts associated with the subpackage.
55 contains all of the command line scripts associated with the subpackage.
56
56
57 Installation and dependencies
57 Installation and dependencies
58 -----------------------------
58 -----------------------------
59
59
60 IPython will not use `setuptools`_ for installation. Instead, we will use standard
60 IPython will not use `setuptools`_ for installation. Instead, we will use standard
61 ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice
61 ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice
62 features that `setuptools`_ has (like namespace packages), the current implementation
62 features that `setuptools`_ has (like namespace packages), the current implementation
63 of `setuptools`_ has performance problems, particularly on shared file systems. In
63 of `setuptools`_ has performance problems, particularly on shared file systems. In
64 particular, when Python packages are installed on NSF file systems, import times
64 particular, when Python packages are installed on NSF file systems, import times
65 become much too long (up towards 10 seconds).
65 become much too long (up towards 10 seconds).
66
66
67 Because IPython is being used extensively in the context of high performance
67 Because IPython is being used extensively in the context of high performance
68 computing, where performance is critical but shared file systems are common, we feel
68 computing, where performance is critical but shared file systems are common, we feel
69 these performance hits are not acceptable. Thus, until the performance problems
69 these performance hits are not acceptable. Thus, until the performance problems
70 associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We
70 associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We
71 are hopeful that these problems will be addressed and that we will eventually begin
71 are hopeful that these problems will be addressed and that we will eventually begin
72 using `setuptools`_. Because of this, we are trying to organize IPython in a way that
72 using `setuptools`_. Because of this, we are trying to organize IPython in a way that
73 will make the eventual transition to `setuptools`_ as painless as possible.
73 will make the eventual transition to `setuptools`_ as painless as possible.
74
74
75 Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows:
75 Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows:
76
76
77 - Distinguish between required and optional dependencies. However, the required
77 - Distinguish between required and optional dependencies. However, the required
78 dependencies for IPython should be only the Python standard library.
78 dependencies for IPython should be only the Python standard library.
79 - Upon installation check to see which optional dependencies are present and tell
79 - Upon installation check to see which optional dependencies are present and tell
80 the user which parts of IPython need which optional dependencies.
80 the user which parts of IPython need which optional dependencies.
81
81
82 It is absolutely critical that each subpackage of IPython has a clearly specified set
82 It is absolutely critical that each subpackage of IPython has a clearly specified set
83 of dependencies and that dependencies are not carelessly inherited from other IPython
83 of dependencies and that dependencies are not carelessly inherited from other IPython
84 subpackages. Furthermore, tests that have certain dependencies should not fail if
84 subpackages. Furthermore, tests that have certain dependencies should not fail if
85 those dependencies are not present. Instead they should be skipped and print a
85 those dependencies are not present. Instead they should be skipped and print a
86 message.
86 message.
87
87
88 .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
88 .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
89 .. _distutils: http://docs.python.org/lib/module-distutils.html
89 .. _distutils: http://docs.python.org/lib/module-distutils.html
90 .. _Matplotlib: http://matplotlib.sourceforge.net/
90 .. _Matplotlib: http://matplotlib.sourceforge.net/
91
91
92 Specific subpackages
92 Specific subpackages
93 --------------------
93 --------------------
94
94
95 ``core``
95 ``core``
96 This is the core functionality of IPython that is independent of the
96 This is the core functionality of IPython that is independent of the
97 terminal, network and GUIs. Most of the code that is in the current
97 terminal, network and GUIs. Most of the code that is in the current
98 IPython trunk will be refactored, cleaned up and moved here.
98 IPython trunk will be refactored, cleaned up and moved here.
99
99
100 ``kernel``
100 ``kernel``
101 The enables the IPython core to be expose to a the network. This is
101 The enables the IPython core to be expose to a the network. This is
102 also where all of the parallel computing capabilities are to be found.
102 also where all of the parallel computing capabilities are to be found.
103
103
104 ``config``
104 ``config``
105 The configuration package used by IPython.
105 The configuration package used by IPython.
106
106
107 ``frontends``
107 ``frontends``
108 The various frontends for IPython. A frontend is the end-user application
108 The various frontends for IPython. A frontend is the end-user application
109 that exposes the capabilities of IPython to the user. The most basic frontend
109 that exposes the capabilities of IPython to the user. The most basic frontend
110 will simply be a terminal based application that looks just like today 's
110 will simply be a terminal based application that looks just like today 's
111 IPython. Other frontends will likely be more powerful and based on GUI toolkits.
111 IPython. Other frontends will likely be more powerful and based on GUI toolkits.
112
112
113 ``notebook``
113 ``notebook``
114 An application that allows users to work with IPython notebooks.
114 An application that allows users to work with IPython notebooks.
115
115
116 ``tools``
116 ``tools``
117 This is where general utilities go.
117 This is where general utilities go.
118
118
119
119
120 Version control
120 Version control
121 ===============
121 ===============
122
122
123 In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people
123 In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people
124 to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython
124 to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython
125 development. First, you should install Bazaar. After you have done that, make
125 development. First, you should install Bazaar. After you have done that, make
126 sure that it is working by getting the latest main branch of IPython::
126 sure that it is working by getting the latest main branch of IPython::
127
127
128 $ bzr branch lp:ipython
128 $ bzr branch lp:ipython
129
129
130 Now you can create a new branch for you to do your work in::
130 Now you can create a new branch for you to do your work in::
131
131
132 $ bzr branch ipython ipython-mybranch
132 $ bzr branch ipython ipython-mybranch
133
133
134 The typical work cycle in this branch will be to make changes in `ipython-mybranch`
134 The typical work cycle in this branch will be to make changes in `ipython-mybranch`
135 and then commit those changes using the commit command::
135 and then commit those changes using the commit command::
136
136
137 $ ...do work in ipython-mybranch...
137 $ ...do work in ipython-mybranch...
138 $ bzr ci -m "the commit message goes here"
138 $ bzr ci -m "the commit message goes here"
139
139
140 Please note that since we now don't use an old-style linear ChangeLog
140 Please note that since we now don't use an old-style linear ChangeLog
141 (that tends to cause problems with distributed version control
141 (that tends to cause problems with distributed version control
142 systems), you should ensure that your log messages are reasonably
142 systems), you should ensure that your log messages are reasonably
143 detailed. Use a docstring-like approach in the commit messages
143 detailed. Use a docstring-like approach in the commit messages
144 (including the second line being left *blank*)::
144 (including the second line being left *blank*)::
145
145
146 Single line summary of changes being committed.
146 Single line summary of changes being committed.
147
147
148 - more details when warranted ...
148 - more details when warranted ...
149 - including crediting outside contributors if they sent the
149 - including crediting outside contributors if they sent the
150 code/bug/idea!
150 code/bug/idea!
151
151
152 If we couple this with a policy of making single commits for each
152 If we couple this with a policy of making single commits for each
153 reasonably atomic change, the bzr log should give an excellent view of
153 reasonably atomic change, the bzr log should give an excellent view of
154 the project, and the `--short` log option becomes a nice summary.
154 the project, and the `--short` log option becomes a nice summary.
155
155
156 While working with this branch, it is a good idea to merge in changes that have been
156 While working with this branch, it is a good idea to merge in changes that have been
157 made upstream in the parent branch. This can be done by doing::
157 made upstream in the parent branch. This can be done by doing::
158
158
159 $ bzr pull
159 $ bzr pull
160
160
161 If this command shows that the branches have diverged, then you should do a merge
161 If this command shows that the branches have diverged, then you should do a merge
162 instead::
162 instead::
163
163
164 $ bzr merge lp:ipython
164 $ bzr merge lp:ipython
165
165
166 If you want others to be able to see your branch, you can create an account with
166 If you want others to be able to see your branch, you can create an account with
167 launchpad and push the branch to your own workspace::
167 launchpad and push the branch to your own workspace::
168
168
169 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
169 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
170
170
171 Finally, once the work in your branch is done, you can merge your changes back into
171 Finally, once the work in your branch is done, you can merge your changes back into
172 the `ipython` branch by using merge::
172 the `ipython` branch by using merge::
173
173
174 $ cd ipython
174 $ cd ipython
175 $ merge ../ipython-mybranch
175 $ merge ../ipython-mybranch
176 [resolve any conflicts]
176 [resolve any conflicts]
177 $ bzr ci -m "Fixing that bug"
177 $ bzr ci -m "Fixing that bug"
178 $ bzr push
178 $ bzr push
179
179
180 But this will require you to have write permissions to the `ipython` branch. It you don't
180 But this will require you to have write permissions to the `ipython` branch. It you don't
181 you can tell one of the IPython devs about your branch and they can do the merge for you.
181 you can tell one of the IPython devs about your branch and they can do the merge for you.
182
182
183 More information about Bazaar workflows can be found `here`__.
183 More information about Bazaar workflows can be found `here`__.
184
184
185 .. __: http://subversion.tigris.org/
185 .. __: http://subversion.tigris.org/
186 .. __: http://bazaar-vcs.org/
186 .. __: http://bazaar-vcs.org/
187 .. __: http://www.launchpad.net/ipython
187 .. __: http://www.launchpad.net/ipython
188 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
188 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
189
189
190 Documentation
190 Documentation
191 =============
191 =============
192
192
193 Standalone documentation
193 Standalone documentation
194 ------------------------
194 ------------------------
195
195
196 All standalone documentation should be written in plain text (``.txt``) files using
196 All standalone documentation should be written in plain text (``.txt``) files using
197 `reStructuredText`_ for markup and formatting. All such documentation should be placed
197 `reStructuredText`_ for markup and formatting. All such documentation should be placed
198 in the top level directory ``docs`` of the IPython source tree. Or, when appropriate,
198 in the top level directory ``docs`` of the IPython source tree. Or, when appropriate,
199 a suitably named subdirectory should be used. The documentation in this location will
199 a suitably named subdirectory should be used. The documentation in this location will
200 serve as the main source for IPython documentation and all existing documentation
200 serve as the main source for IPython documentation and all existing documentation
201 should be converted to this format.
201 should be converted to this format.
202
202
203 In the future, the text files in the ``docs`` directory will be used to generate all
203 In the future, the text files in the ``docs`` directory will be used to generate all
204 forms of documentation for IPython. This include documentation on the IPython website
204 forms of documentation for IPython. This include documentation on the IPython website
205 as well as *pdf* documentation.
205 as well as *pdf* documentation.
206
206
207 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
207 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
208
208
209 Docstring format
209 Docstring format
210 ----------------
210 ----------------
211
211
212 Good docstrings are very important. All new code will use `Epydoc`_ for generating API
212 Good docstrings are very important. All new code will use `Epydoc`_ for generating API
213 docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use
213 docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use
214 `reStructuredText`_ for markup and formatting, since it is understood by a wide
214 `reStructuredText`_ for markup and formatting, since it is understood by a wide
215 variety of tools. This means that if in the future we have any reason to change from
215 variety of tools. This means that if in the future we have any reason to change from
216 `Epydoc`_ to something else, we'll have fewer transition pains.
216 `Epydoc`_ to something else, we'll have fewer transition pains.
217
217
218 Details about using `reStructuredText`_ for docstrings can be found `here
218 Details about using `reStructuredText`_ for docstrings can be found `here
219 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
219 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
220
220
221 .. _Epydoc: http://epydoc.sourceforge.net/
221 .. _Epydoc: http://epydoc.sourceforge.net/
222
222
223 Additional PEPs of interest regarding documentation of code:
223 Additional PEPs of interest regarding documentation of code:
224
224
225 - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
225 - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
226 - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_
226 - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_
227 - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
227 - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
228
228
229
229
230 Coding conventions
230 Coding conventions
231 ==================
231 ==================
232
232
233 General
233 General
234 -------
234 -------
235
235
236 In general, we'll try to follow the standard Python style conventions as described here:
236 In general, we'll try to follow the standard Python style conventions as described here:
237
237
238 - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_
238 - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_
239
239
240
240
241 Other comments:
241 Other comments:
242
242
243 - In a large file, top level classes and functions should be
243 - In a large file, top level classes and functions should be
244 separated by 2-3 lines to make it easier to separate them visually.
244 separated by 2-3 lines to make it easier to separate them visually.
245 - Use 4 spaces for indentation.
245 - Use 4 spaces for indentation.
246 - Keep the ordering of methods the same in classes that have the same
246 - Keep the ordering of methods the same in classes that have the same
247 methods. This is particularly true for classes that implement
247 methods. This is particularly true for classes that implement
248 similar interfaces and for interfaces that are similar.
248 similar interfaces and for interfaces that are similar.
249
249
250 Naming conventions
250 Naming conventions
251 ------------------
251 ------------------
252
252
253 In terms of naming conventions, we'll follow the guidelines from the `Style Guide for
253 In terms of naming conventions, we'll follow the guidelines from the `Style Guide for
254 Python Code`_.
254 Python Code`_.
255
255
256 For all new IPython code (and much existing code is being refactored), we'll use:
256 For all new IPython code (and much existing code is being refactored), we'll use:
257
257
258 - All ``lowercase`` module names.
258 - All ``lowercase`` module names.
259
259
260 - ``CamelCase`` for class names.
260 - ``CamelCase`` for class names.
261
261
262 - ``lowercase_with_underscores`` for methods, functions, variables and attributes.
262 - ``lowercase_with_underscores`` for methods, functions, variables and attributes.
263
263
264 This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new
264 This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new
265 convention, providing shadow names for backward compatibility in public interfaces.
265 convention, providing shadow names for backward compatibility in public interfaces.
266
266
267 There are, however, some important exceptions to these rules. In some cases, IPython
267 There are, however, some important exceptions to these rules. In some cases, IPython
268 code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy:
268 code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy:
269
269
270 - If you are subclassing a class that uses different conventions, use its
270 - If you are subclassing a class that uses different conventions, use its
271 naming conventions throughout your subclass. Thus, if you are creating a
271 naming conventions throughout your subclass. Thus, if you are creating a
272 Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.``
272 Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.``
273
273
274 - All IPython's official interfaces should use our conventions. In some cases
274 - All IPython's official interfaces should use our conventions. In some cases
275 this will mean that you need to provide shadow names (first implement ``fooBar``
275 this will mean that you need to provide shadow names (first implement ``fooBar``
276 and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it
276 and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it
277 will probably be necessary at times. But, please use this sparingly!
277 will probably be necessary at times. But, please use this sparingly!
278
278
279 Implementation-specific *private* methods will use ``_single_underscore_prefix``.
279 Implementation-specific *private* methods will use ``_single_underscore_prefix``.
280 Names with a leading double underscore will *only* be used in special cases, as they
280 Names with a leading double underscore will *only* be used in special cases, as they
281 makes subclassing difficult (such names are not easily seen by child classes).
281 makes subclassing difficult (such names are not easily seen by child classes).
282
282
283 Occasionally some run-in lowercase names are used, but mostly for very short names or
283 Occasionally some run-in lowercase names are used, but mostly for very short names or
284 where we are implementing methods very similar to existing ones in a base class (like
284 where we are implementing methods very similar to existing ones in a base class (like
285 ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent).
285 ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent).
286
286
287 The old IPython codebase has a big mix of classes and modules prefixed with an
287 The old IPython codebase has a big mix of classes and modules prefixed with an
288 explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as
288 explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as
289 namespaces offer cleaner prefixing. The only case where this approach is justified is
289 namespaces offer cleaner prefixing. The only case where this approach is justified is
290 for classes which are expected to be imported into external namespaces and a very
290 for classes which are expected to be imported into external namespaces and a very
291 generic name (like Shell) is too likely to clash with something else. We'll need to
291 generic name (like Shell) is too likely to clash with something else. We'll need to
292 revisit this issue as we clean up and refactor the code, but in general we should
292 revisit this issue as we clean up and refactor the code, but in general we should
293 remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix
293 remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix
294 seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred.
294 seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred.
295
295
296 .. _devel_testing:
296 .. _devel_testing:
297
297
298 Testing system
298 Testing system
299 ==============
299 ==============
300
300
301 It is extremely important that all code contributed to IPython has tests. Tests should
301 It is extremely important that all code contributed to IPython has tests. Tests should
302 be written as unittests, doctests or as entities that the `Nose`_ testing package will
302 be written as unittests, doctests or as entities that the `Nose`_ testing package will
303 find. Regardless of how the tests are written, we will use `Nose`_ for discovering and
303 find. Regardless of how the tests are written, we will use `Nose`_ for discovering and
304 running the tests. `Nose`_ will be required to run the IPython test suite, but will
304 running the tests. `Nose`_ will be required to run the IPython test suite, but will
305 not be required to simply use IPython.
305 not be required to simply use IPython.
306
306
307 .. _Nose: http://code.google.com/p/python-nose/
307 .. _Nose: http://code.google.com/p/python-nose/
308
308
309 Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class
309 Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class
310 that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to
310 that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to
311 run the tests and the twisted reactor will be handled correctly.
311 run the tests and the twisted reactor will be handled correctly.
312
312
313 .. __: http://www.twistedmatrix.com
313 .. __: http://www.twistedmatrix.com
314
314
315 Each subpackage in IPython should have its own ``tests`` directory that contains all
315 Each subpackage in IPython should have its own ``tests`` directory that contains all
316 of the tests for that subpackage. This allows each subpackage to be self-contained. If
316 of the tests for that subpackage. This allows each subpackage to be self-contained. If
317 a subpackage has any dependencies beyond the Python standard library, the tests for
317 a subpackage has any dependencies beyond the Python standard library, the tests for
318 that subpackage should be skipped if the dependencies are not found. This is very
318 that subpackage should be skipped if the dependencies are not found. This is very
319 important so users don't get tests failing simply because they don't have dependencies.
319 important so users don't get tests failing simply because they don't have dependencies.
320
320
321 We also need to look into use Noses ability to tag tests to allow a more modular
321 We also need to look into use Noses ability to tag tests to allow a more modular
322 approach of running tests.
322 approach of running tests.
323
323
324 .. _devel_config:
324 .. _devel_config:
325
325
326 Configuration system
326 Configuration system
327 ====================
327 ====================
328
328
329 IPython uses `.ini`_ files for configuration purposes. This represents a huge
329 IPython uses `.ini`_ files for configuration purposes. This represents a huge
330 improvement over the configuration system used in IPython. IPython works with these
330 improvement over the configuration system used in IPython. IPython works with these
331 files using the `ConfigObj`_ package, which IPython includes as
331 files using the `ConfigObj`_ package, which IPython includes as
332 ``ipython1/external/configobj.py``.
332 ``ipython1/external/configobj.py``.
333
333
334 Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython
334 Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython
335 should contain a ``config`` subdirectory that contains all of the configuration
335 should contain a ``config`` subdirectory that contains all of the configuration
336 information for the subpackage. To see how configuration information is defined (along
336 information for the subpackage. To see how configuration information is defined (along
337 with defaults) see at the examples in ``ipython1/kernel/config`` and
337 with defaults) see at the examples in ``ipython1/kernel/config`` and
338 ``ipython1/core/config``. Likewise, to see how the configuration information is used,
338 ``ipython1/core/config``. Likewise, to see how the configuration information is used,
339 see examples in ``ipython1/kernel/scripts/ipengine.py``.
339 see examples in ``ipython1/kernel/scripts/ipengine.py``.
340
340
341 Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are
341 Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are
342 calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model.
342 calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model.
343 We won't actually use `Traits`_, but will implement something similar in pure Python.
343 We won't actually use `Traits`_, but will implement something similar in pure Python.
344 But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files
344 But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files
345 underneath the hood. Talk to Fernando if you are interested in working on this part of
345 underneath the hood. Talk to Fernando if you are interested in working on this part of
346 IPython. The current prototype of ``tconfig`` is located in the IPython sandbox.
346 IPython. The current prototype of ``tconfig`` is located in the IPython sandbox.
347
347
348 .. _.ini: http://docs.python.org/lib/module-ConfigParser.html
348 .. _.ini: http://docs.python.org/lib/module-ConfigParser.html
349 .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
349 .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
350 .. _Traits: http://code.enthought.com/traits/
350 .. _Traits: http://code.enthought.com/traits/
351
351
352 Installation and testing scenarios
353 ==================================
354
355 This section outlines the various scenarios that we need to test before we release an IPython version. These scenarios represent different ways of installing IPython and its dependencies.
356
357 Installation scenarios
358 ----------------------
359
360 1. Install from tarball using `python setup.py install`.
361 a. With only readline+nose dependencies installed.
362 b. With all dependencies installed (readline, zope.interface,
363 Twisted, foolscap, Sphinx, nose, pyOpenSSL).
364 2. Install using easy_install.
365 a. With only readline+nose dependencies installed.
366 i. Default dependencies: `easy_install ipython-0.9.beta3-py2.5.egg`
367 ii. Optional dependency sets: `easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]`
368 b. With all dependencies already installed.
369
370
371 Tests to run for these scenarios
372 --------------------------------
373
374 1. Run the full test suite.
375 2. Start a controller and engines and try a few things by hand.
376 a. Using ipcluster.
377 b. Using ipcontroller/ipengine by hand.
378 3. Run a few of the parallel examples.
379 4. Try the kernel with and without security with and without PyOpenSSL
380 installed.
381 5. Beat on the IPython terminal a bunch.
382 6. Make sure that furl files are being put in proper locations.
352
383
353
384
354
385
355
386
356
387
357
388
358
389
359
390
360
391
@@ -1,272 +1,286 b''
1 =============================
1 =============================
2 Basic installation of IPython
2 Basic installation of IPython
3 =============================
3 =============================
4
4
5 Installation
5 Installation
6 ============
6 ============
7
7
8 Instant instructions
8 Instant instructions
9 --------------------
9 --------------------
10
10
11 If you are of the impatient kind, under Linux/Unix simply untar/unzip
11 If you are of the impatient kind, under Linux/Unix simply untar/unzip
12 the download, then install with 'python setup.py install'. Under
12 the download, then install with 'python setup.py install'. Under
13 Windows, double-click on the provided .exe binary installer.
13 Windows, double-click on the provided .exe binary installer.
14
14
15 Then, take a look at Customization_ section for configuring things
15 Then, take a look at Customization_ section for configuring things
16 optimally and `Quick tips`_ for quick tips on efficient use of
16 optimally and `Quick tips`_ for quick tips on efficient use of
17 IPython. You can later refer to the rest of the manual for all the
17 IPython. You can later refer to the rest of the manual for all the
18 gory details.
18 gory details.
19
19
20 See the notes in upgrading_ section for upgrading IPython versions.
20 See the notes in upgrading_ section for upgrading IPython versions.
21
21
22
22
23 Detailed Unix instructions (Linux, Mac OS X, etc.)
23 Detailed Unix instructions (Linux, Mac OS X, etc.)
24
24
25 For RPM based systems, simply install the supplied package in the usual
25 For RPM based systems, simply install the supplied package in the usual
26 manner. If you download the tar archive, the process is:
26 manner. If you download the tar archive, the process is:
27
27
28 1. Unzip/untar the ipython-XXX.tar.gz file wherever you want (XXX is
28 1. Unzip/untar the ipython-XXX.tar.gz file wherever you want (XXX is
29 the version number). It will make a directory called ipython-XXX.
29 the version number). It will make a directory called ipython-XXX.
30 Change into that directory where you will find the files README
30 Change into that directory where you will find the files README
31 and setup.py. Once you've completed the installation, you can
31 and setup.py. Once you've completed the installation, you can
32 safely remove this directory.
32 safely remove this directory.
33 2. If you are installing over a previous installation of version
33 2. If you are installing over a previous installation of version
34 0.2.0 or earlier, first remove your $HOME/.ipython directory,
34 0.2.0 or earlier, first remove your $HOME/.ipython directory,
35 since the configuration file format has changed somewhat (the '='
35 since the configuration file format has changed somewhat (the '='
36 were removed from all option specifications). Or you can call
36 were removed from all option specifications). Or you can call
37 ipython with the -upgrade option and it will do this automatically
37 ipython with the -upgrade option and it will do this automatically
38 for you.
38 for you.
39 3. IPython uses distutils, so you can install it by simply typing at
39 3. IPython uses distutils, so you can install it by simply typing at
40 the system prompt (don't type the $)::
40 the system prompt (don't type the $)::
41
41
42 $ python setup.py install
42 $ python setup.py install
43
43
44 Note that this assumes you have root access to your machine. If
44 Note that this assumes you have root access to your machine. If
45 you don't have root access or don't want IPython to go in the
45 you don't have root access or don't want IPython to go in the
46 default python directories, you'll need to use the ``--home`` option
46 default python directories, you'll need to use the ``--home`` option
47 (or ``--prefix``). For example::
47 (or ``--prefix``). For example::
48
48
49 $ python setup.py install --home $HOME/local
49 $ python setup.py install --home $HOME/local
50
50
51 will install IPython into $HOME/local and its subdirectories
51 will install IPython into $HOME/local and its subdirectories
52 (creating them if necessary).
52 (creating them if necessary).
53 You can type::
53 You can type::
54
54
55 $ python setup.py --help
55 $ python setup.py --help
56
56
57 for more details.
57 for more details.
58
58
59 Note that if you change the default location for ``--home`` at
59 Note that if you change the default location for ``--home`` at
60 installation, IPython may end up installed at a location which is
60 installation, IPython may end up installed at a location which is
61 not part of your $PYTHONPATH environment variable. In this case,
61 not part of your $PYTHONPATH environment variable. In this case,
62 you'll need to configure this variable to include the actual
62 you'll need to configure this variable to include the actual
63 directory where the IPython/ directory ended (typically the value
63 directory where the IPython/ directory ended (typically the value
64 you give to ``--home`` plus /lib/python).
64 you give to ``--home`` plus /lib/python).
65
65
66
66
67 Mac OSX information
67 Mac OSX information
68 -------------------
68 -------------------
69
69
70 Under OSX, there is a choice you need to make. Apple ships its own build
70 Under OSX, there is a choice you need to make. Apple ships its own build
71 of Python, which lives in the core OSX filesystem hierarchy. You can
71 of Python, which lives in the core OSX filesystem hierarchy (/System/Library/Frameworks/Python.framework). You can
72 also manually install a separate Python, either purely by hand
72 also manually install a separate Python, either purely by hand
73 (typically in /usr/local) or by using Fink, which puts everything under
73 (typically in /usr/local), by using Fink or DarwinPorts, which put everything under /sw or /opt respectively, or using the python.org "Framework" python, which installs a framework similar to the system python in /Library/Frameworks. The Enthought Python Distribution (http://www.enthought.com/products/epd.php), uses the python.org "Framework" python and thus also installs into /Library/Frameworks. Which route to follow is a matter of personal preference, as I've seen users who favor each of the approaches.
74 /sw. Which route to follow is a matter of personal preference, as I've
74
75 seen users who favor each of the approaches. Here I will simply list the
75 For users of OS X 10.5 (Leopard), the system python installation contains support for `DTrace`_, a kernel-level profiling system integrated into OS X 10.5. This facility provides significant advantage to developers and users interested in high-performance computing by using the system python.
76 known installation issues under OSX, along with their solutions.
76
77 .. _DTrace: http://www.apple.com/macosx/technology/unix.html
78
79 IPython is known to work with all the above installation options. As such, we do not endorse one choice over the others. Here we will simply list the known installation issues under OSX, along with their solutions.
77
80
78 This page: http://geosci.uchicago.edu/~tobis/pylab.html contains
81 This page: http://geosci.uchicago.edu/~tobis/pylab.html contains
79 information on this topic, with additional details on how to make
82 information on this topic, with additional details on how to make
80 IPython and matplotlib play nicely under OSX.
83 IPython and matplotlib play nicely under OSX.
81
84
82 To run IPython and readline on OSX "Leopard" system python, see the
83 wiki page at http://ipython.scipy.org/moin/InstallationOSXLeopard
84
85
85
86 GUI problems
86 GUI problems (older versions of OS X)
87 ------------
87 -------------------------------------
88
88
89 The following instructions apply to an install of IPython under OSX from
89 The following instructions apply to an install of IPython under OSX before OS X 10.5 (users of OS X 10.5 see [#]_ ) by unpacking the .tar.gz distribution and installing it for the default Python interpreter shipped by Apple. If you are using a fink or DarwinPorts install, they will take care of these details for you, by installing IPython against their Python.
90 unpacking the .tar.gz distribution and installing it for the default
91 Python interpreter shipped by Apple. If you are using a fink install,
92 fink will take care of these details for you, by installing IPython
93 against fink's Python.
94
90
95 IPython offers various forms of support for interacting with graphical
91 IPython offers various forms of support for interacting with graphical
96 applications from the command line, from simple Tk apps (which are in
92 applications from the command line, from simple Tk apps (which are in
97 principle always supported by Python) to interactive control of WX, Qt
93 principle always supported by Python) to interactive control of WX, Qt
98 and GTK apps. Under OSX, however, this requires that ipython is
94 and GTK apps. Under OSX, however, this requires that ipython is
99 installed by calling the special pythonw script at installation time,
95 installed by calling the special pythonw script at installation time,
100 which takes care of coordinating things with Apple's graphical environment.
96 which takes care of coordinating things with Apple's graphical environment.
101
97
102 So when installing under OSX, it is best to use the following command::
98 So when installing under OSX, it is best to use the following command::
103
99
104 $ sudo pythonw setup.py install --install-scripts=/usr/local/bin
100 $ sudo pythonw setup.py install --install-scripts=/usr/local/bin
105
101
106 or
102 or::
107
103
108 $ sudo pythonw setup.py install --install-scripts=/usr/bin
104 $ sudo pythonw setup.py install --install-scripts=/usr/bin
109
105
110 depending on where you like to keep hand-installed executables.
106 depending on where you like to keep hand-installed executables.
111
107
112 The resulting script will have an appropriate shebang line (the first
108 The resulting script will have an appropriate shebang line (the first
113 line in the script whic begins with #!...) such that the ipython
109 line in the script whic begins with #!...) such that the ipython
114 interpreter can interact with the OS X GUI. If the installed version
110 interpreter can interact with the OS X GUI. If the installed version
115 does not work and has a shebang line that points to, for example, just
111 does not work and has a shebang line that points to, for example, just
116 /usr/bin/python, then you might have a stale, cached version in your
112 /usr/bin/python, then you might have a stale, cached version in your
117 build/scripts-<python-version> directory. Delete that directory and
113 build/scripts-<python-version> directory. Delete that directory and
118 rerun the setup.py.
114 rerun the setup.py.
119
115
120 It is also a good idea to use the special flag ``--install-scripts`` as
116 It is also a good idea to use the special flag ``--install-scripts`` as
121 indicated above, to ensure that the ipython scripts end up in a location
117 indicated above, to ensure that the ipython scripts end up in a location
122 which is part of your $PATH. Otherwise Apple's Python will put the
118 which is part of your $PATH. Otherwise Apple's Python will put the
123 scripts in an internal directory not available by default at the command
119 scripts in an internal directory not available by default at the command
124 line (if you use /usr/local/bin, you need to make sure this is in your
120 line (if you use /usr/local/bin, you need to make sure this is in your
125 $PATH, which may not be true by default).
121 $PATH, which may not be true by default).
126
122
123 .. [#] Users of OS X 10.5 who choose to use the system-installed python should install IPython using ``sudo python setupegg.py install`` from the IPython source directory or from `PyPI`_ using ``sudo easy_install ipython``.
124
125 .. _PyPI: http://pypi.python.org/pypi
127
126
128 Readline problems
127 Readline problems
129 -----------------
128 -----------------
130
129
131 By default, the Python version shipped by Apple does not include the
130 By default, the Python version shipped by Apple before OS X 10.5 does not include the readline library, so central to IPython's behavior. If you install
132 readline library, so central to IPython's behavior. If you install
133 IPython against Apple's Python, you will not have arrow keys, tab
131 IPython against Apple's Python, you will not have arrow keys, tab
134 completion, etc. For Mac OSX 10.3 (Panther), you can find a prebuilt
132 completion, etc. For Mac OSX 10.3 (Panther), you can find a prebuilt
135 readline library here:
133 readline library here:
136 http://pythonmac.org/packages/readline-5.0-py2.3-macosx10.3.zip
134 http://pythonmac.org/packages/readline-5.0-py2.3-macosx10.3.zip
137
135
138 If you are using OSX 10.4 (Tiger), after installing this package you
136 If you are using OSX 10.4 (Tiger), after installing this package you
139 need to either:
137 need to either:
140
138
141 1. move readline.so from /Library/Python/2.3 to
139 1. move readline.so from /Library/Python/2.3 to
142 /Library/Python/2.3/site-packages, or
140 /Library/Python/2.3/site-packages, or
143 2. install http://pythonmac.org/packages/TigerPython23Compat.pkg.zip
141 2. install http://pythonmac.org/packages/TigerPython23Compat.pkg.zip
144
142
145 Users installing against Fink's Python or a properly hand-built one
146 should not have this problem.
147
143
144 Beginning with OS X 10.5, Apple's python installation uses libedit, a BSD-licensed not-quite-compatible readline replacement. As of IPython 0.9, many of the issues related to the differences between readline and libedit have been resolved. If you find that you are experiencing readline-related issues (e.g. problems with tab-completion, history movement, or line editing), you can install Ludwig Schwartz's readline package which effectively replaces libedit with readline for packages installed via setuptools. If you installed IPython from the source directory using::
145
146 sudo python setupegg.py
147
148 or from PyPI with::
149
150 sudo easy_install ipython
151
152 then you can install the readline egg via [#]_::
153
154 sudo easy_install readline
155
156 If needed, the readline egg can be build and installed from source (see the
157 wiki page at http://ipython.scipy.org/moin/InstallationOSXLeopard).
158
159 Users installing against Fink or DarwinPorts's Python or a properly hand-built python installation should not have this problem.
160
161 .. [#] If you have installed SVN 1.5, you will also to install a patch to setuptools before installing the readline egg. Use ``sudo easy_install http://www.jaraco.com/ASP/eggs/setuptools-0.6c8_svn15fix.egg``.
148
162
149 DarwinPorts
163 DarwinPorts
150 -----------
164 -----------
151
165
152 I report here a message from an OSX user, who suggests an alternative
166 I report here a message from an OSX user, who suggests an alternative
153 means of using IPython under this operating system with good results.
167 means of using IPython under this operating system with good results.
154 Please let me know of any updates that may be useful for this section.
168 Please let me know of any updates that may be useful for this section.
155 His message is reproduced verbatim below:
169 His message is reproduced verbatim below:
156
170
157 From: Markus Banfi <markus.banfi-AT-mospheira.net>
171 From: Markus Banfi <markus.banfi-AT-mospheira.net>
158
172
159 As a MacOS X (10.4.2) user I prefer to install software using
173 As a MacOS X (10.4.2) user I prefer to install software using
160 DawinPorts instead of Fink. I had no problems installing ipython
174 DawinPorts instead of Fink. I had no problems installing ipython
161 with DarwinPorts. It's just:
175 with DarwinPorts. It's just:
162
176
163 sudo port install py-ipython
177 sudo port install py-ipython
164
178
165 It automatically resolved all dependencies (python24, readline,
179 It automatically resolved all dependencies (python24, readline,
166 py-readline). So far I did not encounter any problems with the
180 py-readline). So far I did not encounter any problems with the
167 DarwinPorts port of ipython.
181 DarwinPorts port of ipython.
168
182
169
183
170
184
171 Windows instructions
185 Windows instructions
172 --------------------
186 --------------------
173
187
174 Some of IPython's very useful features are:
188 Some of IPython's very useful features are:
175
189
176 * Integrated readline support (Tab-based file, object and attribute
190 * Integrated readline support (Tab-based file, object and attribute
177 completion, input history across sessions, editable command line,
191 completion, input history across sessions, editable command line,
178 etc.)
192 etc.)
179 * Coloring of prompts, code and tracebacks.
193 * Coloring of prompts, code and tracebacks.
180
194
181 .. _pyreadline:
195 .. _pyreadline:
182
196
183 These, by default, are only available under Unix-like operating systems.
197 These, by default, are only available under Unix-like operating systems.
184 However, thanks to Gary Bishop's work, Windows XP/2k users can also
198 However, thanks to Gary Bishop's work, Windows XP/2k users can also
185 benefit from them. His readline library originally implemented both GNU
199 benefit from them. His readline library originally implemented both GNU
186 readline functionality and color support, so that IPython under Windows
200 readline functionality and color support, so that IPython under Windows
187 XP/2k can be as friendly and powerful as under Unix-like environments.
201 XP/2k can be as friendly and powerful as under Unix-like environments.
188
202
189 This library, now named PyReadline, has been absorbed by the IPython
203 This library, now named PyReadline, has been absorbed by the IPython
190 team (Jörgen Stenarson, in particular), and it continues to be developed
204 team (Jörgen Stenarson, in particular), and it continues to be developed
191 with new features, as well as being distributed directly from the
205 with new features, as well as being distributed directly from the
192 IPython site.
206 IPython site.
193
207
194 The PyReadline extension requires CTypes and the windows IPython
208 The PyReadline extension requires CTypes and the windows IPython
195 installer needs PyWin32, so in all you need:
209 installer needs PyWin32, so in all you need:
196
210
197 1. PyWin32 from http://sourceforge.net/projects/pywin32.
211 1. PyWin32 from http://sourceforge.net/projects/pywin32.
198 2. PyReadline for Windows from
212 2. PyReadline for Windows from
199 http://ipython.scipy.org/moin/PyReadline/Intro. That page contains
213 http://ipython.scipy.org/moin/PyReadline/Intro. That page contains
200 further details on using and configuring the system to your liking.
214 further details on using and configuring the system to your liking.
201 3. Finally, only if you are using Python 2.3 or 2.4, you need CTypes
215 3. Finally, only if you are using Python 2.3 or 2.4, you need CTypes
202 from http://starship.python.net/crew/theller/ctypes(you must use
216 from http://starship.python.net/crew/theller/ctypes(you must use
203 version 0.9.1 or newer). This package is included in Python 2.5,
217 version 0.9.1 or newer). This package is included in Python 2.5,
204 so you don't need to manually get it if your Python version is 2.5
218 so you don't need to manually get it if your Python version is 2.5
205 or newer.
219 or newer.
206
220
207 Warning about a broken readline-like library: several users have
221 Warning about a broken readline-like library: several users have
208 reported problems stemming from using the pseudo-readline library at
222 reported problems stemming from using the pseudo-readline library at
209 http://newcenturycomputers.net/projects/readline.html. This is a broken
223 http://newcenturycomputers.net/projects/readline.html. This is a broken
210 library which, while called readline, only implements an incomplete
224 library which, while called readline, only implements an incomplete
211 subset of the readline API. Since it is still called readline, it fools
225 subset of the readline API. Since it is still called readline, it fools
212 IPython's detection mechanisms and causes unpredictable crashes later.
226 IPython's detection mechanisms and causes unpredictable crashes later.
213 If you wish to use IPython under Windows, you must NOT use this library,
227 If you wish to use IPython under Windows, you must NOT use this library,
214 which for all purposes is (at least as of version 1.6) terminally broken.
228 which for all purposes is (at least as of version 1.6) terminally broken.
215
229
216
230
217 Installation procedure
231 Installation procedure
218 ----------------------
232 ----------------------
219
233
220 Once you have the above installed, from the IPython download directory
234 Once you have the above installed, from the IPython download directory
221 grab the ipython-XXX.win32.exe file, where XXX represents the version
235 grab the ipython-XXX.win32.exe file, where XXX represents the version
222 number. This is a regular windows executable installer, which you can
236 number. This is a regular windows executable installer, which you can
223 simply double-click to install. It will add an entry for IPython to your
237 simply double-click to install. It will add an entry for IPython to your
224 Start Menu, as well as registering IPython in the Windows list of
238 Start Menu, as well as registering IPython in the Windows list of
225 applications, so you can later uninstall it from the Control Panel.
239 applications, so you can later uninstall it from the Control Panel.
226
240
227 IPython tries to install the configuration information in a directory
241 IPython tries to install the configuration information in a directory
228 named .ipython (_ipython under Windows) located in your 'home'
242 named .ipython (_ipython under Windows) located in your 'home'
229 directory. IPython sets this directory by looking for a HOME environment
243 directory. IPython sets this directory by looking for a HOME environment
230 variable; if such a variable does not exist, it uses HOMEDRIVE\HOMEPATH
244 variable; if such a variable does not exist, it uses HOMEDRIVE\HOMEPATH
231 (these are always defined by Windows). This typically gives something
245 (these are always defined by Windows). This typically gives something
232 like C:\Documents and Settings\YourUserName, but your local details may
246 like C:\Documents and Settings\YourUserName, but your local details may
233 vary. In this directory you will find all the files that configure
247 vary. In this directory you will find all the files that configure
234 IPython's defaults, and you can put there your profiles and extensions.
248 IPython's defaults, and you can put there your profiles and extensions.
235 This directory is automatically added by IPython to sys.path, so
249 This directory is automatically added by IPython to sys.path, so
236 anything you place there can be found by import statements.
250 anything you place there can be found by import statements.
237
251
238
252
239 Upgrading
253 Upgrading
240 ---------
254 ---------
241
255
242 For an IPython upgrade, you should first uninstall the previous version.
256 For an IPython upgrade, you should first uninstall the previous version.
243 This will ensure that all files and directories (such as the
257 This will ensure that all files and directories (such as the
244 documentation) which carry embedded version strings in their names are
258 documentation) which carry embedded version strings in their names are
245 properly removed.
259 properly removed.
246
260
247
261
248 Manual installation under Win32
262 Manual installation under Win32
249 -------------------------------
263 -------------------------------
250
264
251 In case the automatic installer does not work for some reason, you can
265 In case the automatic installer does not work for some reason, you can
252 download the ipython-XXX.tar.gz file, which contains the full IPython
266 download the ipython-XXX.tar.gz file, which contains the full IPython
253 source distribution (the popular WinZip can read .tar.gz files). After
267 source distribution (the popular WinZip can read .tar.gz files). After
254 uncompressing the archive, you can install it at a command terminal just
268 uncompressing the archive, you can install it at a command terminal just
255 like any other Python module, by using 'python setup.py install'.
269 like any other Python module, by using 'python setup.py install'.
256
270
257 After the installation, run the supplied win32_manual_post_install.py
271 After the installation, run the supplied win32_manual_post_install.py
258 script, which creates the necessary Start Menu shortcuts for you.
272 script, which creates the necessary Start Menu shortcuts for you.
259
273
260
274
261 .. upgrading:
275 .. upgrading:
262
276
263 Upgrading from a previous version
277 Upgrading from a previous version
264 ---------------------------------
278 ---------------------------------
265
279
266 If you are upgrading from a previous version of IPython, you may want
280 If you are upgrading from a previous version of IPython, you may want
267 to upgrade the contents of your ~/.ipython directory. Just run
281 to upgrade the contents of your ~/.ipython directory. Just run
268 %upgrade, look at the diffs and delete the suggested files manually,
282 %upgrade, look at the diffs and delete the suggested files manually,
269 if you think you can lose the old versions. %upgrade will never
283 if you think you can lose the old versions. %upgrade will never
270 overwrite or delete anything.
284 overwrite or delete anything.
271
285
272
286
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now