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

« back to all changes in this revision

Viewing changes to scripts/python-console

  • Committer: mattgiuca
  • Date: 2008-07-27 10:29:16 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:954
Added svn:ignore on some directories.

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
16
from functools import partial
16
17
 
50
51
            return ln['chat']
51
52
        if 'interrupt' in ln:
52
53
            raise Interrupt()
 
54
        if 'terminate' in ln:
 
55
            sys.exit(0)
 
56
            
53
57
 
54
58
class StdoutToWeb(object):
55
59
    def __init__(self, cmdQ, lineQ):
57
61
        self.lineQ = lineQ
58
62
        self.remainder = ''
59
63
 
 
64
    def _trim_incomplete_final(self, stuff):
 
65
        '''Trim an incomplete UTF-8 character from the end of a string.
 
66
           Returns (trimmed_string, count_of_trimmed_bytes).
 
67
        '''
 
68
        tokill = incomplete_utf8_sequence(stuff)
 
69
        if tokill == 0:
 
70
            return (stuff, tokill)
 
71
        else:
 
72
            return (stuff[:-tokill], tokill)
 
73
 
60
74
    def write(self, stuff):
 
75
        # print will only give a non-file a unicode or str. There's no way
 
76
        # to convince it to encode unicodes, so we have to do it ourselves.
 
77
        # Yay for file special-cases (fileobject.c, PyFile_WriteObject).
 
78
        # If somebody wants to write some other object to here, they do it
 
79
        # at their own peril.
 
80
        if isinstance(stuff, unicode):
 
81
            stuff = stuff.encode('utf-8')
61
82
        self.remainder = self.remainder + stuff
62
83
 
63
84
        # if there's less than 128 bytes, buffer
66
87
 
67
88
        # if there's lots, then send it in 1/2K blocks
68
89
        while len(self.remainder) > 512:
69
 
            blk = self.remainder[0:512]
70
 
            self.cmdQ.put({"output":blk})
 
90
            # We send things as Unicode inside JSON, so we must only send
 
91
            # complete UTF-8 characters.
 
92
            (blk, count) = self._trim_incomplete_final(self.remainder[:512])
 
93
            self.cmdQ.put({"output":blk.decode('utf-8', 'replace')})
71
94
            expiry.ping()
72
95
            ln = self.lineQ.get()
73
 
            self.remainder = self.remainder[512:]
 
96
            self.remainder = self.remainder[512 - count:]
74
97
 
75
98
        # Finally, split the remainder up into lines, and ship all the
76
99
        # completed lines off to the server.
81
104
        if len(lines) > 0:
82
105
            lines.append('')
83
106
            text = "\n".join(lines)
84
 
            self.cmdQ.put({"output":text})
 
107
            self.cmdQ.put({"output":text.decode('utf-8', 'replace')})
85
108
            expiry.ping()
86
109
            ln = self.lineQ.get()
87
110
            if 'interrupt' in ln:
89
112
 
90
113
    def flush(self):
91
114
        if len(self.remainder) > 0:
92
 
            self.cmdQ.put({"output":self.remainder})
 
115
            (out, count) = self._trim_incomplete_final(self.remainder)
 
116
            self.cmdQ.put({"output":out.decode('utf-8', 'replace')})
93
117
            expiry.ping()
94
118
            ln = self.lineQ.get()
95
 
            self.remainder = ''
 
119
            # Leave incomplete characters in the buffer.
 
120
            # Yes, this does mean that an incomplete character will be left
 
121
            # off the end, but we discussed this and it was deemed best.
 
122
            self.remainder = self.remainder[len(self.remainder)-count:]
96
123
            if 'interrupt' in ln:
97
124
                raise Interrupt()
98
125
 
 
126
class WebIO(object):
 
127
    """Provides a file like interface to the Web front end of the console.
 
128
    You may print text to the console using write(), flush any buffered output 
 
129
    using flush(), or request text from the console using readline()"""
 
130
    
 
131
    def __init__(self, cmdQ, lineQ):
 
