##// END OF EJS Templates
for code review
Barry Wark -
Show More
@@ -1,254 +1,295 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 FrontEndBase: Base classes for frontends.
3 frontendbase provides an interface and base class for GUI frontends for IPython.kernel/IPython.kernel.core.
4
4
5 Todo:
5 Frontend implementations will likely want to subclass FrontEndBase.
6 - synchronous and asynchronous interfaces
6
7 - adapter to add async to FrontEndBase
7 Author: Barry Wark
8 """
8 """
9 __docformat__ = "restructuredtext en"
9 __docformat__ = "restructuredtext en"
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 Barry Wark <barrywark at gmail _dot_ com>
12 # Copyright (C) 2008 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 import string
21 import string
22 import uuid
22 import uuid
23
23
24
24
25 from IPython.kernel.core.history import FrontEndHistory
25 from IPython.kernel.core.history import FrontEndHistory
26 from IPython.kernel.core.util import Bunch
26 from IPython.kernel.core.util import Bunch
27
27
28 from IPython.kernel.engineservice import IEngineCore
28 from IPython.kernel.engineservice import IEngineCore
29
29
30 import zope.interface as zi
30 import zope.interface as zi
31
31
32 import _ast
32 import _ast
33
33
34 ##############################################################################
34 ##############################################################################
35 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
35 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
36 # not
36 # not
37
37
38 rc = Bunch()
38 rc = Bunch()
39 rc.prompt_in1 = r'In [$number]: '
39 rc.prompt_in1 = r'In [$number]: '
40 rc.prompt_in2 = r'...'
40 rc.prompt_in2 = r'...'
41 rc.prompt_out = r'Out [$number]: '
41 rc.prompt_out = r'Out [$number]: '
42
42
43 ##############################################################################
43 ##############################################################################
44
44
45 class IFrontEnd(zi.Interface):
45 class IFrontEndFactory(zi.Interface):
46 """Interface for frontends. All methods return t.i.d.Deferred"""
46 """Factory interface for frontends."""
47
47
48 zi.Attribute("input_prompt_template", "string.Template instance substituteable with execute result.")
48 def __call__(engine=None, history=None):
49 zi.Attribute("output_prompt_template", "string.Template instance substituteable with execute result.")
50 zi.Attribute("continuation_prompt_template", "string.Template instance substituteable with execute result.")
51
52 def __init__(engine=None, history=None):
53 """
49 """
54 Parameters:
50 Parameters:
55 interpreter : IPython.kernel.engineservice.IEngineCore
51 interpreter : IPython.kernel.engineservice.IEngineCore
56 """
52 """
53
57 pass
54 pass
55
56
57
58 class IFrontEnd(zi.Interface):
59 """Interface for frontends. All methods return t.i.d.Deferred"""
60
61 zi.Attribute("input_prompt_template", "string.Template instance substituteable with execute result.")
62 zi.Attribute("output_prompt_template", "string.Template instance substituteable with execute result.")
63 zi.Attribute("continuation_prompt_template", "string.Template instance substituteable with execute result.")
58
64
59 def update_cell_prompt(self, result):
65 def update_cell_prompt(self, result):
60 """Subclass may override to update the input prompt for a block.
66 """Subclass may override to update the input prompt for a block.
61 Since this method will be called as a twisted.internet.defer.Deferred's callback,
67 Since this method will be called as a twisted.internet.defer.Deferred's callback,
62 implementations should return result when finished."""
68 implementations should return result when finished."""
63
69
64 return result
70 pass
65
71
66 def render_result(self, result):
72 def render_result(self, result):
67 """Render the result of an execute call. Implementors may choose the method of rendering.
73 """Render the result of an execute call. Implementors may choose the method of rendering.
68 For example, a notebook-style frontend might render a Chaco plot inline.
74 For example, a notebook-style frontend might render a Chaco plot inline.
69
75
70 Parameters:
76 Parameters:
71 result : dict (result of IEngineBase.execute )
77 result : dict (result of IEngineBase.execute )
72
78
73 Result:
79 Result:
74 Output of frontend rendering
80 Output of frontend rendering
75 """
81 """
76
82
77 return result
83 pass
78
84
79 def render_error(self, failure):
85 def render_error(self, failure):
80 """Subclasses must override to render the failure. Since this method will be called as a
86 """Subclasses must override to render the failure. Since this method will be called as a
81 twisted.internet.defer.Deferred's callback, implementations should return result
87 twisted.internet.defer.Deferred's callback, implementations should return result
82 when finished."""
88 when finished."""
83
89
84 return failure
90 pass
91
92
93 def inputPrompt(result={}):
94 """Returns the input prompt by subsituting into self.input_prompt_template"""
95 pass
96
97 def outputPrompt(result):
98 """Returns the output prompt by subsituting into self.output_prompt_template"""
99
100 pass
101
102 def continuationPrompt():
103 """Returns the continuation prompt by subsituting into self.continuation_prompt_template"""
104
105 pass
106
107 def is_complete(block):
108 """Returns True if block is complete, False otherwise."""
109
110 pass
111
112 def compile_ast(block):
113 """Compiles block to an _ast.AST"""
114
115 pass
116
117
118 def get_history_item_previous(currentBlock):
119 """Returns the block previous in the history."""
120 pass
121
122 def get_history_item_next(currentBlock):
123 """Returns the next block in the history."""
124
125 pass
85
126
86 # TODO: finish interface
87
127
88 class FrontEndBase(object):
128 class FrontEndBase(object):
89 """
129 """
90 FrontEndBase manages the state tasks for a CLI frontend:
130 FrontEndBase manages the state tasks for a CLI frontend:
91 - Input and output history management
131 - Input and output history management
92 - Input/continuation and output prompt generation
132 - Input/continuation and output prompt generation
93
133
94 Some issues (due to possibly unavailable engine):
134 Some issues (due to possibly unavailable engine):
95 - How do we get the current cell number for the engine?
135 - How do we get the current cell number for the engine?
96 - How do we handle completions?
136 - How do we handle completions?
97 """
137 """
98
138
99 zi.implements(IFrontEnd)
139 zi.implements(IFrontEnd)
140 zi.classProvides(IFrontEndFactory)
100
141
101 history_cursor = 0
142 history_cursor = 0
102
143
103 current_indent_level = 0
144 current_indent_level = 0
104
145
105
146
106 input_prompt_template = string.Template(rc.prompt_in1)
147 input_prompt_template = string.Template(rc.prompt_in1)
107 output_prompt_template = string.Template(rc.prompt_out)
148 output_prompt_template = string.Template(rc.prompt_out)
108 continuation_prompt_template = string.Template(rc.prompt_in2)
149 continuation_prompt_template = string.Template(rc.prompt_in2)
109
150
110 def __init__(self, engine=None, history=None):
151 def __init__(self, engine=None, history=None):
111 assert(engine==None or IEngineCore.providedBy(engine))
152 assert(engine==None or IEngineCore.providedBy(engine))
112 self.engine = IEngineCore(engine)
153 self.engine = IEngineCore(engine)
113 if history is None:
154 if history is None:
114 self.history = FrontEndHistory(input_cache=[''])
155 self.history = FrontEndHistory(input_cache=[''])
115 else:
156 else:
116 self.history = history
157 self.history = history
117
158
118
159
119 def inputPrompt(self, result={}):
160 def inputPrompt(self, result={}):
120 """Returns the current input prompt
161 """Returns the current input prompt
121
162
122 It would be great to use ipython1.core.prompts.Prompt1 here
163 It would be great to use ipython1.core.prompts.Prompt1 here
123 """
164 """
124
165
125 result.setdefault('number','')
166 result.setdefault('number','')
126
167
127 return self.input_prompt_template.safe_substitute(result)
168 return self.input_prompt_template.safe_substitute(result)
128
169
129
170
130 def continuationPrompt(self):
171 def continuationPrompt(self):
131 """Returns the current continuation prompt"""
172 """Returns the current continuation prompt"""
132
173
133 return self.continuation_prompt_template.safe_substitute()
174 return self.continuation_prompt_template.safe_substitute()
134
175
135 def outputPrompt(self, result):
176 def outputPrompt(self, result):
136 """Returns the output prompt for result"""
177 """Returns the output prompt for result"""
137
178
138 return self.output_prompt_template.safe_substitute(result)
179 return self.output_prompt_template.safe_substitute(result)
139
180
140
181
141 def is_complete(self, block):
182 def is_complete(self, block):
142 """Determine if block is complete.
183 """Determine if block is complete.
143
184
144 Parameters
185 Parameters
145 block : string
186 block : string
146
187
147 Result
188 Result
148 True if block can be sent to the engine without compile errors.
189 True if block can be sent to the engine without compile errors.
149 False otherwise.
190 False otherwise.
150 """
191 """
151
192
152 try:
193 try:
153 self.compile_ast(block)
194 self.compile_ast(block)
154 return True
195 return True
155 except:
196 except:
156 return False
197 return False
157
198
158
199
159 def compile_ast(self, block):
200 def compile_ast(self, block):
160 """Compile block to an AST
201 """Compile block to an AST
161
202
162 Parameters:
203 Parameters:
163 block : str
204 block : str
164
205
165 Result:
206 Result:
166 AST
207 AST
167
208
168 Throws:
209 Throws:
169 Exception if block cannot be compiled
210 Exception if block cannot be compiled
170 """
211 """
171
212
172 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
213 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
173
214
174
215
175 def execute(self, block, blockID=None):
216 def execute(self, block, blockID=None):
176 """Execute the block and return result.
217 """Execute the block and return result.
177
218
178 Parameters:
219 Parameters:
179 block : {str, AST}
220 block : {str, AST}
180 blockID : any
221 blockID : any
181 Caller may provide an ID to identify this block. result['blockID'] := blockID
222 Caller may provide an ID to identify this block. result['blockID'] := blockID
182
223
183 Result:
224 Result:
184 Deferred result of self.interpreter.execute
225 Deferred result of self.interpreter.execute
185 """
226 """
186 # if(not isinstance(block, _ast.AST)):
227 # if(not isinstance(block, _ast.AST)):
187 # block = self.compile_ast(block)
228 # block = self.compile_ast(block)
188
229
189 if(blockID == None):
230 if(blockID == None):
190 blockID = uuid.uuid4() #random UUID
231 blockID = uuid.uuid4() #random UUID
191
232
192 d = self.engine.execute(block)
233 d = self.engine.execute(block)
193 d.addCallback(self._add_block_id, blockID)
234 d.addCallback(self._add_block_id, blockID)
194 d.addCallback(self.update_cell_prompt)
235 d.addCallback(self.update_cell_prompt)
195 d.addCallbacks(self.render_result, errback=self.render_error)
236 d.addCallbacks(self.render_result, errback=self.render_error)
196
237
197 return d
238 return d
198
239
199 def _add_block_id(self, result, blockID):
240 def _add_block_id(self, result, blockID):
200 """add_block_id"""
241 """add_block_id"""
201
242
202 result['blockID'] = blockID
243 result['blockID'] = blockID
203
244
204 return result
245 return result
205
246
206
247
207 def get_history_item_previous(self, current_block):
248 def get_history_item_previous(self, currentBlock):
208 """ Returns previous history string and decrement history cursor.
249 """ Returns previous history string and decrement history cursor.
209 """
250 """
210 command = self.history.get_history_item(self.history_cursor - 1)
251 command = self.history.get_history_item(self.history_cursor - 1)
211 if command is not None:
252 if command is not None:
212 self.history.input_cache[self.history_cursor] = current_block
253 self.history.input_cache[self.history_cursor] = currentBlock
213 self.history_cursor -= 1
254 self.history_cursor -= 1
214 return command
255 return command
215
256
216
257
217 def get_history_item_next(self, current_block):
258 def get_history_item_next(self, currentBlock):
218 """ Returns next history string and increment history cursor.
259 """ Returns next history string and increment history cursor.
219 """
260 """
220 command = self.history.get_history_item(self.history_cursor + 1)
261 command = self.history.get_history_item(self.history_cursor + 1)
221 if command is not None:
262 if command is not None:
222 self.history.input_cache[self.history_cursor] = current_block
263 self.history.input_cache[self.history_cursor] = currentBlock
223 self.history_cursor += 1
264 self.history_cursor += 1
224 return command
265 return command
225
266
226 ###
267 ###
227 # Subclasses probably want to override these methods...
268 # Subclasses probably want to override these methods...
228 ###
269 ###
229
270
230 def update_cell_prompt(self, result):
271 def update_cell_prompt(self, result):
231 """Subclass may override to update the input prompt for a block.
272 """Subclass may override to update the input prompt for a block.
232 Since this method will be called as a twisted.internet.defer.Deferred's callback,
273 Since this method will be called as a twisted.internet.defer.Deferred's callback,
233 implementations should return result when finished."""
274 implementations should return result when finished."""
234
275
235 return result
276 return result
236
277
237
278
238 def render_result(self, result):
279 def render_result(self, result):
239 """Subclasses must override to render result. Since this method will be called as a
280 """Subclasses must override to render result. Since this method will be called as a
240 twisted.internet.defer.Deferred's callback, implementations should return result
281 twisted.internet.defer.Deferred's callback, implementations should return result
241 when finished."""
282 when finished."""
242
283
243 return result
284 return result
244
285
245
286
246 def render_error(self, failure):
287 def render_error(self, failure):
247 """Subclasses must override to render the failure. Since this method will be called as a
288 """Subclasses must override to render the failure. Since this method will be called as a
248 twisted.internet.defer.Deferred's callback, implementations should return result
289 twisted.internet.defer.Deferred's callback, implementations should return result
249 when finished."""
290 when finished."""
250
291
251 return failure
292 return failure
252
293
253
294
254
295
General Comments 0
You need to be logged in to leave comments. Login now