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 |
|
|
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 |
|
|
45 | IEngineCoreTestCase, | |
46 |
|
|
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 |
|
|
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 |
|
|
64 | self.engine_service = es.EngineService() | |
65 | self.engine_tub = Tub() |
|
65 | self.engine_service.startService() | |
66 |
|
|
66 | self.engine_tub = Tub() | |
67 | engine_connector = EngineConnector(self.engine_tub) |
|
67 | self.engine_tub.startService() | |
68 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
77 | def tearDown(self): | |
62 |
|
|
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 |
|
|
38 | def tearDown(self): | |
31 |
|
|
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 |
|
|
52 | def tearDown(self): | |
45 |
|
|
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 |
|
|
36 | def _raise_it(f): | |
37 |
|
|
37 | try: | |
38 |
|
|
38 | f.raiseException() | |
39 |
|
|
39 | except CompositeError, e: | |
40 |
|
|
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 |
|
|
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 |
|
|
74 | # Shut down the engines | |
78 | d = self.controller_tub.stopService() |
|
75 | for e in self.engines: | |
79 |
|
|
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 |
|
|
83 | def test_mapper(self): | |
84 |
|
|
84 | self.addEngine(4) | |
85 |
|
|
85 | m = self.multiengine.mapper() | |
86 |
|
|
86 | self.assertEquals(m.multiengine,self.multiengine) | |
87 |
|
|
87 | self.assertEquals(m.dist,'b') | |
88 |
|
|
88 | self.assertEquals(m.targets,'all') | |
89 |
|
|
89 | self.assertEquals(m.block,True) | |
90 |
|
|
90 | ||
91 |
|
|
91 | def test_map_default(self): | |
92 |
|
|
92 | self.addEngine(4) | |
93 |
|
|
93 | m = self.multiengine.mapper() | |
94 |
|
|
94 | d = m.map(lambda x: 2*x, range(10)) | |
95 |
|
|
95 | d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)])) | |
96 |
|
|
96 | d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10))) | |
97 |
|
|
97 | d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)])) | |
98 |
|
|
98 | return d | |
99 |
|
|
99 | ||
100 |
|
|
100 | def test_map_noblock(self): | |
101 |
|
|
101 | self.addEngine(4) | |
102 |
|
|
102 | m = self.multiengine.mapper(block=False) | |
103 |
|
|
103 | d = m.map(lambda x: 2*x, range(10)) | |
104 |
|
|
104 | d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True)) | |
105 |
|
|
105 | d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)])) | |
106 |
|
|
106 | return d | |
107 |
|
|
107 | ||
108 |
|
|
108 | def test_mapper_fail(self): | |
109 |
|
|
109 | self.addEngine(4) | |
110 |
|
|
110 | m = self.multiengine.mapper() | |
111 |
|
|
111 | d = m.map(lambda x: 1/0, range(10)) | |
112 |
|
|
112 | d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f)) | |
113 |
|
|
113 | return d | |
114 |
|
|
114 | ||
115 |
|
|
115 | def test_parallel(self): | |
116 |
|
|
116 | self.addEngine(4) | |
117 |
|
|
117 | p = self.multiengine.parallel() | |
118 |
|
|
118 | self.assert_(isinstance(p, ParallelFunction)) | |
119 |
|
|
119 | @p | |
120 |
|
|
120 | def f(x): return 2*x | |
121 |
|
|
121 | d = f(range(10)) | |
122 |
|
|
122 | d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)])) | |
123 |
|
|
123 | return d | |
124 |
|
|
124 | ||
125 |
|
|
125 | def test_parallel_noblock(self): | |
126 |
|
|
126 | self.addEngine(1) | |
127 |
|
|
127 | p = self.multiengine.parallel(block=False) | |
128 |
|
|
128 | self.assert_(isinstance(p, ParallelFunction)) | |
129 |
|
|
129 | @p | |
130 |
|
|
130 | def f(x): return 2*x | |
131 |
|
|
131 | d = f(range(10)) | |
132 |
|
|
132 | d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True)) | |
133 |
|
|
133 | d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)])) | |
134 |
|
|
134 | return d | |
135 |
|
|
135 | ||
136 |
|
|
136 | def test_parallel_fail(self): | |
137 |
|
|
137 | self.addEngine(4) | |
138 |
|
|
138 | p = self.multiengine.parallel() | |
139 |
|
|
139 | self.assert_(isinstance(p, ParallelFunction)) | |
140 |
|
|
140 | @p | |
141 |
|
|
141 | def f(x): return 1/0 | |
142 |
|
|
142 | d = f(range(10)) | |
143 |
|
|
143 | d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f)) | |
144 |
|
|
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 |
|
|
48 | us = UnSerialized({'a':10, 'b':range(10)}) | |
48 |
|
|
49 | s = ISerialized(us) | |
49 |
|
|
50 | uss = IUnSerialized(s) | |
50 |
|
|
51 | self.assert_(ISerialized.providedBy(s)) | |
51 |
|
|
52 | self.assert_(IUnSerialized.providedBy(us)) | |
52 |
|
|
53 | self.assert_(IUnSerialized.providedBy(uss)) | |
53 |
|
|
54 | for m in list(ISerialized): | |
54 |
|
|
55 | self.assert_(hasattr(s, m)) | |
55 |
|
|
56 | for m in list(IUnSerialized): | |
56 |
|
|
57 | self.assert_(hasattr(us, m)) | |
57 |
|
|
58 | for m in list(IUnSerialized): | |
58 |
|
|
59 | self.assert_(hasattr(uss, m)) | |
59 |
|
60 | |||
60 |
|
|
61 | def testPickleSerialized(self): | |
61 |
|
|
62 | obj = {'a':1.45345, 'b':'asdfsdf', 'c':10000L} | |
62 |
|
|
63 | original = UnSerialized(obj) | |
63 |
|
|
64 | originalSer = ISerialized(original) | |
64 |
|
|
65 | firstData = originalSer.getData() | |
65 |
|
|
66 | firstTD = originalSer.getTypeDescriptor() | |
66 |
|
|
67 | firstMD = originalSer.getMetadata() | |
67 |
|
|
68 | self.assert_(firstTD == 'pickle') | |
68 |
|
|
69 | self.assert_(firstMD == {}) | |
69 |
|
|
70 | unSerialized = IUnSerialized(originalSer) | |
70 |
|
|
71 | secondObj = unSerialized.getObject() | |
71 |
|
|
72 | for k, v in secondObj.iteritems(): | |
72 |
|
|
73 | self.assert_(obj[k] == v) | |
73 |
|
|
74 | secondSer = ISerialized(UnSerialized(secondObj)) | |
74 |
|
|
75 | self.assert_(firstData == secondSer.getData()) | |
75 |
|
|
76 | self.assert_(firstTD == secondSer.getTypeDescriptor() ) | |
76 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
149 | def test_noresult_noblock(self): | |
176 |
|
|
150 | d = defer.Deferred() | |
177 |
|
|
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 |
|
|
46 | def tearDown(self): | |
38 |
|
|
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 |
|
|
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 |
|
|
74 | d = self.client_tub.get_multiengine_client(mec_furl) | |
76 |
|
|
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 |
|
|
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 |
|
|
96 | This is the core functionality of IPython that is independent of the | |
97 |
|
|
97 | terminal, network and GUIs. Most of the code that is in the current | |
98 |
|
|
98 | IPython trunk will be refactored, cleaned up and moved here. | |
99 |
|
99 | |||
100 | ``kernel`` |
|
100 | ``kernel`` | |
101 |
|
|
101 | The enables the IPython core to be expose to a the network. This is | |
102 |
|
|
102 | also where all of the parallel computing capabilities are to be found. | |
103 |
|
103 | |||
104 | ``config`` |
|
104 | ``config`` | |
105 |
|
|
105 | The configuration package used by IPython. | |
106 |
|
106 | |||
107 | ``frontends`` |
|
107 | ``frontends`` | |
108 |
|
|
108 | The various frontends for IPython. A frontend is the end-user application | |
109 |
|
|
109 | that exposes the capabilities of IPython to the user. The most basic frontend | |
110 |
|
|
110 | will simply be a terminal based application that looks just like today 's | |
111 |
|
|
111 | IPython. Other frontends will likely be more powerful and based on GUI toolkits. | |
112 |
|
112 | |||
113 | ``notebook`` |
|
113 | ``notebook`` | |
114 |
|
|
114 | An application that allows users to work with IPython notebooks. | |
115 |
|
115 | |||
116 | ``tools`` |
|
116 | ``tools`` | |
117 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
137 | $ ...do work in ipython-mybranch... | |
138 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
174 | $ cd ipython | |
175 |
|
|
175 | $ merge ../ipython-mybranch | |
176 |
|
|
176 | [resolve any conflicts] | |
177 |
|
|
177 | $ bzr ci -m "Fixing that bug" | |
178 |
|
|
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