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

« back to all changes in this revision

Viewing changes to scripts/python-console

  • Committer: dcoles
  • Date: 2008-08-10 02:53:08 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1005
Groups: Make sure the links look 'clickable'

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
import signal
12
12
import socket
13
13
import sys
 
14
import traceback
14
15
from threading import Thread
15
 
from functools import partial
16
16
 
17
17
import common.chat
18
18
 
23
23
class ExpiryTimer(object):
24
24
    def __init__(self, idle):
25
25
        self.idle = idle
26
 
        signal.signal(signal.SIGALRM, partial(self.timeout))
 
26
        signal.signal(signal.SIGALRM, self.timeout)
27
27
 
28
28
    def ping(self):
29
29
        signal.alarm(self.idle)
36
36
 
37
37
    def timeout(self, signum, frame):
38
38
        sys.exit(1)
39
 
        
 
39
 
40
40
class StdinFromWeb(object):
41
41
    def __init__(self, cmdQ, lineQ):
42
42
        self.cmdQ = cmdQ
57
57
        self.lineQ = lineQ
58
58
        self.remainder = ''
59
59
 
 
60
    def _trim_incomplete_final(self, stuff):
 
61
        '''Trim an incomplete UTF-8 character from the end of a string.
 
62
           Returns (trimmed_string, count_of_trimmed_bytes).
 
63
        '''
 
64
        tokill = incomplete_utf8_sequence(stuff)
 
65
        if tokill == 0:
 
66
            return (stuff, tokill)
 
67
        else:
 
68
            return (stuff[:-tokill], tokill)
 
69
 
60
70
    def write(self, stuff):
 
71
        # print will only give a non-file a unicode or str. There's no way
 
72
        # to convince it to encode unicodes, so we have to do it ourselves.
 
73
        # Yay for file special-cases (fileobject.c, PyFile_WriteObject).
 
74
        # If somebody wants to write some other object to here, they do it
 
75
        # at their own peril.
 
76
        if isinstance(stuff, unicode):
 
77
            stuff = stuff.encode('utf-8')
61
78
        self.remainder = self.remainder + stuff
62
79
 
63
80
        # if there's less than 128 bytes, buffer
66
83
 
67
84
        # if there's lots, then send it in 1/2K blocks
68
85
        while len(self.remainder) > 512:
69
 
            blk = self.remainder[0:512]
70
 
            self.cmdQ.put({"output":blk})
 
86
            # We send things as Unicode inside JSON, so we must only send
 
87
            # complete UTF-8 characters.
 
88
            (blk, count) = self._trim_incomplete_final(self.remainder[:512])
 
89
            self.cmdQ.put({"output":blk.decode('utf-8', 'replace')})
71
90
            expiry.ping()
72
91
            ln = self.lineQ.get()
73
 
            self.remainder = self.remainder[512:]
 
92
            self.remainder = self.remainder[512 - count:]
74
93
 
75
94
        # Finally, split the remainder up into lines, and ship all the
76
95
        # completed lines off to the server.
81
100
        if len(lines) > 0:
82
101
            lines.append('')
83
102
            text = "\n".join(lines)
84
 
            self.cmdQ.put({"output":text})
 
103
            self.cmdQ.put({"output":text.decode('utf-8', 'replace')})
85
104
            expiry.ping()
86
105
            ln = self.lineQ.get()
87
106
            if 'interrupt' in ln:
89
108
 
90
109
    def flush(self):
91
110
        if len(self.remainder) > 0:
92
 
            self.cmdQ.put({"output":self.remainder})
 
111
            (out, count) = self._trim_incomplete_final(self.remainder)
 
112
            self.cmdQ.put({"output":out.decode('utf-8', 'replace')})
93
113
            expiry.ping()
94
114
            ln = self.lineQ.get()
95
 
            self.remainder = ''
 
115
            # Leave incomplete characters in the buffer.
 
116
            # Yes, this does mean that an incomplete character will be left
 
117
            # off the end, but we discussed this and it was deemed best.
 
118
            self.remainder = self.remainder[len(self.remainder)-count:]