132
        self.cmdQ = cmdQ
 
133
        self.lineQ = lineQ
 
134
        self.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
 
135
        self.stdout = StdoutToWeb(self.cmdQ, self.lineQ)
 
136
 
 
137
    def write(self, stuff):
 
138
        self.stdout.write(stuff)
 
139
 
 
140
    def flush(self):
 
141
        self.stdout.flush()
 
142
 
 
143
    def readline(self):
 
144
        self.stdout.flush()
 
145
        return self.stdin.readline()
 
146
 
99
147
class PythonRunner(Thread):
100
148
    def __init__(self, cmdQ, lineQ):
101
149
        self.cmdQ = cmdQ
102
150
        self.lineQ = lineQ
103
 
        self.stdout = StdoutToWeb(self.cmdQ, self.lineQ)
 
151
        self.webio = WebIO(self.cmdQ, self.lineQ)
104
152
        Thread.__init__(self)
105
153
 
106
154
    def execCmd(self, cmd):
107
155
        try:
108
 
            sys.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
109
 
            sys.stdout = self.stdout
110
 
            sys.stderr = self.stdout
111
 
            res = eval(cmd, self.globs, self.locls)
112
 
            self.stdout.flush()
113
 
            self.cmdQ.put({"okay":res})
 
156
            sys.stdin = self.webio
 
157
            sys.stdout = self.webio
 
158
            sys.stderr = self.webio
 
159
            # We don't expect a return value - 'single' symbol prints it.
 
160
            eval(cmd, self.globs)
 
161
            self.webio.flush()
 
162
            self.cmdQ.put({"okay": None})
114
163
            self.curr_cmd = ''
115
 
        except Exception, exc:
116
 
            self.stdout.flush()
117
 
            self.cmdQ.put({"exc":str(exc)})
 
164
        except:
 
165
            tb = format_exc_start(start=1)
 
166
            self.webio.flush()
 
167
            self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
118
168
            self.curr_cmd = ''
119
169
 
120
170
    def run(self):
121
171
        self.globs = {}
122
172
        self.globs['__builtins__'] = globals()['__builtins__']
123
 
        self.locls = {}
124
173
        self.curr_cmd = ''
125
 
        compiler = codeop.CommandCompiler()
126
174
 
127
175
        while True:
128
176
            ln = self.lineQ.get()
132
180
                else:
133
181
                    self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
134
182
                try:
135
 
                    cmd = compiler(self.curr_cmd)
 
183
                    cmd = codeop.compile_command(self.curr_cmd, '<web session>')
136
184
                    if cmd is None:
137
185
                        # The command was incomplete,
138
186
                        # so send back a None, so the
140
188
                        self.cmdQ.put({"more":None})
141
189
                    else:
142
190
                        self.execCmd(cmd)
143
 
                except Exception, exc:
144
 
                    self.stdout.flush()
145
 
                    self.cmdQ.put({"exc":str(exc)})
 
191
                except:
 
192
                    tb = format_exc_start(start=3)
 
193
                    self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
 
194
                    self.webio.flush()
146
195
                    self.curr_cmd = ''
147
196
            if 'block' in ln:
148
197
                # throw away a partial command.
149
198
                try:
150
199
                    cmd = compile(ln['block'], "<web session>", 'exec');
151
200
                    self.execCmd(cmd)
152
 
                except Exception, exc:
153
 
                    self.stdout.flush()
154
 
                    self.cmdQ.put({"exc":str(exc)})
 
201
                except:
 
202
                    tb = format_exc_start(start=1)
 
203
                    self.webio.flush()
 
204
                    self.cmdQ.put({"exc": ''.join(tb).decode('utf-8', 'replace')})
155
205
                    self.curr_cmd = ''
156
206
 
157
207
def daemonize():
184
234
    lineQ.put({msg['cmd']:msg['text']})
185
235
    return cmdQ.get()
186
236
 
 
237
def format_exc_start(start=0):
 
