~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to scripts/python-console

  • Committer: dcoles
  • Date: 2008-08-21 03:11:19 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1037
Console: More clean up work to try and prevent coding errors from getting cmdQ 
and lineQ out of sync (that's very bad!). We now use functions to handle each 
request and the top level loop does the queue management. In short, don't mess 
directly with the queues unless you know what you're doing!

Also added in a ExistingConsole class (extends Console) which can be used to 
connect up an existing console process rather than a new one with most of the 
functionality of the Console class. (Basically you can't start a new console)

Show diffs side-by-side

added added

removed removed

Lines of Context:
51
51
    def readline(self):
52
52
        self.cmdQ.put({"input":None})
53
53
        expiry.ping()
54
 
        ln = self.lineQ.get()
55
 
        if 'chat' in ln:
56
 
            return ln['chat']
57
 
        if 'interrupt' in ln:
 
54
        action, params = self.lineQ.get()
 
55
        if action == 'chat':
 
56
            return params
 
57
        elif action == 'interrupt':
58
58
            raise Interrupt()
59
59
 
60
60
class StdoutToWeb(object):
94
94
            (blk, count) = self._trim_incomplete_final(self.remainder[:512])
95
95
            self.cmdQ.put({"output":blk.decode('utf-8', 'replace')})
96
96
            expiry.ping()
97
 
            ln = self.lineQ.get()
 
97
            action, params = self.lineQ.get()
98
98
            self.remainder = self.remainder[512 - count:]
99
99
 
100
100
        # Finally, split the remainder up into lines, and ship all the
108
108
            text = "\n".join(lines)
109
109
            self.cmdQ.put({"output":text.decode('utf-8', 'replace')})
110
110
            expiry.ping()
111
 
            ln = self.lineQ.get()
112
 
            if 'interrupt' in ln:
 
111
            action, params = self.lineQ.get()
 
112
            if action == 'interrupt':
113
113
                raise Interrupt()
114
114
 
115
115
    def flush(self):
117
117
            (out, count) = self._trim_incomplete_final(self.remainder)
118
118
            self.cmdQ.put({"output":out.decode('utf-8', 'replace')})
119
119
            expiry.ping()
120
 
            ln = self.lineQ.get()
 
120
            action, params = self.lineQ.get()
121
121
            # Leave incomplete characters in the buffer.
122
122
            # Yes, this does mean that an incomplete character will be left
123
123
            # off the end, but we discussed this and it was deemed best.
124
124
            self.remainder = self.remainder[len(self.remainder)-count:]
125
 
            if 'interrupt' in ln:
 
125
            if action == 'interrupt':
126
126
                raise Interrupt()
127
127
 
128
128
class WebIO(object):
129
129
    """Provides a file like interface to the Web front end of the console.
130
130
    You may print text to the console using write(), flush any buffered output 
131
131
    using flush(), or request text from the console using readline()"""
 
132
    # FIXME: Clean up the whole stdin, stdout, stderr mess. We really need to 
 
133
    # be able to deal with the streams individually.
132
134
    
133
135
    def __init__(self, cmdQ, lineQ):
134
136
        self.cmdQ = cmdQ
155
157
 
156
158
    def execCmd(self, cmd):
157
159
        try:
158
 
            sys.stdin = self.webio
159
 
            sys.stdout = self.webio
160
 
            sys.stderr = self.webio
161
160
            # We don't expect a return value - 'single' symbol prints it.
162
161
            self.eval(cmd)
 
162
            self.curr_cmd = ''
163
163
            self.webio.flush()
164
 
            self.cmdQ.put({"okay": None})
165
 
            self.curr_cmd = ''
 
164
            return({"okay": None})
166
165
        except:
167
 
            tb = format_exc_start(start=1)
 
166
            self.curr_cmd = ''
168
167
            self.webio.flush()
169
 
            self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
170
 
            self.curr_cmd = ''
 
168
            tb = format_exc_start(start=2)
 
169
            return({"exc": ''.join(tb).decode('utf-8', 'replace')})
171
170
 
172
171
    def run(self):
 
172
        # Set up global space and partial command buffer
173
173
        self.globs = {}
174
174
        self.curr_cmd = ''
175
175
 
 
176
        # Set up I/O to use web interface
 
177
        sys.stdin = self.webio
 
178
        sys.stdout = self.webio
 
179
        sys.stderr = self.webio
 
180
 
 
181
        # Handlers for each action
 
182
        actions = {
 
183
            'chat': self.handle_chat,
 
184
            'block': self.handle_block,
 
185
            'globals': self.handle_globals,
 
186
            'call': self.handle_call,
 
187
            'execute': self.handle_execute,
 
188
            'setvars': self.handle_setvars,
 
189
            }
 
190
 
 
191
        # Run the processing loop
176
192
        while True:
177
 
            ln = self.lineQ.get()
178
 
            if 'chat' in ln:
179
 
                if self.curr_cmd == '':
180
 
                    self.curr_cmd = ln['chat']
181
 
                else:
182
 
                    self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
183
 
                try:
184
 
                    cmd = codeop.compile_command(self.curr_cmd, '<web session>')
185
 
                    if cmd is None:
186
 
                        # The command was incomplete,
187
 
                        # so send back a None, so the
188
 
                        # client can print a '...'
189
 
                        self.cmdQ.put({"more":None})
190
 
                    else:
191
 
                        self.execCmd(cmd)
192
 
                except:
193
 
                    tb = format_exc_start(start=3)
194
 
                    self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
195
 
                    self.webio.flush()
196
 
                    self.curr_cmd = ''
197
 
            elif 'block' in ln:
198
 
                # throw away a partial command.
199
 
                try:
200
 
                    cmd = compile(ln['block'], "<web session>", 'exec');
201
 
                    self.execCmd(cmd)
202
 
                except:
 
193
            action, params = self.lineQ.get()
 
194
            try:
 
195
                response = actions[action](params)
 
196
            except Exception, e:
 
197
                response = {'error': repr(e)}
 
198
            finally:
 
199
                self.cmdQ.put(response)
 
200
                   
 
201
    def handle_chat(self, params):
 
202
        # Set up the partial cmd buffer
 
203
        if self.curr_cmd == '':
 
204
            self.curr_cmd = params
 
205
        else:
 
206
            self.curr_cmd = self.curr_cmd + '\n' + params
 
207
 
 
208
        # Try to execute the buffer
 
209
        try:
 
210
            cmd = codeop.compile_command(self.curr_cmd, '<web session>')
 
211
            if cmd is None:
 
212
                # The command was incomplete, so send back a None, so the              
 
213
                # client can print a '...'
 
214
                return({"more":None})
 
215
            else:
 
216
                return(self.execCmd(cmd))
 
217
        except:
 
218
            # Clear any partial command
 
219
            self.curr_cmd = ''
 
220
            # Flush the output buffers
 
221
            sys.stderr.flush()
 
222
            sys.stdout.flush()
 
223
            # Return the exception
 
224
            tb = format_exc_start(start=3)
 
225
            return({"exc": ''.join(tb).decode('utf-8', 'replace')})
 
226
 
 
227
    def handle_block(self, params):
 
228
        # throw away any partial command.
 
229
        self.curr_cmd = ''
 
230
 
 
231
        # Try to execute a complete block of code
 
232
        try:
 
233
            cmd = compile(params, "<web session>", 'exec');
 
234
            return(self.execCmd(cmd))
 
235
        except:
 
236
            # Flush the output buffers
 
237
            sys.stderr.flush()
 
238
            sys.stdout.flush()
 
239
            # Return the exception
 
240
            tb = format_exc_start(start=1)
 
241
            return({"exc": ''.join(tb).decode('utf-8', 'replace')})
 
242
 
 
243
    def handle_globals(self, params):
 
244
        # Unpickle the new space (if provided)
 
245
        if isinstance(params, dict):
 
246
            self.globs = {}
 
247
            for g in params:
 
248
                try:
 
249
                    self.globs[g] = cPickle.loads(params[g])
 
250
                except:
 
251
                    pass
 
252
 
 
253
        # Return the current globals
 
254
        return({'globals': flatten(self.globs)})
 
255
 
 
256
    def handle_call(self, params):
 
257
        call = {}
 
258
        
 
259
        # throw away any partial command.
 
260
        self.curr_cmd = ''
 
261
 
 
262
        if isinstance(params, dict):
 
263
            try:
 
264
                # Expand parameters
 
265
                if isinstance(params['args'], list):
 
266
                    args = map(self.eval, params['args'])
 
267
                else:
 
268
                    args = []
 
269
                if isinstance(params['kwargs'], dict):
 
270
                    kwargs = {}
 
271
                    for kwarg in params['kwargs']:
 
272
                        kwargs[kwarg] = self.eval(
 
273
                            params['kwargs'][kwarg])
 
274
                else:
 
275
                    kwargs = {}
 
276
 
 
277
                # Run the fuction
 
278
                function = self.eval(params['function'])
 
279
                try:
 
280
                    call['result'] = function(*args, **kwargs)
 
281
                except Exception, e:
 
282
                    exception = {}
203
283
                    tb = format_exc_start(start=1)
204
 
                    self.webio.flush()
205
 
                    self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
206
 
                    self.curr_cmd = ''
207
 
            elif 'globals' in ln:
208
 
                # Unpickle the new space (if provided)
209
 
                if isinstance(ln['globals'],dict):
210
 
                    self.globs = {}
211
 
                    for g in ln['globals']:
212
 
                        try:
213
 
                            self.globs[g] = cPickle.loads(ln['globals'][g])
214
 
                        except:
215
 
                            pass
216
 
 
217
 
                # Return the current globals
218
 
                self.cmdQ.put({'globals': flatten(self.globs)})
219
 
            elif 'call' in ln:
220
 
                call = {}
221
 
                sys.stdin = self.webio
222
 
                sys.stdout = self.webio
223
 
                sys.stderr = self.webio
224
 
 
225
 
                if isinstance(ln['call'], dict):
226
 
                    params = ln['call']
227
 
                    try:
228
 
                        # Expand parameters
229
 
                        if isinstance(params['args'], list):
230
 
                            args = map(self.eval, params['args'])
231
 
                        else:
232
 
                            args = []
233
 
                        if isinstance(params['kwargs'], dict):
234
 
                            kwargs = {}
235
 
                            for kwarg in params['kwargs']:
236
 
                                kwargs[kwarg] = self.eval(
237
 
                                    params['kwargs'][kwarg])
238
 
                        else:
239
 
                            kwargs = {}
240
 
 
241
 
                        # Run the fuction
242
 
                        function = self.eval(params['function'])
243
 
                        try:
244
 
                            call['result'] = function(*args, **kwargs)
245
 
                        except Exception, e:
246
 
                            exception = {}
247
 
                            tb = format_exc_start(start=1)
248
 
                            exception['traceback'] = \
249
 
                                ''.join(tb).decode('utf-8', 'replace')
250
 
                            exception['except'] = cPickle.dumps(e,
251
 
                                PICKLEVERSION)
252
 
                            call['exception'] = exception
253
 
                    except Exception, e:
254
 
                        tb = format_exc_start(start=1)
255
 
                        self.cmdQ.put(
256
 
                            {"exc": ''.join(tb).decode('utf-8', 'replace')})
257
 
                    
258
 
                    # Write out the inspection object
259
 
                    self.cmdQ.put(call)
260
 
                else:
261
 
                    self.cmdQ.put({'response': 'failure'})
262
 
                self.curr_cmd = ''
263
 
            elif 'execute' in ln:
264
 
                # Like block but return a serialization of the state
265
 
                # throw away partial command
266
 
                response = {'okay': None}
267
 
                sys.stdin = self.webio
268
 
                sys.stdout = self.webio
269
 
                sys.stderr = self.webio
270
 
                try:
271
 
                    cmd = compile(ln['execute'], "<web session>", 'exec');
272
 
                    # We don't expect a return value - 'single' symbol prints 
273
 
                    # it.
274
 
                    self.eval(cmd)
275
 
                except Exception, e:
276
 
                    response = {'exception': cPickle.dumps(e, PICKLEVERSION)}
 
284
                    exception['traceback'] = \
 
285
                        ''.join(tb).decode('utf-8', 'replace')
 
286
                    exception['except'] = cPickle.dumps(e,
 
287
                        PICKLEVERSION)
 
288
                    call['exception'] = exception
 
289
            except Exception, e:
 
290
                tb = format_exc_start(start=1)
 
291
                call = {"exc": ''.join(tb).decode('utf-8', 'replace')}
 
292
            
 
293
            # Flush the output buffers
 
294
            sys.stderr.flush()
 
295
            sys.stdout.flush()
 
296
 
 
297
            # Write out the inspection object
 
298
            return(call)
 
299
        else:
 
300
            return({'response': 'failure'})
 
301
 
 
302
    def handle_execute(self, params):
 
303
        # throw away any partial command.
 
304
        self.curr_cmd = ''
 
305
        
 
306
        # Like block but return a serialization of the state
 
307
        # throw away partial command
 
308
        response = {'okay': None}
 
309
        try:
 
310
            cmd = compile(params, "<web session>", 'exec');
 
311
            # We don't expect a return value - 'single' symbol prints it.
 
312
            self.eval(cmd)
 
313
        except Exception, e:
 
314
            response = {'exception': cPickle.dumps(e, PICKLEVERSION)}
 
315
           
 
316
        # Flush the output
 
317
        sys.stderr.flush()
 
318
        sys.stdout.flush()
277
319
               
278
 
                # Flush the files
279
 
                sys.stderr.flush()
280
 
                sys.stdout.flush()
281
 
                
282
 
                # Return the inspection object
283
 
                self.cmdQ.put(response)
284
 
 
285
 
                # Clear any previous command
286
 
                self.curr_cmd = ''
287
 
            elif 'set_vars' in ln:
288
 
                # Adds some variables to the global dictionary
289
 
                for var in ln['set_vars']:
290
 
                    try:
291
 
                        self.globs[var] = self.eval(ln['set_vars'][var])
292
 
                    except Exception, e:
293
 
                        tb = format_exc_start(start=1)
294
 
                        self.cmdQ.put(
295
 
                            {"exc": ''.join(tb).decode('utf-8', 'replace')})
296
 
 
297
 
                self.cmdQ.put({'okay': None})
298
 
            else:
299
 
                raise Exception, "Invalid Command"
 
320
        # Return the inspection object
 
321
        return(response)
 
322
 
 
323
    def handle_setvars(self, params):
 
324
        # Adds some variables to the global dictionary
 
325
        for var in params['set_vars']:
 
326
            try:
 
327
                self.globs[var] = self.eval(params['set_vars'][var])
 
328
            except Exception, e:
 
329
                tb = format_exc_start(start=1)
 
330
                return({"exc": ''.join(tb).decode('utf-8', 'replace')})
 
331
 
 
332
        return({'okay': None})
300
333
 
301
334
    def eval(self, source):
302
335
        """ Evaluates a string in the private global space """
342
375
    if terminate:
343
376
        raise common.chat.Terminate({"terminate":terminate})
344
377
    expiry.ping()
345
 
    lineQ.put({msg['cmd']:msg['text']})
 
378
    lineQ.put((msg['cmd'],msg['text']))
346
379
    if terminate:
347
380
        raise common.chat.Terminate({"terminate":terminate})
348
381
    return cmdQ.get()