96
119
            if 'interrupt' in ln:
97
120
                raise Interrupt()
98
121
 
129
152
            sys.stdin = self.webio
130
153
            sys.stdout = self.webio
131
154
            sys.stderr = self.webio
132
 
            res = eval(cmd, self.globs)
 
155
            # We don't expect a return value - 'single' symbol prints it.
 
156
            eval(cmd, self.globs)
133
157
            self.webio.flush()
134
 
            self.cmdQ.put({"okay":res})
 
158
            self.cmdQ.put({"okay": None})
135
159
            self.curr_cmd = ''
136
 
        except Exception, exc:
 
160
        except:
 
161
            tb = format_exc_start(start=1)
137
162
            self.webio.flush()
138
 
            exc_classname = exc.__class__.__name__
139
 
            self.cmdQ.put({"exc": exc_classname + ": " + str(exc)})
 
163
            self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
140
164
            self.curr_cmd = ''
141
165
 
142
166
    def run(self):
143
167
        self.globs = {}
144
168
        self.globs['__builtins__'] = globals()['__builtins__']
145
169
        self.curr_cmd = ''
146
 
        compiler = codeop.CommandCompiler()
147
170
 
148
171
        while True:
149
172
            ln = self.lineQ.get()
153
176
                else:
154
177
                    self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
155
178
                try:
156
 
                    cmd = compiler(self.curr_cmd)
 
179
                    cmd = codeop.compile_command(self.curr_cmd, '<web session>')
157
180
                    if cmd is None:
158
181
                        # The command was incomplete,
159
182
                        # so send back a None, so the
161
184
                        self.cmdQ.put({"more":None})
162
185
                    else:
163
186
                        self.execCmd(cmd)
164
 
                except Exception, exc:
 
187
                except:
 
188
                    tb = format_exc_start(start=3)
 
189
                    self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
165
190
                    self.webio.flush()
166
 
                    self.cmdQ.put({"exc":str(exc)})
167
191
                    self.curr_cmd = ''
168
192
            if 'block' in ln:
169
193
                # throw away a partial command.
170
194
                try:
171
195
                    cmd = compile(ln['block'], "<web session>", 'exec');
172
196
                    self.execCmd(cmd)
173
 
                except Exception, exc:
 
197
                except:
 
198
                    tb = format_exc_start(start=1)
174
199
                    self.webio.flush()
175
 
                    self.cmdQ.put({"exc":str(exc)})
 
200
                    self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
176
201
                    self.curr_cmd = ''
177
202
 
178
203
def daemonize():
191
216
cmdQ = Queue.Queue()
192
217
lineQ = Queue.Queue()
193
218
interpThread = PythonRunner(cmdQ, lineQ)
 
219
terminate = None
194
220
 
195
221
# Default expiry time of 15 minutes
196
222
expiry = ExpiryTimer(15 * 60)
198
224
def initializer():
199
225
    interpThread.setDaemon(True)
200
226
    interpThread.start()
 
227
    signal.signal(signal.SIGXCPU, sig_handler)
201
228
    expiry.ping()
202
229
 
 
230
def sig_handler(signum, frame):
 
231
    """Handles response from signals"""
 
232
    global terminate
 
233
    if signum == signal.SIGXCPU:
 
234
        terminate = "CPU Time Limit Exceeded"
 
235
 
203
236
def dispatch_msg(msg):
 
237
    global terminate
 
238
    if msg['cmd'] == 'restart':
 
239
        terminate = "User requested console be reset"
 
240
    if terminate:
 
241
        raise common.chat.Terminate({"restart":terminate})
204
242
    expiry.ping()
205
243
    lineQ.put({msg['cmd']:msg['text']})
 
244
    if terminate:
 
245
        raise common.chat.Terminate({"restart":terminate})
206
246
    return cmdQ.get()
207
247
 
 
248
def format_exc_start(start=0):
 
249
    etype, value, tb = sys.exc_info()
 
250
    tbbits = traceback.extract_tb(tb)[start:]
 
251
    list = ['Traceback (most recent call last):\n']
 