238
    etype, value, tb = sys.exc_info()
 
239
    tbbits = traceback.extract_tb(tb)[start:]
 
240
    list = ['Traceback (most recent call last):\n']
 
241
    list = list + traceback.format_list(tbbits)
 
242
    list = list + traceback.format_exception_only(etype, value)
 
243
    return ''.join(list)
 
244
 
 
245
def incomplete_utf8_sequence(byteseq):
 
246
    """
 
247
    str -> int
 
248
    Given a UTF-8-encoded byte sequence (str), returns the number of bytes at
 
249
    the end of the string which comprise an incomplete UTF-8 character
 
250
    sequence.
 
251
 
 
252
    If the string is empty or ends with a complete character OR INVALID
 
253
    sequence, returns 0.
 
254
    Otherwise, returns 1-3 indicating the number of bytes in the final
 
255
    incomplete (but valid) character sequence.
 
256
 
 
257
    Does not check any bytes before the final sequence for correctness.
 
258
 
 
259
    >>> incomplete_utf8_sequence("")
 
260
    0
 
261
    >>> incomplete_utf8_sequence("xy")
 
262
    0
 
263
    >>> incomplete_utf8_sequence("xy\xc3\xbc")
 
264
    0
 
265
    >>> incomplete_utf8_sequence("\xc3")
 
266
    1
 
267
    >>> incomplete_utf8_sequence("\xbc\xc3")
 
268
    1
 
269
    >>> incomplete_utf8_sequence("xy\xbc\xc3")
 
270
    1
 
271
    >>> incomplete_utf8_sequence("xy\xe0\xa0")
 
272
    2
 
273
    >>> incomplete_utf8_sequence("xy\xf4")
 
274
    1
 
275
    >>> incomplete_utf8_sequence("xy\xf4\x8f")
 
276
    2
 
277
    >>> incomplete_utf8_sequence("xy\xf4\x8f\xa0")
 
278
    3
 
279
    """
 
280
    count = 0
 
281
    expect = None
 
282
    for b in byteseq[::-1]:
 
283
        b = ord(b)
 
284
        count += 1
 
285
        if b & 0x80 == 0x0:
 
286
            # 0xxxxxxx (single-byte character)
 
287
            expect = 1
 
288
            break
 
289
        elif b & 0xc0 == 0x80:
 
290
            # 10xxxxxx (subsequent byte)
 
291
            pass
 
292
        elif b & 0xe0 == 0xc0:
 
293
            # 110xxxxx (start of 2-byte sequence)
 
294
            expect = 2
 
295
            break
 
296
        elif b & 0xf0 == 0xe0:
 
297
            # 1110xxxx (start of 3-byte sequence)
 
298
            expect = 3
 
299
            break
 
300
        elif b & 0xf8 == 0xf0:
 
301
            # 11110xxx (start of 4-byte sequence)
 
302
            expect = 4
 
303
            break
 
304
        else:
 
305
            # Invalid byte
 
306
            return 0
 
307
 
 
308
        if count >= 4:
 
309
            # Seen too many "subsequent bytes", invalid
 
310
            return 0
 
311
 
 
312
    if expect is None:
 
313
        # We never saw a "first byte", invalid
 
314
        return 0
 
315
 
 
316
    # We now know expect and count
 
317
    if count >= expect:
 
318
        # Complete, or we saw an invalid sequence
 
319
        return 0
 
320
    elif count < expect:
 
321
        # Incomplete
 
322
        return count
 
323
 
187
324
if __name__ == "__main__":
188
325
    port = int(sys.argv[1])
189
326
    magic = sys.argv[2]
190
327
    if len(sys.argv) >= 4:
191
328
        # working_dir
192
329
        os.chdir(sys.argv[3])
 
330
        # Make python's search path follow the cwd
 
331
        sys.path[0] = ''
193
332
        os.environ['HOME'] = sys.argv[3]
194
333
 
195
334
    common.chat.start_server(port, magic, True, dispatch_msg, initializer)