##// END OF EJS Templates
Partial fixes for 2.4 compatibility. Unfinished....
Fernando Perez -
Show More
@@ -1,97 +1,97 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Release data for the IPython project."""
2 """Release data for the IPython project."""
3
3
4 #*****************************************************************************
4 #*****************************************************************************
5 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
5 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
6 #
6 #
7 # Copyright (c) 2001 Janko Hauser <jhauser@zscout.de> and Nathaniel Gray
7 # Copyright (c) 2001 Janko Hauser <jhauser@zscout.de> and Nathaniel Gray
8 # <n8gray@caltech.edu>
8 # <n8gray@caltech.edu>
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 # Name of the package for release purposes. This is the name which labels
14 # Name of the package for release purposes. This is the name which labels
15 # the tarballs and RPMs made by distutils, so it's best to lowercase it.
15 # the tarballs and RPMs made by distutils, so it's best to lowercase it.
16 name = 'ipython'
16 name = 'ipython'
17
17
18 # For versions with substrings (like 0.6.16.svn), use an extra . to separate
18 # For versions with substrings (like 0.6.16.svn), use an extra . to separate
19 # the new substring. We have to avoid using either dashes or underscores,
19 # the new substring. We have to avoid using either dashes or underscores,
20 # because bdist_rpm does not accept dashes (an RPM) convention, and
20 # because bdist_rpm does not accept dashes (an RPM) convention, and
21 # bdist_deb does not accept underscores (a Debian convention).
21 # bdist_deb does not accept underscores (a Debian convention).
22
22
23 development = False # change this to False to do a release
23 development = False # change this to False to do a release
24 version_base = '0.9'
24 version_base = '0.9.1'
25 branch = 'ipython'
25 branch = 'ipython'
26 revision = '1143'
26 revision = '1143'
27
27
28 if development:
28 if development:
29 if branch == 'ipython':
29 if branch == 'ipython':
30 version = '%s.bzr.r%s' % (version_base, revision)
30 version = '%s.bzr.r%s' % (version_base, revision)
31 else:
31 else:
32 version = '%s.bzr.r%s.%s' % (version_base, revision, branch)
32 version = '%s.bzr.r%s.%s' % (version_base, revision, branch)
33 else:
33 else:
34 version = version_base
34 version = version_base
35
35
36
36
37 description = "Tools for interactive development in Python."
37 description = "Tools for interactive development in Python."
38
38
39 long_description = \
39 long_description = \
40 """
40 """
41 IPython provides a replacement for the interactive Python interpreter with
41 IPython provides a replacement for the interactive Python interpreter with
42 extra functionality.
42 extra functionality.
43
43
44 Main features:
44 Main features:
45
45
46 * Comprehensive object introspection.
46 * Comprehensive object introspection.
47
47
48 * Input history, persistent across sessions.
48 * Input history, persistent across sessions.
49
49
50 * Caching of output results during a session with automatically generated
50 * Caching of output results during a session with automatically generated
51 references.
51 references.
52
52
53 * Readline based name completion.
53 * Readline based name completion.
54
54
55 * Extensible system of 'magic' commands for controlling the environment and
55 * Extensible system of 'magic' commands for controlling the environment and
56 performing many tasks related either to IPython or the operating system.
56 performing many tasks related either to IPython or the operating system.
57
57
58 * Configuration system with easy switching between different setups (simpler
58 * Configuration system with easy switching between different setups (simpler
59 than changing $PYTHONSTARTUP environment variables every time).
59 than changing $PYTHONSTARTUP environment variables every time).
60
60
61 * Session logging and reloading.
61 * Session logging and reloading.
62
62
63 * Extensible syntax processing for special purpose situations.
63 * Extensible syntax processing for special purpose situations.
64
64
65 * Access to the system shell with user-extensible alias system.
65 * Access to the system shell with user-extensible alias system.
66
66
67 * Easily embeddable in other Python programs.
67 * Easily embeddable in other Python programs.
68
68
69 * Integrated access to the pdb debugger and the Python profiler.
69 * Integrated access to the pdb debugger and the Python profiler.
70
70
71 The latest development version is always available at the IPython subversion
71 The latest development version is always available at the IPython subversion
72 repository_.
72 repository_.
73
73
74 .. _repository: http://ipython.scipy.org/svn/ipython/ipython/trunk#egg=ipython-dev
74 .. _repository: http://ipython.scipy.org/svn/ipython/ipython/trunk#egg=ipython-dev
75 """
75 """
76
76
77 license = 'BSD'
77 license = 'BSD'
78
78
79 authors = {'Fernando' : ('Fernando Perez','fperez@colorado.edu'),
79 authors = {'Fernando' : ('Fernando Perez','fperez@colorado.edu'),
80 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
80 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
81 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
81 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
82 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
82 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
83 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
83 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
84 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com')
84 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com')
85 }
85 }
86
86
87 author = 'The IPython Development Team'
87 author = 'The IPython Development Team'
88
88
89 author_email = 'ipython-dev@scipy.org'
89 author_email = 'ipython-dev@scipy.org'
90
90
91 url = 'http://ipython.scipy.org'
91 url = 'http://ipython.scipy.org'
92
92
93 download_url = 'http://ipython.scipy.org/dist'
93 download_url = 'http://ipython.scipy.org/dist'
94
94
95 platforms = ['Linux','Mac OSX','Windows XP/2000/NT','Windows 95/98/ME']
95 platforms = ['Linux','Mac OSX','Windows XP/2000/NT','Windows 95/98/ME']
96
96
97 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed']
97 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed']
@@ -1,362 +1,431 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
25 import _ast
25 try:
26 import _ast
27 except ImportError:
28 # Python 2.4 hackish workaround.
29 class bunch: pass
30 _ast = bunch()
31 _ast.PyCF_ONLY_AST = 1024
32
33
34
35 try:
36 import uuid
37 except ImportError:
38 # Python 2.4 hackish workaround.
39 class UUID:
40 def __init__(self,bytes):
41 version = 4
42 int = long(('%02x'*16) % tuple(map(ord, bytes)), 16)
43 # Set the variant to RFC 4122.
44 int &= ~(0xc000 << 48L)
45 int |= 0x8000 << 48L
46 # Set the version number.
47 int &= ~(0xf000 << 64L)
48 int |= version << 76L
49 self.__dict__['int'] = int
50
51 def __cmp__(self, other):
52 if isinstance(other, UUID):
53 return cmp(self.int, other.int)
54 return NotImplemented
55
56 def __hash__(self):
57 return hash(self.int)
58
59 def __int__(self):
60 return self.int
61
62 def __repr__(self):
63 return 'UUID(%r)' % str(self)
64
65 def __setattr__(self, name, value):
66 raise TypeError('UUID objects are immutable')
67
68 def __str__(self):
69 hex = '%032x' % self.int
70 return '%s-%s-%s-%s-%s' % (
71 hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
72
73 def get_bytes(self):
74 bytes = ''
75 for shift in range(0, 128, 8):
76 bytes = chr((self.int >> shift) & 0xff) + bytes
77 return bytes
78
79 bytes = property(get_bytes)
80
81
82 def _u4():
83 "Fake random uuid"
84
85 import random
86 bytes = [chr(random.randrange(256)) for i in range(16)]
87 return UUID(bytes)
88
89 class bunch: pass
90 uuid = bunch()
91 uuid.uuid4 = _u4
92 del _u4
93
94
26
95
27 from IPython.frontend.zopeinterface import (
96 from IPython.frontend.zopeinterface import (
28 Interface,
97 Interface,
29 Attribute,
98 Attribute,
30 implements,
99 implements,
31 classProvides
100 classProvides
32 )
101 )
33 from IPython.kernel.core.history import FrontEndHistory
102 from IPython.kernel.core.history import FrontEndHistory
34 from IPython.kernel.core.util import Bunch
103 from IPython.kernel.core.util import Bunch
35
104
36 ##############################################################################
105 ##############################################################################
37 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
106 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
38 # not
107 # not
39
108
40 rc = Bunch()
109 rc = Bunch()
41 rc.prompt_in1 = r'In [$number]: '
110 rc.prompt_in1 = r'In [$number]: '
42 rc.prompt_in2 = r'...'
111 rc.prompt_in2 = r'...'
43 rc.prompt_out = r'Out [$number]: '
112 rc.prompt_out = r'Out [$number]: '
44
113
45 ##############################################################################
114 ##############################################################################
46 # Interface definitions
115 # Interface definitions
47 ##############################################################################
116 ##############################################################################
48
117
49 class IFrontEndFactory(Interface):
118 class IFrontEndFactory(Interface):
50 """Factory interface for frontends."""
119 """Factory interface for frontends."""
51
120
52 def __call__(engine=None, history=None):
121 def __call__(engine=None, history=None):
53 """
122 """
54 Parameters:
123 Parameters:
55 interpreter : IPython.kernel.engineservice.IEngineCore
124 interpreter : IPython.kernel.engineservice.IEngineCore
56 """
125 """
57
126
58 pass
127 pass
59
128
60
129
61 class IFrontEnd(Interface):
130 class IFrontEnd(Interface):
62 """Interface for frontends. All methods return t.i.d.Deferred"""
131 """Interface for frontends. All methods return t.i.d.Deferred"""
63
132
64 Attribute("input_prompt_template", "string.Template instance\
133 Attribute("input_prompt_template", "string.Template instance\
65 substituteable with execute result.")
134 substituteable with execute result.")
66 Attribute("output_prompt_template", "string.Template instance\
135 Attribute("output_prompt_template", "string.Template instance\
67 substituteable with execute result.")
136 substituteable with execute result.")
68 Attribute("continuation_prompt_template", "string.Template instance\
137 Attribute("continuation_prompt_template", "string.Template instance\
69 substituteable with execute result.")
138 substituteable with execute result.")
70
139
71 def update_cell_prompt(result, blockID=None):
140 def update_cell_prompt(result, blockID=None):
72 """Subclass may override to update the input prompt for a block.
141 """Subclass may override to update the input prompt for a block.
73
142
74 In asynchronous frontends, this method will be called as a
143 In asynchronous frontends, this method will be called as a
75 twisted.internet.defer.Deferred's callback/errback.
144 twisted.internet.defer.Deferred's callback/errback.
76 Implementations should thus return result when finished.
145 Implementations should thus return result when finished.
77
146
78 Result is a result dict in case of success, and a
147 Result is a result dict in case of success, and a
79 twisted.python.util.failure.Failure in case of an error
148 twisted.python.util.failure.Failure in case of an error
80 """
149 """
81
150
82 pass
151 pass
83
152
84 def render_result(result):
153 def render_result(result):
85 """Render the result of an execute call. Implementors may choose the
154 """Render the result of an execute call. Implementors may choose the
86 method of rendering.
155 method of rendering.
87 For example, a notebook-style frontend might render a Chaco plot
156 For example, a notebook-style frontend might render a Chaco plot
88 inline.
157 inline.
89
158
90 Parameters:
159 Parameters:
91 result : dict (result of IEngineBase.execute )
160 result : dict (result of IEngineBase.execute )
92 blockID = result['blockID']
161 blockID = result['blockID']
93
162
94 Result:
163 Result:
95 Output of frontend rendering
164 Output of frontend rendering
96 """
165 """
97
166
98 pass
167 pass
99
168
100 def render_error(failure):
169 def render_error(failure):
101 """Subclasses must override to render the failure.
170 """Subclasses must override to render the failure.
102
171
103 In asynchronous frontend, since this method will be called as a
172 In asynchronous frontend, since this method will be called as a
104 twisted.internet.defer.Deferred's callback. Implementations
173 twisted.internet.defer.Deferred's callback. Implementations
105 should thus return result when finished.
174 should thus return result when finished.
106
175
107 blockID = failure.blockID
176 blockID = failure.blockID
108 """
177 """
109
178
110 pass
179 pass
111
180
112 def input_prompt(number=''):
181 def input_prompt(number=''):
113 """Returns the input prompt by subsituting into
182 """Returns the input prompt by subsituting into
114 self.input_prompt_template
183 self.input_prompt_template
115 """
184 """
116 pass
185 pass
117
186
118 def output_prompt(number=''):
187 def output_prompt(number=''):
119 """Returns the output prompt by subsituting into
188 """Returns the output prompt by subsituting into
120 self.output_prompt_template
189 self.output_prompt_template
121 """
190 """
122
191
123 pass
192 pass
124
193
125 def continuation_prompt():
194 def continuation_prompt():
126 """Returns the continuation prompt by subsituting into
195 """Returns the continuation prompt by subsituting into
127 self.continuation_prompt_template
196 self.continuation_prompt_template
128 """
197 """
129
198
130 pass
199 pass
131
200
132 def is_complete(block):
201 def is_complete(block):
133 """Returns True if block is complete, False otherwise."""
202 """Returns True if block is complete, False otherwise."""
134
203
135 pass
204 pass
136
205
137 def compile_ast(block):
206 def compile_ast(block):
138 """Compiles block to an _ast.AST"""
207 """Compiles block to an _ast.AST"""
139
208
140 pass
209 pass
141
210
142 def get_history_previous(current_block):
211 def get_history_previous(current_block):
143 """Returns the block previous in the history. Saves currentBlock if
212 """Returns the block previous in the history. Saves currentBlock if
144 the history_cursor is currently at the end of the input history"""
213 the history_cursor is currently at the end of the input history"""
145 pass
214 pass
146
215
147 def get_history_next():
216 def get_history_next():
148 """Returns the next block in the history."""
217 """Returns the next block in the history."""
149
218
150 pass
219 pass
151
220
152 def complete(self, line):
221 def complete(self, line):
153 """Returns the list of possible completions, and the completed
222 """Returns the list of possible completions, and the completed
154 line.
223 line.
155
224
156 The input argument is the full line to be completed. This method
225 The input argument is the full line to be completed. This method
157 returns both the line completed as much as possible, and the list
226 returns both the line completed as much as possible, and the list
158 of further possible completions (full words).
227 of further possible completions (full words).
159 """
228 """
160 pass
229 pass
161
230
162
231
163 ##############################################################################
232 ##############################################################################
164 # Base class for all the frontends.
233 # Base class for all the frontends.
165 ##############################################################################
234 ##############################################################################
166
235
167 class FrontEndBase(object):
236 class FrontEndBase(object):
168 """
237 """
169 FrontEndBase manages the state tasks for a CLI frontend:
238 FrontEndBase manages the state tasks for a CLI frontend:
170 - Input and output history management
239 - Input and output history management
171 - Input/continuation and output prompt generation
240 - Input/continuation and output prompt generation
172
241
173 Some issues (due to possibly unavailable engine):
242 Some issues (due to possibly unavailable engine):
174 - How do we get the current cell number for the engine?
243 - How do we get the current cell number for the engine?
175 - How do we handle completions?
244 - How do we handle completions?
176 """
245 """
177
246
178 history_cursor = 0
247 history_cursor = 0
179
248
180 input_prompt_template = string.Template(rc.prompt_in1)
249 input_prompt_template = string.Template(rc.prompt_in1)
181 output_prompt_template = string.Template(rc.prompt_out)
250 output_prompt_template = string.Template(rc.prompt_out)
182 continuation_prompt_template = string.Template(rc.prompt_in2)
251 continuation_prompt_template = string.Template(rc.prompt_in2)
183
252
184 def __init__(self, shell=None, history=None):
253 def __init__(self, shell=None, history=None):
185 self.shell = shell
254 self.shell = shell
186 if history is None:
255 if history is None:
187 self.history = FrontEndHistory(input_cache=[''])
256 self.history = FrontEndHistory(input_cache=[''])
188 else:
257 else:
189 self.history = history
258 self.history = history
190
259
191
260
192 def input_prompt(self, number=''):
261 def input_prompt(self, number=''):
193 """Returns the current input prompt
262 """Returns the current input prompt
194
263
195 It would be great to use ipython1.core.prompts.Prompt1 here
264 It would be great to use ipython1.core.prompts.Prompt1 here
196 """
265 """
197 return self.input_prompt_template.safe_substitute({'number':number})
266 return self.input_prompt_template.safe_substitute({'number':number})
198
267
199
268
200 def continuation_prompt(self):
269 def continuation_prompt(self):
201 """Returns the current continuation prompt"""
270 """Returns the current continuation prompt"""
202
271
203 return self.continuation_prompt_template.safe_substitute()
272 return self.continuation_prompt_template.safe_substitute()
204
273
205 def output_prompt(self, number=''):
274 def output_prompt(self, number=''):
206 """Returns the output prompt for result"""
275 """Returns the output prompt for result"""
207
276
208 return self.output_prompt_template.safe_substitute({'number':number})
277 return self.output_prompt_template.safe_substitute({'number':number})
209
278
210
279
211 def is_complete(self, block):
280 def is_complete(self, block):
212 """Determine if block is complete.
281 """Determine if block is complete.
213
282
214 Parameters
283 Parameters
215 block : string
284 block : string
216
285
217 Result
286 Result
218 True if block can be sent to the engine without compile errors.
287 True if block can be sent to the engine without compile errors.
219 False otherwise.
288 False otherwise.
220 """
289 """
221
290
222 try:
291 try:
223 ast = self.compile_ast(block)
292 ast = self.compile_ast(block)
224 except:
293 except:
225 return False
294 return False
226
295
227 lines = block.split('\n')
296 lines = block.split('\n')
228 return (len(lines)==1 or str(lines[-1])=='')
297 return (len(lines)==1 or str(lines[-1])=='')
229
298
230
299
231 def compile_ast(self, block):
300 def compile_ast(self, block):
232 """Compile block to an AST
301 """Compile block to an AST
233
302
234 Parameters:
303 Parameters:
235 block : str
304 block : str
236
305
237 Result:
306 Result:
238 AST
307 AST
239
308
240 Throws:
309 Throws:
241 Exception if block cannot be compiled
310 Exception if block cannot be compiled
242 """
311 """
243
312
244 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
313 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
245
314
246
315
247 def execute(self, block, blockID=None):
316 def execute(self, block, blockID=None):
248 """Execute the block and return the result.
317 """Execute the block and return the result.
249
318
250 Parameters:
319 Parameters:
251 block : {str, AST}
320 block : {str, AST}
252 blockID : any
321 blockID : any
253 Caller may provide an ID to identify this block.
322 Caller may provide an ID to identify this block.
254 result['blockID'] := blockID
323 result['blockID'] := blockID
255
324
256 Result:
325 Result:
257 Deferred result of self.interpreter.execute
326 Deferred result of self.interpreter.execute
258 """
327 """
259
328
260 if(not self.is_complete(block)):
329 if(not self.is_complete(block)):
261 raise Exception("Block is not compilable")
330 raise Exception("Block is not compilable")
262
331
263 if(blockID == None):
332 if(blockID == None):
264 blockID = uuid.uuid4() #random UUID
333 blockID = uuid.uuid4() #random UUID
265
334
266 try:
335 try:
267 result = self.shell.execute(block)
336 result = self.shell.execute(block)
268 except Exception,e:
337 except Exception,e:
269 e = self._add_block_id_for_failure(e, blockID=blockID)
338 e = self._add_block_id_for_failure(e, blockID=blockID)
270 e = self.update_cell_prompt(e, blockID=blockID)
339 e = self.update_cell_prompt(e, blockID=blockID)
271 e = self.render_error(e)
340 e = self.render_error(e)
272 else:
341 else:
273 result = self._add_block_id_for_result(result, blockID=blockID)
342 result = self._add_block_id_for_result(result, blockID=blockID)
274 result = self.update_cell_prompt(result, blockID=blockID)
343 result = self.update_cell_prompt(result, blockID=blockID)
275 result = self.render_result(result)
344 result = self.render_result(result)
276
345
277 return result
346 return result
278
347
279
348
280 def _add_block_id_for_result(self, result, blockID):
349 def _add_block_id_for_result(self, result, blockID):
281 """Add the blockID to result or failure. Unfortunatley, we have to
350 """Add the blockID to result or failure. Unfortunatley, we have to
282 treat failures differently than result dicts.
351 treat failures differently than result dicts.
283 """
352 """
284
353
285 result['blockID'] = blockID
354 result['blockID'] = blockID
286
355
287 return result
356 return result
288
357
289 def _add_block_id_for_failure(self, failure, blockID):
358 def _add_block_id_for_failure(self, failure, blockID):
290 """_add_block_id_for_failure"""
359 """_add_block_id_for_failure"""
291 failure.blockID = blockID
360 failure.blockID = blockID
292 return failure
361 return failure
293
362
294
363
295 def _add_history(self, result, block=None):
364 def _add_history(self, result, block=None):
296 """Add block to the history"""
365 """Add block to the history"""
297
366
298 assert(block != None)
367 assert(block != None)
299 self.history.add_items([block])
368 self.history.add_items([block])
300 self.history_cursor += 1
369 self.history_cursor += 1
301
370
302 return result
371 return result
303
372
304
373
305 def get_history_previous(self, current_block):
374 def get_history_previous(self, current_block):
306 """ Returns previous history string and decrement history cursor.
375 """ Returns previous history string and decrement history cursor.
307 """
376 """
308 command = self.history.get_history_item(self.history_cursor - 1)
377 command = self.history.get_history_item(self.history_cursor - 1)
309
378
310 if command is not None:
379 if command is not None:
311 if(self.history_cursor+1 == len(self.history.input_cache)):
380 if(self.history_cursor+1 == len(self.history.input_cache)):
312 self.history.input_cache[self.history_cursor] = current_block
381 self.history.input_cache[self.history_cursor] = current_block
313 self.history_cursor -= 1
382 self.history_cursor -= 1
314 return command
383 return command
315
384
316
385
317 def get_history_next(self):
386 def get_history_next(self):
318 """ Returns next history string and increment history cursor.
387 """ Returns next history string and increment history cursor.
319 """
388 """
320 command = self.history.get_history_item(self.history_cursor+1)
389 command = self.history.get_history_item(self.history_cursor+1)
321
390
322 if command is not None:
391 if command is not None:
323 self.history_cursor += 1
392 self.history_cursor += 1
324 return command
393 return command
325
394
326 ###
395 ###
327 # Subclasses probably want to override these methods...
396 # Subclasses probably want to override these methods...
328 ###
397 ###
329
398
330 def update_cell_prompt(self, result, blockID=None):
399 def update_cell_prompt(self, result, blockID=None):
331 """Subclass may override to update the input prompt for a block.
400 """Subclass may override to update the input prompt for a block.
332
401
333 This method only really makes sens in asyncrhonous frontend.
402 This method only really makes sens in asyncrhonous frontend.
334 Since this method will be called as a
403 Since this method will be called as a
335 twisted.internet.defer.Deferred's callback, implementations should
404 twisted.internet.defer.Deferred's callback, implementations should
336 return result when finished.
405 return result when finished.
337 """
406 """
338
407
339 raise NotImplementedError
408 raise NotImplementedError
340
409
341
410
342 def render_result(self, result):
411 def render_result(self, result):
343 """Subclasses must override to render result.
412 """Subclasses must override to render result.
344
413
345 In asynchronous frontends, this method will be called as a
414 In asynchronous frontends, this method will be called as a
346 twisted.internet.defer.Deferred's callback. Implementations
415 twisted.internet.defer.Deferred's callback. Implementations
347 should thus return result when finished.
416 should thus return result when finished.
348 """
417 """
349
418
350 raise NotImplementedError
419 raise NotImplementedError
351
420
352
421
353 def render_error(self, failure):
422 def render_error(self, failure):
354 """Subclasses must override to render the failure.
423 """Subclasses must override to render the failure.
355
424
356 In asynchronous frontends, this method will be called as a
425 In asynchronous frontends, this method will be called as a
357 twisted.internet.defer.Deferred's callback. Implementations
426 twisted.internet.defer.Deferred's callback. Implementations
358 should thus return result when finished.
427 should thus return result when finished.
359 """
428 """
360
429
361 raise NotImplementedError
430 raise NotImplementedError
362
431
@@ -1,320 +1,333 b''
1 """
1 """
2 Base front end class for all line-oriented frontends, rather than
2 Base front end class for all line-oriented frontends, rather than
3 block-oriented.
3 block-oriented.
4
4
5 Currently this focuses on synchronous frontends.
5 Currently this focuses on synchronous frontends.
6 """
6 """
7 __docformat__ = "restructuredtext en"
7 __docformat__ = "restructuredtext en"
8
8
9 #-------------------------------------------------------------------------------
9 #-------------------------------------------------------------------------------
10 # Copyright (C) 2008 The IPython Development Team
10 # Copyright (C) 2008 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15
15
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 import re
19 import re
20
20
21 import IPython
21 import IPython
22 import sys
22 import sys
23 import codeop
23 import codeop
24 import traceback
24 import traceback
25
25
26 from frontendbase import FrontEndBase
26 from frontendbase import FrontEndBase
27 from IPython.kernel.core.interpreter import Interpreter
27 from IPython.kernel.core.interpreter import Interpreter
28
28
29 def common_prefix(strings):
29 def common_prefix(strings):
30 """ Given a list of strings, return the common prefix between all
30 """ Given a list of strings, return the common prefix between all
31 these strings.
31 these strings.
32 """
32 """
33 ref = strings[0]
33 ref = strings[0]
34 prefix = ''
34 prefix = ''
35 for size in range(len(ref)):
35 for size in range(len(ref)):
36 test_prefix = ref[:size+1]
36 test_prefix = ref[:size+1]
37 for string in strings[1:]:
37 for string in strings[1:]:
38 if not string.startswith(test_prefix):
38 if not string.startswith(test_prefix):
39 return prefix
39 return prefix
40 prefix = test_prefix
40 prefix = test_prefix
41
41
42 return prefix
42 return prefix
43
43
44 #-------------------------------------------------------------------------------
44 #-------------------------------------------------------------------------------
45 # Base class for the line-oriented front ends
45 # Base class for the line-oriented front ends
46 #-------------------------------------------------------------------------------
46 #-------------------------------------------------------------------------------
47 class LineFrontEndBase(FrontEndBase):
47 class LineFrontEndBase(FrontEndBase):
48 """ Concrete implementation of the FrontEndBase class. This is meant
48 """ Concrete implementation of the FrontEndBase class. This is meant
49 to be the base class behind all the frontend that are line-oriented,
49 to be the base class behind all the frontend that are line-oriented,
50 rather than block-oriented.
50 rather than block-oriented.
51 """
51 """
52
52
53 # We need to keep the prompt number, to be able to increment
53 # We need to keep the prompt number, to be able to increment
54 # it when there is an exception.
54 # it when there is an exception.
55 prompt_number = 1
55 prompt_number = 1
56
56
57 # We keep a reference to the last result: it helps testing and
57 # We keep a reference to the last result: it helps testing and
58 # programatic control of the frontend.
58 # programatic control of the frontend.
59 last_result = dict(number=0)
59 last_result = dict(number=0)
60
60
61 # The input buffer being edited
61 # The input buffer being edited
62 input_buffer = ''
62 input_buffer = ''
63
63
64 # Set to true for debug output
64 # Set to true for debug output
65 debug = False
65 debug = False
66
66
67 # A banner to print at startup
67 # A banner to print at startup
68 banner = None
68 banner = None
69
69
70 #--------------------------------------------------------------------------
70 #--------------------------------------------------------------------------
71 # FrontEndBase interface
71 # FrontEndBase interface
72 #--------------------------------------------------------------------------
72 #--------------------------------------------------------------------------
73
73
74 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
74 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
75 if shell is None:
75 if shell is None:
76 shell = Interpreter()
76 shell = Interpreter()
77 FrontEndBase.__init__(self, shell=shell, history=history)
77 FrontEndBase.__init__(self, shell=shell, history=history)
78
78
79 if banner is not None:
79 if banner is not None:
80 self.banner = banner
80 self.banner = banner
81
81
82 def start(self):
82 def start(self):
83 """ Put the frontend in a state where it is ready for user
83 """ Put the frontend in a state where it is ready for user
84 interaction.
84 interaction.
85 """
85 """
86 if self.banner is not None:
86 if self.banner is not None:
87 self.write(self.banner, refresh=False)
87 self.write(self.banner, refresh=False)
88
88
89 self.new_prompt(self.input_prompt_template.substitute(number=1))
89 self.new_prompt(self.input_prompt_template.substitute(number=1))
90
90
91
91
92 def complete(self, line):
92 def complete(self, line):
93 """Complete line in engine's user_ns
93 """Complete line in engine's user_ns
94
94
95 Parameters
95 Parameters
96 ----------
96 ----------
97 line : string
97 line : string
98
98
99 Result
99 Result
100 ------
100 ------
101 The replacement for the line and the list of possible completions.
101 The replacement for the line and the list of possible completions.
102 """
102 """
103 completions = self.shell.complete(line)
103 completions = self.shell.complete(line)
104 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
104 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
105 if completions:
105 if completions:
106 prefix = common_prefix(completions)
106 prefix = common_prefix(completions)
107 residual = complete_sep.split(line)[:-1]
107 residual = complete_sep.split(line)[:-1]
108 line = line[:-len(residual)] + prefix
108 line = line[:-len(residual)] + prefix
109 return line, completions
109 return line, completions
110
110
111
111
112 def render_result(self, result):
112 def render_result(self, result):
113 """ Frontend-specific rendering of the result of a calculation
113 """ Frontend-specific rendering of the result of a calculation
114 that has been sent to an engine.
114 that has been sent to an engine.
115 """
115 """
116 if 'stdout' in result and result['stdout']:
116 if 'stdout' in result and result['stdout']:
117 self.write('\n' + result['stdout'])
117 self.write('\n' + result['stdout'])
118 if 'display' in result and result['display']:
118 if 'display' in result and result['display']:
119 self.write("%s%s\n" % (
119 self.write("%s%s\n" % (
120 self.output_prompt_template.substitute(
120 self.output_prompt_template.substitute(
121 number=result['number']),
121 number=result['number']),
122 result['display']['pprint']
122 result['display']['pprint']
123 ) )
123 ) )
124
124
125
125
126 def render_error(self, failure):
126 def render_error(self, failure):
127 """ Frontend-specific rendering of error.
127 """ Frontend-specific rendering of error.
128 """
128 """
129 self.write('\n\n'+str(failure)+'\n\n')
129 self.write('\n\n'+str(failure)+'\n\n')
130 return failure
130 return failure
131
131
132
132
133 def is_complete(self, string):
133 def is_complete(self, string):
134 """ Check if a string forms a complete, executable set of
134 """ Check if a string forms a complete, executable set of
135 commands.
135 commands.
136
136
137 For the line-oriented frontend, multi-line code is not executed
137 For the line-oriented frontend, multi-line code is not executed
138 as soon as it is complete: the users has to enter two line
138 as soon as it is complete: the users has to enter two line
139 returns.
139 returns.
140 """
140 """
141 if string in ('', '\n'):
141 if string in ('', '\n'):
142 # Prefiltering, eg through ipython0, may return an empty
142 # Prefiltering, eg through ipython0, may return an empty
143 # string although some operations have been accomplished. We
143 # string although some operations have been accomplished. We
144 # thus want to consider an empty string as a complete
144 # thus want to consider an empty string as a complete
145 # statement.
145 # statement.
146 return True
146 return True
147 elif ( len(self.input_buffer.split('\n'))>2
147 elif ( len(self.input_buffer.split('\n'))>2
148 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
148 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
149 return False
149 return False
150 else:
150 else:
151 self.capture_output()
151 self.capture_output()
152 try:
152 try:
153 # Add line returns here, to make sure that the statement is
153 # Add line returns here, to make sure that the statement is
154 # complete.
154 # complete.
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
156 "<string>", "exec")
156 "<string>", "exec")
157 self.release_output()
157 self.release_output()
158 except Exception, e:
158 except Exception, e:
159 # XXX: Hack: return True so that the
159 # XXX: Hack: return True so that the
160 # code gets executed and the error captured.
160 # code gets executed and the error captured.
161 is_complete = True
161 is_complete = True
162 return is_complete
162 return is_complete
163
163
164
164
165 def write(self, string, refresh=True):
165 def write(self, string, refresh=True):
166 """ Write some characters to the display.
166 """ Write some characters to the display.
167
167
168 Subclass should overide this method.
168 Subclass should overide this method.
169
169
170 The refresh keyword argument is used in frontends with an
170 The refresh keyword argument is used in frontends with an
171 event loop, to choose whether the write should trigget an UI
171 event loop, to choose whether the write should trigget an UI
172 refresh, and thus be syncrhonous, or not.
172 refresh, and thus be syncrhonous, or not.
173 """
173 """
174 print >>sys.__stderr__, string
174 print >>sys.__stderr__, string
175
175
176
176
177 def execute(self, python_string, raw_string=None):
177 def execute(self, python_string, raw_string=None):
178 """ Stores the raw_string in the history, and sends the
178 """ Stores the raw_string in the history, and sends the
179 python string to the interpreter.
179 python string to the interpreter.
180 """
180 """
181 if raw_string is None:
181 if raw_string is None:
182 raw_string = python_string
182 raw_string = python_string
183 # Create a false result, in case there is an exception
183 # Create a false result, in case there is an exception
184 self.last_result = dict(number=self.prompt_number)
184 self.last_result = dict(number=self.prompt_number)
185
186 ## try:
187 ## self.history.input_cache[-1] = raw_string.rstrip()
188 ## result = self.shell.execute(python_string)
189 ## self.last_result = result
190 ## self.render_result(result)
191 ## except:
192 ## self.show_traceback()
193 ## finally:
194 ## self.after_execute()
195
185 try:
196 try:
186 self.history.input_cache[-1] = raw_string.rstrip()
197 try:
187 result = self.shell.execute(python_string)
198 self.history.input_cache[-1] = raw_string.rstrip()
188 self.last_result = result
199 result = self.shell.execute(python_string)
189 self.render_result(result)
200 self.last_result = result
190 except:
201 self.render_result(result)
191 self.show_traceback()
202 except:
203 self.show_traceback()
192 finally:
204 finally:
193 self.after_execute()
205 self.after_execute()
194
206
207
195 #--------------------------------------------------------------------------
208 #--------------------------------------------------------------------------
196 # LineFrontEndBase interface
209 # LineFrontEndBase interface
197 #--------------------------------------------------------------------------
210 #--------------------------------------------------------------------------
198
211
199 def prefilter_input(self, string):
212 def prefilter_input(self, string):
200 """ Prefilter the input to turn it in valid python.
213 """ Prefilter the input to turn it in valid python.
201 """
214 """
202 string = string.replace('\r\n', '\n')
215 string = string.replace('\r\n', '\n')
203 string = string.replace('\t', 4*' ')
216 string = string.replace('\t', 4*' ')
204 # Clean the trailing whitespace
217 # Clean the trailing whitespace
205 string = '\n'.join(l.rstrip() for l in string.split('\n'))
218 string = '\n'.join(l.rstrip() for l in string.split('\n'))
206 return string
219 return string
207
220
208
221
209 def after_execute(self):
222 def after_execute(self):
210 """ All the operations required after an execution to put the
223 """ All the operations required after an execution to put the
211 terminal back in a shape where it is usable.
224 terminal back in a shape where it is usable.
212 """
225 """
213 self.prompt_number += 1
226 self.prompt_number += 1
214 self.new_prompt(self.input_prompt_template.substitute(
227 self.new_prompt(self.input_prompt_template.substitute(
215 number=(self.last_result['number'] + 1)))
228 number=(self.last_result['number'] + 1)))
216 # Start a new empty history entry
229 # Start a new empty history entry
217 self._add_history(None, '')
230 self._add_history(None, '')
218 self.history_cursor = len(self.history.input_cache) - 1
231 self.history_cursor = len(self.history.input_cache) - 1
219
232
220
233
221 def complete_current_input(self):
234 def complete_current_input(self):
222 """ Do code completion on current line.
235 """ Do code completion on current line.
223 """
236 """
224 if self.debug:
237 if self.debug:
225 print >>sys.__stdout__, "complete_current_input",
238 print >>sys.__stdout__, "complete_current_input",
226 line = self.input_buffer
239 line = self.input_buffer
227 new_line, completions = self.complete(line)
240 new_line, completions = self.complete(line)
228 if len(completions)>1:
241 if len(completions)>1:
229 self.write_completion(completions, new_line=new_line)
242 self.write_completion(completions, new_line=new_line)
230 elif not line == new_line:
243 elif not line == new_line:
231 self.input_buffer = new_line
244 self.input_buffer = new_line
232 if self.debug:
245 if self.debug:
233 print >>sys.__stdout__, 'line', line
246 print >>sys.__stdout__, 'line', line
234 print >>sys.__stdout__, 'new_line', new_line
247 print >>sys.__stdout__, 'new_line', new_line
235 print >>sys.__stdout__, completions
248 print >>sys.__stdout__, completions
236
249
237
250
238 def get_line_width(self):
251 def get_line_width(self):
239 """ Return the width of the line in characters.
252 """ Return the width of the line in characters.
240 """
253 """
241 return 80
254 return 80
242
255
243
256
244 def write_completion(self, possibilities, new_line=None):
257 def write_completion(self, possibilities, new_line=None):
245 """ Write the list of possible completions.
258 """ Write the list of possible completions.
246
259
247 new_line is the completed input line that should be displayed
260 new_line is the completed input line that should be displayed
248 after the completion are writen. If None, the input_buffer
261 after the completion are writen. If None, the input_buffer
249 before the completion is used.
262 before the completion is used.
250 """
263 """
251 if new_line is None:
264 if new_line is None:
252 new_line = self.input_buffer
265 new_line = self.input_buffer
253
266
254 self.write('\n')
267 self.write('\n')
255 max_len = len(max(possibilities, key=len)) + 1
268 max_len = len(max(possibilities, key=len)) + 1
256
269
257 # Now we check how much symbol we can put on a line...
270 # Now we check how much symbol we can put on a line...
258 chars_per_line = self.get_line_width()
271 chars_per_line = self.get_line_width()
259 symbols_per_line = max(1, chars_per_line/max_len)
272 symbols_per_line = max(1, chars_per_line/max_len)
260
273
261 pos = 1
274 pos = 1
262 buf = []
275 buf = []
263 for symbol in possibilities:
276 for symbol in possibilities:
264 if pos < symbols_per_line:
277 if pos < symbols_per_line:
265 buf.append(symbol.ljust(max_len))
278 buf.append(symbol.ljust(max_len))
266 pos += 1
279 pos += 1
267 else:
280 else:
268 buf.append(symbol.rstrip() + '\n')
281 buf.append(symbol.rstrip() + '\n')
269 pos = 1
282 pos = 1
270 self.write(''.join(buf))
283 self.write(''.join(buf))
271 self.new_prompt(self.input_prompt_template.substitute(
284 self.new_prompt(self.input_prompt_template.substitute(
272 number=self.last_result['number'] + 1))
285 number=self.last_result['number'] + 1))
273 self.input_buffer = new_line
286 self.input_buffer = new_line
274
287
275
288
276 def new_prompt(self, prompt):
289 def new_prompt(self, prompt):
277 """ Prints a prompt and starts a new editing buffer.
290 """ Prints a prompt and starts a new editing buffer.
278
291
279 Subclasses should use this method to make sure that the
292 Subclasses should use this method to make sure that the
280 terminal is put in a state favorable for a new line
293 terminal is put in a state favorable for a new line
281 input.
294 input.
282 """
295 """
283 self.input_buffer = ''
296 self.input_buffer = ''
284 self.write(prompt)
297 self.write(prompt)
285
298
286
299
287 #--------------------------------------------------------------------------
300 #--------------------------------------------------------------------------
288 # Private API
301 # Private API
289 #--------------------------------------------------------------------------
302 #--------------------------------------------------------------------------
290
303
291 def _on_enter(self):
304 def _on_enter(self):
292 """ Called when the return key is pressed in a line editing
305 """ Called when the return key is pressed in a line editing
293 buffer.
306 buffer.
294 """
307 """
295 current_buffer = self.input_buffer
308 current_buffer = self.input_buffer
296 cleaned_buffer = self.prefilter_input(current_buffer)
309 cleaned_buffer = self.prefilter_input(current_buffer)
297 if self.is_complete(cleaned_buffer):
310 if self.is_complete(cleaned_buffer):
298 self.execute(cleaned_buffer, raw_string=current_buffer)
311 self.execute(cleaned_buffer, raw_string=current_buffer)
299 else:
312 else:
300 self.input_buffer += self._get_indent_string(
313 self.input_buffer += self._get_indent_string(
301 current_buffer[:-1])
314 current_buffer[:-1])
302 if len(current_buffer.split('\n')) == 2:
315 if len(current_buffer.split('\n')) == 2:
303 self.input_buffer += '\t\t'
316 self.input_buffer += '\t\t'
304 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
317 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
305 self.input_buffer += '\t'
318 self.input_buffer += '\t'
306
319
307
320
308 def _get_indent_string(self, string):
321 def _get_indent_string(self, string):
309 """ Return the string of whitespace that prefixes a line. Used to
322 """ Return the string of whitespace that prefixes a line. Used to
310 add the right amount of indendation when creating a new line.
323 add the right amount of indendation when creating a new line.
311 """
324 """
312 string = string.replace('\t', ' '*4)
325 string = string.replace('\t', ' '*4)
313 string = string.split('\n')[-1]
326 string = string.split('\n')[-1]
314 indent_chars = len(string) - len(string.lstrip())
327 indent_chars = len(string) - len(string.lstrip())
315 indent_string = '\t'*(indent_chars // 4) + \
328 indent_string = '\t'*(indent_chars // 4) + \
316 ' '*(indent_chars % 4)
329 ' '*(indent_chars % 4)
317
330
318 return indent_string
331 return indent_string
319
332
320
333
@@ -1,230 +1,246 b''
1 """
1 """
2 Frontend class that uses IPython0 to prefilter the inputs.
2 Frontend class that uses IPython0 to prefilter the inputs.
3
3
4 Using the IPython0 mechanism gives us access to the magics.
4 Using the IPython0 mechanism gives us access to the magics.
5
5
6 This is a transitory class, used here to do the transition between
6 This is a transitory class, used here to do the transition between
7 ipython0 and ipython1. This class is meant to be short-lived as more
7 ipython0 and ipython1. This class is meant to be short-lived as more
8 functionnality is abstracted out of ipython0 in reusable functions and
8 functionnality is abstracted out of ipython0 in reusable functions and
9 is added on the interpreter. This class can be a used to guide this
9 is added on the interpreter. This class can be a used to guide this
10 refactoring.
10 refactoring.
11 """
11 """
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24 import sys
24 import sys
25
25
26 from linefrontendbase import LineFrontEndBase, common_prefix
26 from linefrontendbase import LineFrontEndBase, common_prefix
27 from frontendbase import FrontEndBase
27 from frontendbase import FrontEndBase
28
28
29 from IPython.ipmaker import make_IPython
29 from IPython.ipmaker import make_IPython
30 from IPython.ipapi import IPApi
30 from IPython.ipapi import IPApi
31 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
32
32
33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
34
34
35 from IPython.genutils import Term
35 from IPython.genutils import Term
36 import pydoc
36 import pydoc
37 import os
37 import os
38 import sys
38 import sys
39
39
40
40
41 def mk_system_call(system_call_function, command):
41 def mk_system_call(system_call_function, command):
42 """ given a os.system replacement, and a leading string command,
42 """ given a os.system replacement, and a leading string command,
43 returns a function that will execute the command with the given
43 returns a function that will execute the command with the given
44 argument string.
44 argument string.
45 """
45 """
46 def my_system_call(args):
46 def my_system_call(args):
47 system_call_function("%s %s" % (command, args))
47 system_call_function("%s %s" % (command, args))
48 return my_system_call
48 return my_system_call
49
49
50 #-------------------------------------------------------------------------------
50 #-------------------------------------------------------------------------------
51 # Frontend class using ipython0 to do the prefiltering.
51 # Frontend class using ipython0 to do the prefiltering.
52 #-------------------------------------------------------------------------------
52 #-------------------------------------------------------------------------------
53 class PrefilterFrontEnd(LineFrontEndBase):
53 class PrefilterFrontEnd(LineFrontEndBase):
54 """ Class that uses ipython0 to do prefilter the input, do the
54 """ Class that uses ipython0 to do prefilter the input, do the
55 completion and the magics.
55 completion and the magics.
56
56
57 The core trick is to use an ipython0 instance to prefilter the
57 The core trick is to use an ipython0 instance to prefilter the
58 input, and share the namespace between the interpreter instance used
58 input, and share the namespace between the interpreter instance used
59 to execute the statements and the ipython0 used for code
59 to execute the statements and the ipython0 used for code
60 completion...
60 completion...
61 """
61 """
62
62
63 debug = False
63 debug = False
64
64
65 def __init__(self, ipython0=None, *args, **kwargs):
65 def __init__(self, ipython0=None, *args, **kwargs):
66 """ Parameters:
66 """ Parameters:
67 -----------
67 -----------
68
68
69 ipython0: an optional ipython0 instance to use for command
69 ipython0: an optional ipython0 instance to use for command
70 prefiltering and completion.
70 prefiltering and completion.
71 """
71 """
72 LineFrontEndBase.__init__(self, *args, **kwargs)
72 LineFrontEndBase.__init__(self, *args, **kwargs)
73 self.shell.output_trap = RedirectorOutputTrap(
73 self.shell.output_trap = RedirectorOutputTrap(
74 out_callback=self.write,
74 out_callback=self.write,
75 err_callback=self.write,
75 err_callback=self.write,
76 )
76 )
77 self.shell.traceback_trap = SyncTracebackTrap(
77 self.shell.traceback_trap = SyncTracebackTrap(
78 formatters=self.shell.traceback_trap.formatters,
78 formatters=self.shell.traceback_trap.formatters,
79 )
79 )
80
80
81 # Start the ipython0 instance:
81 # Start the ipython0 instance:
82 self.save_output_hooks()
82 self.save_output_hooks()
83 if ipython0 is None:
83 if ipython0 is None:
84 # Instanciate an IPython0 interpreter to be able to use the
84 # Instanciate an IPython0 interpreter to be able to use the
85 # prefiltering.
85 # prefiltering.
86 # XXX: argv=[] is a bit bold.
86 # XXX: argv=[] is a bit bold.
87 ipython0 = make_IPython(argv=[],
87 ipython0 = make_IPython(argv=[],
88 user_ns=self.shell.user_ns,
88 user_ns=self.shell.user_ns,
89 user_global_ns=self.shell.user_global_ns)
89 user_global_ns=self.shell.user_global_ns)
90 self.ipython0 = ipython0
90 self.ipython0 = ipython0
91 # Set the pager:
91 # Set the pager:
92 self.ipython0.set_hook('show_in_pager',
92 self.ipython0.set_hook('show_in_pager',
93 lambda s, string: self.write("\n" + string))
93 lambda s, string: self.write("\n" + string))
94 self.ipython0.write = self.write
94 self.ipython0.write = self.write
95 self._ip = _ip = IPApi(self.ipython0)
95 self._ip = _ip = IPApi(self.ipython0)
96 # Make sure the raw system call doesn't get called, as we don't
96 # Make sure the raw system call doesn't get called, as we don't
97 # have a stdin accessible.
97 # have a stdin accessible.
98 self._ip.system = self.system_call
98 self._ip.system = self.system_call
99 # XXX: Muck around with magics so that they work better
99 # XXX: Muck around with magics so that they work better
100 # in our environment
100 # in our environment
101 self.ipython0.magic_ls = mk_system_call(self.system_call,
101 self.ipython0.magic_ls = mk_system_call(self.system_call,
102 'ls -CF')
102 'ls -CF')
103 # And now clean up the mess created by ipython0
103 # And now clean up the mess created by ipython0
104 self.release_output()
104 self.release_output()
105
105
106
106
107 if not 'banner' in kwargs and self.banner is None:
107 if not 'banner' in kwargs and self.banner is None:
108 self.banner = self.ipython0.BANNER + """
108 self.banner = self.ipython0.BANNER + """
109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
110
110
111 self.start()
111 self.start()
112
112
113 #--------------------------------------------------------------------------
113 #--------------------------------------------------------------------------
114 # FrontEndBase interface
114 # FrontEndBase interface
115 #--------------------------------------------------------------------------
115 #--------------------------------------------------------------------------
116
116
117 def show_traceback(self):
117 def show_traceback(self):
118 """ Use ipython0 to capture the last traceback and display it.
118 """ Use ipython0 to capture the last traceback and display it.
119 """
119 """
120 self.capture_output()
120 self.capture_output()
121 self.ipython0.showtraceback(tb_offset=-1)
121 self.ipython0.showtraceback(tb_offset=-1)
122 self.release_output()
122 self.release_output()
123
123
124
124
125 def execute(self, python_string, raw_string=None):
125 def execute(self, python_string, raw_string=None):
126 if self.debug:
126 if self.debug:
127 print 'Executing Python code:', repr(python_string)
127 print 'Executing Python code:', repr(python_string)
128 self.capture_output()
128 self.capture_output()
129 LineFrontEndBase.execute(self, python_string,
129 LineFrontEndBase.execute(self, python_string,
130 raw_string=raw_string)
130 raw_string=raw_string)
131 self.release_output()
131 self.release_output()
132
132
133
133
134 def save_output_hooks(self):
134 def save_output_hooks(self):
135 """ Store all the output hooks we can think of, to be able to
135 """ Store all the output hooks we can think of, to be able to
136 restore them.
136 restore them.
137
137
138 We need to do this early, as starting the ipython0 instance will
138 We need to do this early, as starting the ipython0 instance will
139 screw ouput hooks.
139 screw ouput hooks.
140 """
140 """
141 self.__old_cout_write = Term.cout.write
141 self.__old_cout_write = Term.cout.write
142 self.__old_cerr_write = Term.cerr.write
142 self.__old_cerr_write = Term.cerr.write
143 self.__old_stdout = sys.stdout
143 self.__old_stdout = sys.stdout
144 self.__old_stderr= sys.stderr
144 self.__old_stderr= sys.stderr
145 self.__old_help_output = pydoc.help.output
145 self.__old_help_output = pydoc.help.output
146 self.__old_display_hook = sys.displayhook
146 self.__old_display_hook = sys.displayhook
147
147
148
148
149 def capture_output(self):
149 def capture_output(self):
150 """ Capture all the output mechanisms we can think of.
150 """ Capture all the output mechanisms we can think of.
151 """
151 """
152 self.save_output_hooks()
152 self.save_output_hooks()
153 Term.cout.write = self.write
153 Term.cout.write = self.write
154 Term.cerr.write = self.write
154 Term.cerr.write = self.write
155 sys.stdout = Term.cout
155 sys.stdout = Term.cout
156 sys.stderr = Term.cerr
156 sys.stderr = Term.cerr
157 pydoc.help.output = self.shell.output_trap.out
157 pydoc.help.output = self.shell.output_trap.out
158
158
159
159
160 def release_output(self):
160 def release_output(self):
161 """ Release all the different captures we have made.
161 """ Release all the different captures we have made.
162 """
162 """
163 Term.cout.write = self.__old_cout_write
163 Term.cout.write = self.__old_cout_write
164 Term.cerr.write = self.__old_cerr_write
164 Term.cerr.write = self.__old_cerr_write
165 sys.stdout = self.__old_stdout
165 sys.stdout = self.__old_stdout
166 sys.stderr = self.__old_stderr
166 sys.stderr = self.__old_stderr
167 pydoc.help.output = self.__old_help_output
167 pydoc.help.output = self.__old_help_output
168 sys.displayhook = self.__old_display_hook
168 sys.displayhook = self.__old_display_hook
169
169
170
170
171 def complete(self, line):
171 def complete(self, line):
172 # FIXME: This should be factored out in the linefrontendbase
172 # FIXME: This should be factored out in the linefrontendbase
173 # method.
173 # method.
174 word = line.split('\n')[-1].split(' ')[-1]
174 word = line.split('\n')[-1].split(' ')[-1]
175 completions = self.ipython0.complete(word)
175 completions = self.ipython0.complete(word)
176 # FIXME: The proper sort should be done in the complete method.
176 # FIXME: The proper sort should be done in the complete method.
177 key = lambda x: x.replace('_', '')
177 key = lambda x: x.replace('_', '')
178 completions.sort(key=key)
178 completions.sort(key=key)
179 if completions:
179 if completions:
180 prefix = common_prefix(completions)
180 prefix = common_prefix(completions)
181 line = line[:-len(word)] + prefix
181 line = line[:-len(word)] + prefix
182 return line, completions
182 return line, completions
183
183
184
184
185 #--------------------------------------------------------------------------
185 #--------------------------------------------------------------------------
186 # LineFrontEndBase interface
186 # LineFrontEndBase interface
187 #--------------------------------------------------------------------------
187 #--------------------------------------------------------------------------
188
188
189 def prefilter_input(self, input_string):
189 def prefilter_input(self, input_string):
190 """ Using IPython0 to prefilter the commands to turn them
190 """ Using IPython0 to prefilter the commands to turn them
191 in executable statements that are valid Python strings.
191 in executable statements that are valid Python strings.
192 """
192 """
193 input_string = LineFrontEndBase.prefilter_input(self, input_string)
193 input_string = LineFrontEndBase.prefilter_input(self, input_string)
194 filtered_lines = []
194 filtered_lines = []
195 # The IPython0 prefilters sometime produce output. We need to
195 # The IPython0 prefilters sometime produce output. We need to
196 # capture it.
196 # capture it.
197 self.capture_output()
197 self.capture_output()
198 self.last_result = dict(number=self.prompt_number)
198 self.last_result = dict(number=self.prompt_number)
199
200 ## try:
201 ## for line in input_string.split('\n'):
202 ## filtered_lines.append(
203 ## self.ipython0.prefilter(line, False).rstrip())
204 ## except:
205 ## # XXX: probably not the right thing to do.
206 ## self.ipython0.showsyntaxerror()
207 ## self.after_execute()
208 ## finally:
209 ## self.release_output()
210
211
199 try:
212 try:
200 for line in input_string.split('\n'):
213 try:
201 filtered_lines.append(
214 for line in input_string.split('\n'):
202 self.ipython0.prefilter(line, False).rstrip())
215 filtered_lines.append(
203 except:
216 self.ipython0.prefilter(line, False).rstrip())
204 # XXX: probably not the right thing to do.
217 except:
205 self.ipython0.showsyntaxerror()
218 # XXX: probably not the right thing to do.
206 self.after_execute()
219 self.ipython0.showsyntaxerror()
220 self.after_execute()
207 finally:
221 finally:
208 self.release_output()
222 self.release_output()
209
223
224
225
210 # Clean up the trailing whitespace, to avoid indentation errors
226 # Clean up the trailing whitespace, to avoid indentation errors
211 filtered_string = '\n'.join(filtered_lines)
227 filtered_string = '\n'.join(filtered_lines)
212 return filtered_string
228 return filtered_string
213
229
214
230
215 #--------------------------------------------------------------------------
231 #--------------------------------------------------------------------------
216 # PrefilterFrontEnd interface
232 # PrefilterFrontEnd interface
217 #--------------------------------------------------------------------------
233 #--------------------------------------------------------------------------
218
234
219 def system_call(self, command_string):
235 def system_call(self, command_string):
220 """ Allows for frontend to define their own system call, to be
236 """ Allows for frontend to define their own system call, to be
221 able capture output and redirect input.
237 able capture output and redirect input.
222 """
238 """
223 return os.system(command_string)
239 return os.system(command_string)
224
240
225
241
226 def do_exit(self):
242 def do_exit(self):
227 """ Exit the shell, cleanup and save the history.
243 """ Exit the shell, cleanup and save the history.
228 """
244 """
229 self.ipython0.atexit_operations()
245 self.ipython0.atexit_operations()
230
246
@@ -1,34 +1,30 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 zope.interface mock. If zope is installed, this module provides a zope
4 zope.interface mock. If zope is installed, this module provides a zope
5 interface classes, if not it provides mocks for them.
5 interface classes, if not it provides mocks for them.
6
6
7 Classes provided:
7 Classes provided:
8 Interface, Attribute, implements, classProvides
8 Interface, Attribute, implements, classProvides
9 """
9 """
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
11
11
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
13 # Copyright (C) 2008 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 import string
23 import uuid
24 import _ast
25
26 try:
22 try:
27 from zope.interface import Interface, Attribute, implements, classProvides
23 from zope.interface import Interface, Attribute, implements, classProvides
28 except ImportError:
24 except ImportError:
29 #zope.interface is not available
25 #zope.interface is not available
30 Interface = object
26 Interface = object
31 def Attribute(name, doc): pass
27 def Attribute(name, doc): pass
32 def implements(interface): pass
28 def implements(interface): pass
33 def classProvides(interface): pass
29 def classProvides(interface): pass
34
30
@@ -1,143 +1,141 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.kernel.test.test_contexts -*-
2 # -*- test-case-name: IPython.kernel.test.test_contexts -*-
3 """Context managers for IPython.
3 """Context managers for IPython.
4
4
5 Python 2.5 introduced the `with` statement, which is based on the context
5 Python 2.5 introduced the `with` statement, which is based on the context
6 manager protocol. This module offers a few context managers for common cases,
6 manager protocol. This module offers a few context managers for common cases,
7 which can also be useful as templates for writing new, application-specific
7 which can also be useful as templates for writing new, application-specific
8 managers.
8 managers.
9 """
9 """
10
10
11 from __future__ import with_statement
12
13 __docformat__ = "restructuredtext en"
11 __docformat__ = "restructuredtext en"
14
12
15 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
17 #
15 #
18 # 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
19 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
21
19
22 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
23 # Imports
21 # Imports
24 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
25
23
26 import linecache
24 import linecache
27 import sys
25 import sys
28
26
29 from twisted.internet.error import ConnectionRefusedError
27 from twisted.internet.error import ConnectionRefusedError
30
28
31 from IPython.ultraTB import _fixed_getinnerframes, findsource
29 from IPython.ultraTB import _fixed_getinnerframes, findsource
32 from IPython import ipapi
30 from IPython import ipapi
33
31
34 from IPython.kernel import error
32 from IPython.kernel import error
35
33
36 #---------------------------------------------------------------------------
34 #---------------------------------------------------------------------------
37 # Utility functions needed by all context managers.
35 # Utility functions needed by all context managers.
38 #---------------------------------------------------------------------------
36 #---------------------------------------------------------------------------
39
37
40 def remote():
38 def remote():
41 """Raises a special exception meant to be caught by context managers.
39 """Raises a special exception meant to be caught by context managers.
42 """
40 """
43 m = 'Special exception to stop local execution of parallel code.'
41 m = 'Special exception to stop local execution of parallel code.'
44 raise error.StopLocalExecution(m)
42 raise error.StopLocalExecution(m)
45
43
46
44
47 def strip_whitespace(source,require_remote=True):
45 def strip_whitespace(source,require_remote=True):
48 """strip leading whitespace from input source.
46 """strip leading whitespace from input source.
49
47
50 :Parameters:
48 :Parameters:
51
49
52 """
50 """
53 remote_mark = 'remote()'
51 remote_mark = 'remote()'
54 # Expand tabs to avoid any confusion.
52 # Expand tabs to avoid any confusion.
55 wsource = [l.expandtabs(4) for l in source]
53 wsource = [l.expandtabs(4) for l in source]
56 # Detect the indentation level
54 # Detect the indentation level
57 done = False
55 done = False
58 for line in wsource:
56 for line in wsource:
59 if line.isspace():
57 if line.isspace():
60 continue
58 continue
61 for col,char in enumerate(line):
59 for col,char in enumerate(line):
62 if char != ' ':
60 if char != ' ':
63 done = True
61 done = True
64 break
62 break
65 if done:
63 if done:
66 break
64 break
67 # Now we know how much leading space there is in the code. Next, we
65 # Now we know how much leading space there is in the code. Next, we
68 # extract up to the first line that has less indentation.
66 # extract up to the first line that has less indentation.
69 # WARNINGS: we skip comments that may be misindented, but we do NOT yet
67 # WARNINGS: we skip comments that may be misindented, but we do NOT yet
70 # detect triple quoted strings that may have flush left text.
68 # detect triple quoted strings that may have flush left text.
71 for lno,line in enumerate(wsource):
69 for lno,line in enumerate(wsource):
72 lead = line[:col]
70 lead = line[:col]
73 if lead.isspace():
71 if lead.isspace():
74 continue
72 continue
75 else:
73 else:
76 if not lead.lstrip().startswith('#'):
74 if not lead.lstrip().startswith('#'):
77 break
75 break
78 # The real 'with' source is up to lno
76 # The real 'with' source is up to lno
79 src_lines = [l[col:] for l in wsource[:lno+1]]
77 src_lines = [l[col:] for l in wsource[:lno+1]]
80
78
81 # Finally, check that the source's first non-comment line begins with the
79 # Finally, check that the source's first non-comment line begins with the
82 # special call 'remote()'
80 # special call 'remote()'
83 if require_remote:
81 if require_remote:
84 for nline,line in enumerate(src_lines):
82 for nline,line in enumerate(src_lines):
85 if line.isspace() or line.startswith('#'):
83 if line.isspace() or line.startswith('#'):
86 continue
84 continue
87 if line.startswith(remote_mark):
85 if line.startswith(remote_mark):
88 break
86 break
89 else:
87 else:
90 raise ValueError('%s call missing at the start of code' %
88 raise ValueError('%s call missing at the start of code' %
91 remote_mark)
89 remote_mark)
92 out_lines = src_lines[nline+1:]
90 out_lines = src_lines[nline+1:]
93 else:
91 else:
94 # If the user specified that the remote() call wasn't mandatory
92 # If the user specified that the remote() call wasn't mandatory
95 out_lines = src_lines
93 out_lines = src_lines
96
94
97 # src = ''.join(out_lines) # dbg
95 # src = ''.join(out_lines) # dbg
98 #print 'SRC:\n<<<<<<<>>>>>>>\n%s<<<<<>>>>>>' % src # dbg
96 #print 'SRC:\n<<<<<<<>>>>>>>\n%s<<<<<>>>>>>' % src # dbg
99 return ''.join(out_lines)
97 return ''.join(out_lines)
100
98
101 class RemoteContextBase(object):
99 class RemoteContextBase(object):
102 def __init__(self):
100 def __init__(self):
103 self.ip = ipapi.get()
101 self.ip = ipapi.get()
104
102
105 def _findsource_file(self,f):
103 def _findsource_file(self,f):
106 linecache.checkcache()
104 linecache.checkcache()
107 s = findsource(f.f_code)
105 s = findsource(f.f_code)
108 lnum = f.f_lineno
106 lnum = f.f_lineno
109 wsource = s[0][f.f_lineno:]
107 wsource = s[0][f.f_lineno:]
110 return strip_whitespace(wsource)
108 return strip_whitespace(wsource)
111
109
112 def _findsource_ipython(self,f):
110 def _findsource_ipython(self,f):
113 from IPython import ipapi
111 from IPython import ipapi
114 self.ip = ipapi.get()
112 self.ip = ipapi.get()
115 buf = self.ip.IP.input_hist_raw[-1].splitlines()[1:]
113 buf = self.ip.IP.input_hist_raw[-1].splitlines()[1:]
116 wsource = [l+'\n' for l in buf ]
114 wsource = [l+'\n' for l in buf ]
117
115
118 return strip_whitespace(wsource)
116 return strip_whitespace(wsource)
119
117
120 def findsource(self,frame):
118 def findsource(self,frame):
121 local_ns = frame.f_locals
119 local_ns = frame.f_locals
122 global_ns = frame.f_globals
120 global_ns = frame.f_globals
123 if frame.f_code.co_filename == '<ipython console>':
121 if frame.f_code.co_filename == '<ipython console>':
124 src = self._findsource_ipython(frame)
122 src = self._findsource_ipython(frame)
125 else:
123 else:
126 src = self._findsource_file(frame)
124 src = self._findsource_file(frame)
127 return src
125 return src
128
126
129 def __enter__(self):
127 def __enter__(self):
130 raise NotImplementedError
128 raise NotImplementedError
131
129
132 def __exit__ (self, etype, value, tb):
130 def __exit__ (self, etype, value, tb):
133 if issubclass(etype,error.StopLocalExecution):
131 if issubclass(etype,error.StopLocalExecution):
134 return True
132 return True
135
133
136 class RemoteMultiEngine(RemoteContextBase):
134 class RemoteMultiEngine(RemoteContextBase):
137 def __init__(self,mec):
135 def __init__(self,mec):
138 self.mec = mec
136 self.mec = mec
139 RemoteContextBase.__init__(self)
137 RemoteContextBase.__init__(self)
140
138
141 def __enter__(self):
139 def __enter__(self):
142 src = self.findsource(sys._getframe(1))
140 src = self.findsource(sys._getframe(1))
143 return self.mec.execute(src)
141 return self.mec.execute(src)
@@ -1,41 +1,43 b''
1 from __future__ import with_statement
1 #from __future__ import with_statement
2
3 # XXX This file is currently disabled to preserve 2.4 compatibility.
2
4
3 #def test_simple():
5 #def test_simple():
4 if 0:
6 if 0:
5
7
6 # XXX - for now, we need a running cluster to be started separately. The
8 # XXX - for now, we need a running cluster to be started separately. The
7 # daemon work is almost finished, and will make much of this unnecessary.
9 # daemon work is almost finished, and will make much of this unnecessary.
8 from IPython.kernel import client
10 from IPython.kernel import client
9 mec = client.MultiEngineClient(('127.0.0.1',10105))
11 mec = client.MultiEngineClient(('127.0.0.1',10105))
10
12
11 try:
13 try:
12 mec.get_ids()
14 mec.get_ids()
13 except ConnectionRefusedError:
15 except ConnectionRefusedError:
14 import os, time
16 import os, time
15 os.system('ipcluster -n 2 &')
17 os.system('ipcluster -n 2 &')
16 time.sleep(2)
18 time.sleep(2)
17 mec = client.MultiEngineClient(('127.0.0.1',10105))
19 mec = client.MultiEngineClient(('127.0.0.1',10105))
18
20
19 mec.block = False
21 mec.block = False
20
22
21 import itertools
23 import itertools
22 c = itertools.count()
24 c = itertools.count()
23
25
24 parallel = RemoteMultiEngine(mec)
26 parallel = RemoteMultiEngine(mec)
25
27
26 mec.pushAll()
28 mec.pushAll()
27
29
28 with parallel as pr:
30 ## with parallel as pr:
29 # A comment
31 ## # A comment
30 remote() # this means the code below only runs remotely
32 ## remote() # this means the code below only runs remotely
31 print 'Hello remote world'
33 ## print 'Hello remote world'
32 x = range(10)
34 ## x = range(10)
33 # Comments are OK
35 ## # Comments are OK
34 # Even misindented.
36 ## # Even misindented.
35 y = x+1
37 ## y = x+1
36
38
37
39
38 with pfor('i',sequence) as pr:
40 ## with pfor('i',sequence) as pr:
39 print x[i]
41 ## print x[i]
40
42
41 print pr.x + pr.y
43 print pr.x + pr.y
@@ -1,120 +1,123 b''
1 """Example showing how to merge multiple remote data streams.
1 """Example showing how to merge multiple remote data streams.
2 """
2 """
3 # Slightly modified version of:
3 # Slightly modified version of:
4 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/511509
4 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/511509
5
5
6 import heapq
6 import heapq
7 from IPython.kernel.error import CompositeError
7 from IPython.kernel.error import CompositeError
8
8
9 def mergesort(list_of_lists, key=None):
9 def mergesort(list_of_lists, key=None):
10 """ Perform an N-way merge operation on sorted lists.
10 """ Perform an N-way merge operation on sorted lists.
11
11
12 @param list_of_lists: (really iterable of iterable) of sorted elements
12 @param list_of_lists: (really iterable of iterable) of sorted elements
13 (either by naturally or by C{key})
13 (either by naturally or by C{key})
14 @param key: specify sort key function (like C{sort()}, C{sorted()})
14 @param key: specify sort key function (like C{sort()}, C{sorted()})
15
15
16 Yields tuples of the form C{(item, iterator)}, where the iterator is the
16 Yields tuples of the form C{(item, iterator)}, where the iterator is the
17 built-in list iterator or something you pass in, if you pre-generate the
17 built-in list iterator or something you pass in, if you pre-generate the
18 iterators.
18 iterators.
19
19
20 This is a stable merge; complexity O(N lg N)
20 This is a stable merge; complexity O(N lg N)
21
21
22 Examples::
22 Examples::
23
23
24 >>> print list(mergesort([[1,2,3,4],
24 >>> print list(mergesort([[1,2,3,4],
25 ... [2,3.25,3.75,4.5,6,7],
25 ... [2,3.25,3.75,4.5,6,7],
26 ... [2.625,3.625,6.625,9]]))
26 ... [2.625,3.625,6.625,9]]))
27 [1, 2, 2, 2.625, 3, 3.25, 3.625, 3.75, 4, 4.5, 6, 6.625, 7, 9]
27 [1, 2, 2, 2.625, 3, 3.25, 3.625, 3.75, 4, 4.5, 6, 6.625, 7, 9]
28
28
29 # note stability
29 # note stability
30 >>> print list(mergesort([[1,2,3,4],
30 >>> print list(mergesort([[1,2,3,4],
31 ... [2,3.25,3.75,4.5,6,7],
31 ... [2,3.25,3.75,4.5,6,7],
32 ... [2.625,3.625,6.625,9]],
32 ... [2.625,3.625,6.625,9]],
33 ... key=int))
33 ... key=int))
34 [1, 2, 2, 2.625, 3, 3.25, 3.75, 3.625, 4, 4.5, 6, 6.625, 7, 9]
34 [1, 2, 2, 2.625, 3, 3.25, 3.75, 3.625, 4, 4.5, 6, 6.625, 7, 9]
35
35
36
36
37 >>> print list(mergesort([[4, 3, 2, 1],
37 >>> print list(mergesort([[4, 3, 2, 1],
38 ... [7, 6, 4.5, 3.75, 3.25, 2],
38 ... [7, 6, 4.5, 3.75, 3.25, 2],
39 ... [9, 6.625, 3.625, 2.625]],
39 ... [9, 6.625, 3.625, 2.625]],
40 ... key=lambda x: -x))
40 ... key=lambda x: -x))
41 [9, 7, 6.625, 6, 4.5, 4, 3.75, 3.625, 3.25, 3, 2.625, 2, 2, 1]
41 [9, 7, 6.625, 6, 4.5, 4, 3.75, 3.625, 3.25, 3, 2.625, 2, 2, 1]
42 """
42 """
43
43
44 heap = []
44 heap = []
45 for i, itr in enumerate(iter(pl) for pl in list_of_lists):
45 for i, itr in enumerate(iter(pl) for pl in list_of_lists):
46 try:
46 try:
47 item = itr.next()
47 item = itr.next()
48 toadd = (key(item), i, item, itr) if key else (item, i, itr)
48 if key:
49 toadd = (key(item), i, item, itr)
50 else:
51 toadd = (item, i, itr)
49 heap.append(toadd)
52 heap.append(toadd)
50 except StopIteration:
53 except StopIteration:
51 pass
54 pass
52 heapq.heapify(heap)
55 heapq.heapify(heap)
53
56
54 if key:
57 if key:
55 while heap:
58 while heap:
56 _, idx, item, itr = heap[0]
59 _, idx, item, itr = heap[0]
57 yield item
60 yield item
58 try:
61 try:
59 item = itr.next()
62 item = itr.next()
60 heapq.heapreplace(heap, (key(item), idx, item, itr) )
63 heapq.heapreplace(heap, (key(item), idx, item, itr) )
61 except StopIteration:
64 except StopIteration:
62 heapq.heappop(heap)
65 heapq.heappop(heap)
63
66
64 else:
67 else:
65 while heap:
68 while heap:
66 item, idx, itr = heap[0]
69 item, idx, itr = heap[0]
67 yield item
70 yield item
68 try:
71 try:
69 heapq.heapreplace(heap, (itr.next(), idx, itr))
72 heapq.heapreplace(heap, (itr.next(), idx, itr))
70 except StopIteration:
73 except StopIteration:
71 heapq.heappop(heap)
74 heapq.heappop(heap)
72
75
73
76
74 def remote_iterator(rc,engine,name):
77 def remote_iterator(rc,engine,name):
75 """Return an iterator on an object living on a remote engine.
78 """Return an iterator on an object living on a remote engine.
76 """
79 """
77 # Check that the object exists on the engine and pin a reference to it
80 # Check that the object exists on the engine and pin a reference to it
78 iter_name = '_%s_rmt_iter_' % name
81 iter_name = '_%s_rmt_iter_' % name
79 rc.execute('%s = iter(%s)' % (iter_name,name), targets=engine)
82 rc.execute('%s = iter(%s)' % (iter_name,name), targets=engine)
80 tpl = '_tmp = %s.next()' % iter_name
83 tpl = '_tmp = %s.next()' % iter_name
81 while True:
84 while True:
82 try:
85 try:
83 rc.execute(tpl, targets=engine)
86 rc.execute(tpl, targets=engine)
84 result = rc.pull('_tmp', targets=engine)[0]
87 result = rc.pull('_tmp', targets=engine)[0]
85 # This causes the StopIteration exception to be raised.
88 # This causes the StopIteration exception to be raised.
86 except CompositeError, e:
89 except CompositeError, e:
87 e.raise_exception()
90 e.raise_exception()
88 else:
91 else:
89 yield result
92 yield result
90
93
91 # Main, interactive testing
94 # Main, interactive testing
92 if __name__ == '__main__':
95 if __name__ == '__main__':
93
96
94 from IPython.kernel import client
97 from IPython.kernel import client
95 ipc = client.MultiEngineClient()
98 ipc = client.MultiEngineClient()
96 print 'Engine IDs:',ipc.get_ids()
99 print 'Engine IDs:',ipc.get_ids()
97
100
98 # Make a set of 'sorted datasets'
101 # Make a set of 'sorted datasets'
99 a0 = range(5,20)
102 a0 = range(5,20)
100 a1 = range(10)
103 a1 = range(10)
101 a2 = range(15,25)
104 a2 = range(15,25)
102
105
103 # Now, imagine these had been created in the remote engines by some long
106 # Now, imagine these had been created in the remote engines by some long
104 # computation. In this simple example, we just send them over into the
107 # computation. In this simple example, we just send them over into the
105 # remote engines. They will all be called 'a' in each engine.
108 # remote engines. They will all be called 'a' in each engine.
106 ipc.push(dict(a=a0), targets=0)
109 ipc.push(dict(a=a0), targets=0)
107 ipc.push(dict(a=a1), targets=1)
110 ipc.push(dict(a=a1), targets=1)
108 ipc.push(dict(a=a2), targets=2)
111 ipc.push(dict(a=a2), targets=2)
109
112
110 # And we now make a local object which represents the remote iterator
113 # And we now make a local object which represents the remote iterator
111 aa0 = remote_iterator(ipc,0,'a')
114 aa0 = remote_iterator(ipc,0,'a')
112 aa1 = remote_iterator(ipc,1,'a')
115 aa1 = remote_iterator(ipc,1,'a')
113 aa2 = remote_iterator(ipc,2,'a')
116 aa2 = remote_iterator(ipc,2,'a')
114
117
115 # Let's merge them, both locally and remotely:
118 # Let's merge them, both locally and remotely:
116 print 'Merge the local datasets:'
119 print 'Merge the local datasets:'
117 print list(mergesort([a0,a1,a2]))
120 print list(mergesort([a0,a1,a2]))
118
121
119 print 'Locally merge the remote sets:'
122 print 'Locally merge the remote sets:'
120 print list(mergesort([aa0,aa1,aa2]))
123 print list(mergesort([aa0,aa1,aa2]))
@@ -1,31 +1,31 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 # release test
3 # release test
4
4
5 ipdir=$PWD/..
5 ipdir=$PWD/..
6
6
7 cd $ipdir
7 cd $ipdir
8
8
9 # Clean up build/dist directories
9 # Clean up build/dist directories
10 rm -rf $ipdir/build/*
10 rm -rf $ipdir/build/*
11 rm -rf $ipdir/dist/*
11 rm -rf $ipdir/dist/*
12
12
13 # build source distros
13 # build source distros
14 cd $ipdir
14 cd $ipdir
15 ./setup.py sdist --formats=gztar
15 ./setup.py sdist --formats=gztar
16
16
17 # Build rpms
17 # Build rpms
18 #python2.4 ./setup.py bdist_rpm --binary-only --release=py24 --python=/usr/bin/python2.4
18 python2.4 ./setup.py bdist_rpm --binary-only --release=py24 --python=/usr/bin/python2.4
19 #python2.5 ./setup.py bdist_rpm --binary-only --release=py25 --python=/usr/bin/python2.5
19 python2.5 ./setup.py bdist_rpm --binary-only --release=py25 --python=/usr/bin/python2.5
20
20
21 # Build eggs
21 # Build eggs
22 python2.4 ./setup_bdist_egg.py
22 python2.4 ./setup_bdist_egg.py
23 python2.5 ./setup_bdist_egg.py
23 python2.5 ./setup_bdist_egg.py
24
24
25 # Call the windows build separately, so that the extra Windows scripts don't
25 # Call the windows build separately, so that the extra Windows scripts don't
26 # get pulled into Unix builds (setup.py has code which checks for
26 # get pulled into Unix builds (setup.py has code which checks for
27 # bdist_wininst)
27 # bdist_wininst)
28 ./setup.py bdist_wininst --install-script=ipython_win_post_install.py
28 ./setup.py bdist_wininst --install-script=ipython_win_post_install.py
29
29
30 # Change name so retarded Vista runs the installer correctly
30 # Change name so retarded Vista runs the installer correctly
31 rename 's/win32/win32-setup/' $ipdir/dist/*.exe
31 rename 's/win32/win32-setup/' $ipdir/dist/*.exe
General Comments 0
You need to be logged in to leave comments. Login now