252
    list = list + traceback.format_list(tbbits)
 
253
    list = list + traceback.format_exception_only(etype, value)
 
254
    return ''.join(list)
 
255
 
 
256
def incomplete_utf8_sequence(byteseq):
 
257
    """
 
258
    str -> int
 
259
    Given a UTF-8-encoded byte sequence (str), returns the number of bytes at
 
260
    the end of the string which comprise an incomplete UTF-8 character
 
261
    sequence.
 
262
 
 
263
    If the string is empty or ends with a complete character OR INVALID
 
264
    sequence, returns 0.
 
265
    Otherwise, returns 1-3 indicating the number of bytes in the final
 
266
    incomplete (but valid) character sequence.
 
267
 
 
268
    Does not check any bytes before the final sequence for correctness.
 
269
 
 
270
    >>> incomplete_utf8_sequence("")
 
271
    0
 
272
    >>> incomplete_utf8_sequence("xy")
 
273
    0
 
274
    >>> incomplete_utf8_sequence("xy\xc3\xbc")
 
275
    0
 
276
    >>> incomplete_utf8_sequence("\xc3")
 
277
    1
 
278
    >>> incomplete_utf8_sequence("\xbc\xc3")
 
279
    1
 
280
    >>> incomplete_utf8_sequence("xy\xbc\xc3")
 
281
    1
 
282
    >>> incomplete_utf8_sequence("xy\xe0\xa0")
 
283
    2
 
284
    >>> incomplete_utf8_sequence("xy\xf4")
 
285
    1
 
286
    >>> incomplete_utf8_sequence("xy\xf4\x8f")
 
287
    2
 
288
    >>> incomplete_utf8_sequence("xy\xf4\x8f\xa0")
 
289
    3
 
290
    """
 
291
    count = 0
 
292
    expect = None
 
293
    for b in byteseq[::-1]:
 
294
        b = ord(b)
 
295
        count += 1
 
296
        if b & 0x80 == 0x0:
 
297
            # 0xxxxxxx (single-byte character)
 
298
            expect = 1
 
299
            break
 
300
        elif b & 0xc0 == 0x80:
 
301
            # 10xxxxxx (subsequent byte)
 
302
            pass
 
303
        elif b & 0xe0 == 0xc0:
 
304
            # 110xxxxx (start of 2-byte sequence)
 
305
            expect = 2
 
306
            break
 
307
        elif b & 0xf0 == 0xe0:
 
308
            # 1110xxxx (start of 3-byte sequence)
 
309
            expect = 3
 
310
            break
 
311
        elif b & 0xf8 == 0xf0:
 
312
            # 11110xxx (start of 4-byte sequence)
 
313
            expect = 4
 
314
            break
 
315
        else:
 
316
            # Invalid byte
 
317
            return 0
 
318
 
 
319
        if count >= 4:
 
320
            # Seen too many "subsequent bytes", invalid
 
321
            return 0
 
322
 
 
323
    if expect is None:
 
324
        # We never saw a "first byte", invalid
 
325
        return 0
 
326
 
 
327
    # We now know expect and count
 
328
    if count >= expect:
 
329
        # Complete, or we saw an invalid sequence
 
330
        return 0
 
331
    elif count < expect:
 
332
        # Incomplete
 
333
        return count
 
334
 
208
335
if __name__ == "__main__":
209
336
    port = int(sys.argv[1])
210
337
    magic = sys.argv[2]
 
338
    
 
339
    # Sanitise the Enviroment
 
340
    os.environ = {}
 
341
    os.environ['PATH'] = '/usr/local/bin:/usr/bin:/bin'
 
342
 
211
343
    if len(sys.argv) >= 4:
212
344
        # working_dir
213
345
        os.chdir(sys.argv[3])
214
 
        # Make python's search path follow the cwd
215
 
        sys.path[0] = ''
216
346
        os.environ['HOME'] = sys.argv[3]
217
347
 
 
348
    # Make python's search path follow the cwd
 
349
    sys.path[0] = ''
 
350
 
218
351
    common.chat.start_server(port, magic, True, dispatch_msg, initializer)