##// END OF EJS Templates
Running the wx frontend no longer imports twisted. Hurray.
Gael Varoquaux -
Show More
@@ -1,352 +1,351 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 try:
27 try:
28 from zope.interface import Interface, Attribute, implements, classProvides
28 from zope.interface import Interface, Attribute, implements, classProvides
29 except ImportError:
29 except ImportError:
30 #zope.interface is not available
30 #zope.interface is not available
31 Interface = object
31 Interface = object
32 def Attribute(name, doc): pass
32 def Attribute(name, doc): pass
33 def implements(interface): pass
33 def implements(interface): pass
34 def classProvides(interface): pass
34 def classProvides(interface): pass
35
35
36 from IPython.kernel.core.history import FrontEndHistory
36 from IPython.kernel.core.history import FrontEndHistory
37 from IPython.kernel.core.util import Bunch
37 from IPython.kernel.core.util import Bunch
38 from IPython.kernel.engineservice import IEngineCore
39
38
40 ##############################################################################
39 ##############################################################################
41 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
40 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
42 # not
41 # not
43
42
44 rc = Bunch()
43 rc = Bunch()
45 rc.prompt_in1 = r'In [$number]: '
44 rc.prompt_in1 = r'In [$number]: '
46 rc.prompt_in2 = r'...'
45 rc.prompt_in2 = r'...'
47 rc.prompt_out = r'Out [$number]: '
46 rc.prompt_out = r'Out [$number]: '
48
47
49 ##############################################################################
48 ##############################################################################
50
49
51 class IFrontEndFactory(Interface):
50 class IFrontEndFactory(Interface):
52 """Factory interface for frontends."""
51 """Factory interface for frontends."""
53
52
54 def __call__(engine=None, history=None):
53 def __call__(engine=None, history=None):
55 """
54 """
56 Parameters:
55 Parameters:
57 interpreter : IPython.kernel.engineservice.IEngineCore
56 interpreter : IPython.kernel.engineservice.IEngineCore
58 """
57 """
59
58
60 pass
59 pass
61
60
62
61
63
62
64 class IFrontEnd(Interface):
63 class IFrontEnd(Interface):
65 """Interface for frontends. All methods return t.i.d.Deferred"""
64 """Interface for frontends. All methods return t.i.d.Deferred"""
66
65
67 Attribute("input_prompt_template", "string.Template instance\
66 Attribute("input_prompt_template", "string.Template instance\
68 substituteable with execute result.")
67 substituteable with execute result.")
69 Attribute("output_prompt_template", "string.Template instance\
68 Attribute("output_prompt_template", "string.Template instance\
70 substituteable with execute result.")
69 substituteable with execute result.")
71 Attribute("continuation_prompt_template", "string.Template instance\
70 Attribute("continuation_prompt_template", "string.Template instance\
72 substituteable with execute result.")
71 substituteable with execute result.")
73
72
74 def update_cell_prompt(result, blockID=None):
73 def update_cell_prompt(result, blockID=None):
75 """Subclass may override to update the input prompt for a block.
74 """Subclass may override to update the input prompt for a block.
76 Since this method will be called as a
75 Since this method will be called as a
77 twisted.internet.defer.Deferred's callback/errback,
76 twisted.internet.defer.Deferred's callback/errback,
78 implementations should return result when finished.
77 implementations should return result when finished.
79
78
80 Result is a result dict in case of success, and a
79 Result is a result dict in case of success, and a
81 twisted.python.util.failure.Failure in case of an error
80 twisted.python.util.failure.Failure in case of an error
82 """
81 """
83
82
84 pass
83 pass
85
84
86
85
87 def render_result(result):
86 def render_result(result):
88 """Render the result of an execute call. Implementors may choose the
87 """Render the result of an execute call. Implementors may choose the
89 method of rendering.
88 method of rendering.
90 For example, a notebook-style frontend might render a Chaco plot
89 For example, a notebook-style frontend might render a Chaco plot
91 inline.
90 inline.
92
91
93 Parameters:
92 Parameters:
94 result : dict (result of IEngineBase.execute )
93 result : dict (result of IEngineBase.execute )
95 blockID = result['blockID']
94 blockID = result['blockID']
96
95
97 Result:
96 Result:
98 Output of frontend rendering
97 Output of frontend rendering
99 """
98 """
100
99
101 pass
100 pass
102
101
103 def render_error(failure):
102 def render_error(failure):
104 """Subclasses must override to render the failure. Since this method
103 """Subclasses must override to render the failure. Since this method
105 will be called as a twisted.internet.defer.Deferred's callback,
104 will be called as a twisted.internet.defer.Deferred's callback,
106 implementations should return result when finished.
105 implementations should return result when finished.
107
106
108 blockID = failure.blockID
107 blockID = failure.blockID
109 """
108 """
110
109
111 pass
110 pass
112
111
113
112
114 def input_prompt(number=''):
113 def input_prompt(number=''):
115 """Returns the input prompt by subsituting into
114 """Returns the input prompt by subsituting into
116 self.input_prompt_template
115 self.input_prompt_template
117 """
116 """
118 pass
117 pass
119
118
120 def output_prompt(number=''):
119 def output_prompt(number=''):
121 """Returns the output prompt by subsituting into
120 """Returns the output prompt by subsituting into
122 self.output_prompt_template
121 self.output_prompt_template
123 """
122 """
124
123
125 pass
124 pass
126
125
127 def continuation_prompt():
126 def continuation_prompt():
128 """Returns the continuation prompt by subsituting into
127 """Returns the continuation prompt by subsituting into
129 self.continuation_prompt_template
128 self.continuation_prompt_template
130 """
129 """
131
130
132 pass
131 pass
133
132
134 def is_complete(block):
133 def is_complete(block):
135 """Returns True if block is complete, False otherwise."""
134 """Returns True if block is complete, False otherwise."""
136
135
137 pass
136 pass
138
137
139 def compile_ast(block):
138 def compile_ast(block):
140 """Compiles block to an _ast.AST"""
139 """Compiles block to an _ast.AST"""
141
140
142 pass
141 pass
143
142
144
143
145 def get_history_previous(currentBlock):
144 def get_history_previous(currentBlock):
146 """Returns the block previous in the history. Saves currentBlock if
145 """Returns the block previous in the history. Saves currentBlock if
147 the history_cursor is currently at the end of the input history"""
146 the history_cursor is currently at the end of the input history"""
148 pass
147 pass
149
148
150 def get_history_next():
149 def get_history_next():
151 """Returns the next block in the history."""
150 """Returns the next block in the history."""
152
151
153 pass
152 pass
154
153
155
154
156 class FrontEndBase(object):
155 class FrontEndBase(object):
157 """
156 """
158 FrontEndBase manages the state tasks for a CLI frontend:
157 FrontEndBase manages the state tasks for a CLI frontend:
159 - Input and output history management
158 - Input and output history management
160 - Input/continuation and output prompt generation
159 - Input/continuation and output prompt generation
161
160
162 Some issues (due to possibly unavailable engine):
161 Some issues (due to possibly unavailable engine):
163 - How do we get the current cell number for the engine?
162 - How do we get the current cell number for the engine?
164 - How do we handle completions?
163 - How do we handle completions?
165 """
164 """
166
165
167 history_cursor = 0
166 history_cursor = 0
168
167
169 current_indent_level = 0
168 current_indent_level = 0
170
169
171
170
172 input_prompt_template = string.Template(rc.prompt_in1)
171 input_prompt_template = string.Template(rc.prompt_in1)
173 output_prompt_template = string.Template(rc.prompt_out)
172 output_prompt_template = string.Template(rc.prompt_out)
174 continuation_prompt_template = string.Template(rc.prompt_in2)
173 continuation_prompt_template = string.Template(rc.prompt_in2)
175
174
176 def __init__(self, shell=None, history=None):
175 def __init__(self, shell=None, history=None):
177 self.shell = shell
176 self.shell = shell
178 if history is None:
177 if history is None:
179 self.history = FrontEndHistory(input_cache=[''])
178 self.history = FrontEndHistory(input_cache=[''])
180 else:
179 else:
181 self.history = history
180 self.history = history
182
181
183
182
184 def input_prompt(self, number=''):
183 def input_prompt(self, number=''):
185 """Returns the current input prompt
184 """Returns the current input prompt
186
185
187 It would be great to use ipython1.core.prompts.Prompt1 here
186 It would be great to use ipython1.core.prompts.Prompt1 here
188 """
187 """
189 return self.input_prompt_template.safe_substitute({'number':number})
188 return self.input_prompt_template.safe_substitute({'number':number})
190
189
191
190
192 def continuation_prompt(self):
191 def continuation_prompt(self):
193 """Returns the current continuation prompt"""
192 """Returns the current continuation prompt"""
194
193
195 return self.continuation_prompt_template.safe_substitute()
194 return self.continuation_prompt_template.safe_substitute()
196
195
197 def output_prompt(self, number=''):
196 def output_prompt(self, number=''):
198 """Returns the output prompt for result"""
197 """Returns the output prompt for result"""
199
198
200 return self.output_prompt_template.safe_substitute({'number':number})
199 return self.output_prompt_template.safe_substitute({'number':number})
201
200
202
201
203 def is_complete(self, block):
202 def is_complete(self, block):
204 """Determine if block is complete.
203 """Determine if block is complete.
205
204
206 Parameters
205 Parameters
207 block : string
206 block : string
208
207
209 Result
208 Result
210 True if block can be sent to the engine without compile errors.
209 True if block can be sent to the engine without compile errors.
211 False otherwise.
210 False otherwise.
212 """
211 """
213
212
214 try:
213 try:
215 ast = self.compile_ast(block)
214 ast = self.compile_ast(block)
216 except:
215 except:
217 return False
216 return False
218
217
219 lines = block.split('\n')
218 lines = block.split('\n')
220 return (len(lines)==1 or str(lines[-1])=='')
219 return (len(lines)==1 or str(lines[-1])=='')
221
220
222
221
223 def compile_ast(self, block):
222 def compile_ast(self, block):
224 """Compile block to an AST
223 """Compile block to an AST
225
224
226 Parameters:
225 Parameters:
227 block : str
226 block : str
228
227
229 Result:
228 Result:
230 AST
229 AST
231
230
232 Throws:
231 Throws:
233 Exception if block cannot be compiled
232 Exception if block cannot be compiled
234 """
233 """
235
234
236 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
235 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
237
236
238
237
239 def execute(self, block, blockID=None):
238 def execute(self, block, blockID=None):
240 """Execute the block and return the result.
239 """Execute the block and return the result.
241
240
242 Parameters:
241 Parameters:
243 block : {str, AST}
242 block : {str, AST}
244 blockID : any
243 blockID : any
245 Caller may provide an ID to identify this block.
244 Caller may provide an ID to identify this block.
246 result['blockID'] := blockID
245 result['blockID'] := blockID
247
246
248 Result:
247 Result:
249 Deferred result of self.interpreter.execute
248 Deferred result of self.interpreter.execute
250 """
249 """
251
250
252 if(not self.is_complete(block)):
251 if(not self.is_complete(block)):
253 raise Exception("Block is not compilable")
252 raise Exception("Block is not compilable")
254
253
255 if(blockID == None):
254 if(blockID == None):
256 blockID = uuid.uuid4() #random UUID
255 blockID = uuid.uuid4() #random UUID
257
256
258 try:
257 try:
259 result = self.shell.execute(block)
258 result = self.shell.execute(block)
260 except Exception,e:
259 except Exception,e:
261 e = self._add_block_id_for_failure(e, blockID=blockID)
260 e = self._add_block_id_for_failure(e, blockID=blockID)
262 e = self.update_cell_prompt(e, blockID=blockID)
261 e = self.update_cell_prompt(e, blockID=blockID)
263 e = self.render_error(e)
262 e = self.render_error(e)
264 else:
263 else:
265 result = self._add_block_id_for_result(result, blockID=blockID)
264 result = self._add_block_id_for_result(result, blockID=blockID)
266 result = self.update_cell_prompt(result, blockID=blockID)
265 result = self.update_cell_prompt(result, blockID=blockID)
267 result = self.render_result(result)
266 result = self.render_result(result)
268
267
269 return result
268 return result
270
269
271
270
272 def _add_block_id_for_result(self, result, blockID):
271 def _add_block_id_for_result(self, result, blockID):
273 """Add the blockID to result or failure. Unfortunatley, we have to
272 """Add the blockID to result or failure. Unfortunatley, we have to
274 treat failures differently than result dicts.
273 treat failures differently than result dicts.
275 """
274 """
276
275
277 result['blockID'] = blockID
276 result['blockID'] = blockID
278
277
279 return result
278 return result
280
279
281 def _add_block_id_for_failure(self, failure, blockID):
280 def _add_block_id_for_failure(self, failure, blockID):
282 """_add_block_id_for_failure"""
281 """_add_block_id_for_failure"""
283
282
284 failure.blockID = blockID
283 failure.blockID = blockID
285 return failure
284 return failure
286
285
287
286
288 def _add_history(self, result, block=None):
287 def _add_history(self, result, block=None):
289 """Add block to the history"""
288 """Add block to the history"""
290
289
291 assert(block != None)
290 assert(block != None)
292 self.history.add_items([block])
291 self.history.add_items([block])
293 self.history_cursor += 1
292 self.history_cursor += 1
294
293
295 return result
294 return result
296
295
297
296
298 def get_history_previous(self, currentBlock):
297 def get_history_previous(self, currentBlock):
299 """ Returns previous history string and decrement history cursor.
298 """ Returns previous history string and decrement history cursor.
300 """
299 """
301 command = self.history.get_history_item(self.history_cursor - 1)
300 command = self.history.get_history_item(self.history_cursor - 1)
302
301
303 if command is not None:
302 if command is not None:
304 if(self.history_cursor == len(self.history.input_cache)):
303 if(self.history_cursor == len(self.history.input_cache)):
305 self.history.input_cache[self.history_cursor] = currentBlock
304 self.history.input_cache[self.history_cursor] = currentBlock
306 self.history_cursor -= 1
305 self.history_cursor -= 1
307 return command
306 return command
308
307
309
308
310 def get_history_next(self):
309 def get_history_next(self):
311 """ Returns next history string and increment history cursor.
310 """ Returns next history string and increment history cursor.
312 """
311 """
313 command = self.history.get_history_item(self.history_cursor+1)
312 command = self.history.get_history_item(self.history_cursor+1)
314
313
315 if command is not None:
314 if command is not None:
316 self.history_cursor += 1
315 self.history_cursor += 1
317 return command
316 return command
318
317
319 ###
318 ###
320 # Subclasses probably want to override these methods...
319 # Subclasses probably want to override these methods...
321 ###
320 ###
322
321
323 def update_cell_prompt(self, result, blockID=None):
322 def update_cell_prompt(self, result, blockID=None):
324 """Subclass may override to update the input prompt for a block.
323 """Subclass may override to update the input prompt for a block.
325 Since this method will be called as a
324 Since this method will be called as a
326 twisted.internet.defer.Deferred's callback, implementations should
325 twisted.internet.defer.Deferred's callback, implementations should
327 return result when finished.
326 return result when finished.
328 """
327 """
329
328
330 return result
329 return result
331
330
332
331
333 def render_result(self, result):
332 def render_result(self, result):
334 """Subclasses must override to render result. Since this method will
333 """Subclasses must override to render result. Since this method will
335 be called as a twisted.internet.defer.Deferred's callback,
334 be called as a twisted.internet.defer.Deferred's callback,
336 implementations should return result when finished.
335 implementations should return result when finished.
337 """
336 """
338
337
339 return result
338 return result
340
339
341
340
342 def render_error(self, failure):
341 def render_error(self, failure):
343 """Subclasses must override to render the failure. Since this method
342 """Subclasses must override to render the failure. Since this method
344 will be called as a twisted.internet.defer.Deferred's callback,
343 will be called as a twisted.internet.defer.Deferred's callback,
345 implementations should return result when finished.
344 implementations should return result when finished.
346 """
345 """
347
346
348 return failure
347 return failure
349
348
350
349
351
350
352
351
General Comments 0
You need to be logged in to leave comments. Login now