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

« back to all changes in this revision

Viewing changes to scripts/python-console

  • Committer: wagrant
  • Date: 2008-07-14 04:05:59 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:861
browser: Refactor the server side of actions1 generation. We now take
         a useful Python data structure and generate XHTML on load,
         rather than storing an enormous blob of unreadable XHTML.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
# usage:
 
4
#   python-console <port> <magic> [<working-dir>]
 
5
 
 
6
import cjson
 
7
import codeop
 
8
import md5
 
9
import os
 
10
import Queue
 
11
import signal
 
12
import socket
 
13
import sys
 
14
import traceback
 
15
from threading import Thread
 
16
from functools import partial
 
17
 
 
18
import common.chat
 
19
 
 
20
class Interrupt(Exception):
 
21
    def __init__(self):
 
22
        Exception.__init__(self, "Interrupted!")
 
23
 
 
24
class ExpiryTimer(object):
 
25
    def __init__(self, idle):
 
26
        self.idle = idle
 
27
        signal.signal(signal.SIGALRM, partial(self.timeout))
 
28
 
 
29
    def ping(self):
 
30
        signal.alarm(self.idle)
 
31
 
 
32
    def start(self, time):
 
33
        signal.alarm(time)
 
34
 
 
35
    def stop(self):
 
36
        self.ping()
 
37
 
 
38
    def timeout(self, signum, frame):
 
39
        sys.exit(1)
 
40
        
 
41
class StdinFromWeb(object):
 
42
    def __init__(self, cmdQ, lineQ):
 
43
        self.cmdQ = cmdQ
 
44
        self.lineQ = lineQ
 
45
 
 
46
    def readline(self):
 
47
        self.cmdQ.put({"input":None})
 
48
        expiry.ping()
 
49
        ln = self.lineQ.get()
 
50
        if 'chat' in ln:
 
51
            return ln['chat']
 
52
        if 'interrupt' in ln:
 
53
            raise Interrupt()
 
54
        if 'terminate' in ln:
 
55
            sys.exit(0)
 
56
            
 
57
 
 
58
class StdoutToWeb(object):
 
59
    def __init__(self, cmdQ, lineQ):
 
60
        self.cmdQ = cmdQ
 
61
        self.lineQ = lineQ
 
62
        self.remainder = ''
 
63
 
 
64
    def write(self, stuff):
 
65
        self.remainder = self.remainder + stuff
 
66
 
 
67
        # if there's less than 128 bytes, buffer
 
68
        if len(self.remainder) < 128:
 
69
            return
 
70
 
 
71
        # if there's lots, then send it in 1/2K blocks
 
72
        while len(self.remainder) > 512:
 
73
            blk = self.remainder[0:512]
 
74
            self.cmdQ.put({"output":blk})
 
75
            expiry.ping()
 
76
            ln = self.lineQ.get()
 
77
            self.remainder = self.remainder[512:]
 
78
 
 
79
        # Finally, split the remainder up into lines, and ship all the
 
80
        # completed lines off to the server.
 
81
        lines = self.remainder.split("\n")
 
82
        self.remainder = lines[-1]
 
83
        del lines[-1]
 
84
 
 
85
        if len(lines) > 0:
 
86
            lines.append('')
 
87
            text = "\n".join(lines)
 
88
            self.cmdQ.put({"output":text})
 
89
            expiry.ping()
 
90
            ln = self.lineQ.get()
 
91
            if 'interrupt' in ln:
 
92
                raise Interrupt()
 
93
 
 
94
    def flush(self):
 
95
        if len(self.remainder) > 0:
 
96
            self.cmdQ.put({"output":self.remainder})
 
97
            expiry.ping()
 
98
            ln = self.lineQ.get()
 
99
            self.remainder = ''
 
100
            if 'interrupt' in ln:
 
101
                raise Interrupt()
 
102
 
 
103
class WebIO(object):
 
104
    """Provides a file like interface to the Web front end of the console.
 
105
    You may print text to the console using write(), flush any buffered output 
 
106
    using flush(), or request text from the console using readline()"""
 
107
    
 
108
    def __init__(self, cmdQ, lineQ):
 
109
        self.cmdQ = cmdQ
 
110
        self.lineQ = lineQ
 
111
        self.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
 
112
        self.stdout = StdoutToWeb(self.cmdQ, self.lineQ)
 
113
 
 
114
    def write(self, stuff):
 
115
        self.stdout.write(stuff)
 
116
 
 
117
    def flush(self):
 
118
        self.stdout.flush()
 
119
 
 
120
    def readline(self):
 
121
        self.stdout.flush()
 
122
        return self.stdin.readline()
 
123
 
 
124
class PythonRunner(Thread):
 
125
    def __init__(self, cmdQ, lineQ):
 
126
        self.cmdQ = cmdQ
 
127
        self.lineQ = lineQ
 
128
        self.webio = WebIO(self.cmdQ, self.lineQ)
 
129
        Thread.__init__(self)
 
130
 
 
131
    def execCmd(self, cmd):
 
132
        try:
 
133
            sys.stdin = self.webio
 
134
            sys.stdout = self.webio
 
135
            sys.stderr = self.webio
 
136
            res = eval(cmd, self.globs)
 
137
            self.webio.flush()
 
138
            self.cmdQ.put({"okay":res})
 
139
            self.curr_cmd = ''
 
140
        except:
 
141
            tb = format_exc_start(start=1)
 
142
            self.webio.flush()
 
143
            self.cmdQ.put({"exc": ''.join(tb)})
 
144
            self.curr_cmd = ''
 
145
 
 
146
    def run(self):
 
147
        self.globs = {}
 
148
        self.globs['__builtins__'] = globals()['__builtins__']
 
149
        self.curr_cmd = ''
 
150
        compiler = codeop.CommandCompiler()
 
151
 
 
152
        while True:
 
153
            ln = self.lineQ.get()
 
154
            if 'chat' in ln:
 
155
                if self.curr_cmd == '':
 
156
                    self.curr_cmd = ln['chat']
 
157
                else:
 
158
                    self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
 
159
                try:
 
160
                    cmd = compiler(self.curr_cmd)
 
161
                    if cmd is None:
 
162
                        # The command was incomplete,
 
163
                        # so send back a None, so the
 
164
                        # client can print a '...'
 
165
                        self.cmdQ.put({"more":None})
 
166
                    else:
 
167
                        self.execCmd(cmd)
 
168
                except:
 
169
                    tb = format_exc_start(start=3)
 
170
                    self.cmdQ.put({"exc": ''.join(tb)})
 
171
                    self.webio.flush()
 
172
                    self.curr_cmd = ''
 
173
            if 'block' in ln:
 
174
                # throw away a partial command.
 
175
                try:
 
176
                    cmd = compile(ln['block'], "<web session>", 'exec');
 
177
                    self.execCmd(cmd)
 
178
                except:
 
179
                    tb = format_exc_start()
 
180
                    self.webio.flush()
 
181
                    self.cmdQ.put({"exc": ''.join(tb)})
 
182
                    self.curr_cmd = ''
 
183
 
 
184
def daemonize():
 
185
    if os.fork():   # launch child and...
 
186
        os._exit(0) # kill off parent
 
187
    os.setsid()
 
188
    if os.fork():   # launch child and...
 
189
        os._exit(0) # kill off parent again.
 
190
    os.umask(077)
 
191
 
 
192
# The global 'magic' is the secret that the client and server share
 
193
# which is used to create and md5 digest to authenticate requests.
 
194
# It is assigned a real value at startup.
 
195
magic = ''
 
196
 
 
197
cmdQ = Queue.Queue()
 
198
lineQ = Queue.Queue()
 
199
interpThread = PythonRunner(cmdQ, lineQ)
 
200
 
 
201
# Default expiry time of 15 minutes
 
202
expiry = ExpiryTimer(15 * 60)
 
203
 
 
204
def initializer():
 
205
    interpThread.setDaemon(True)
 
206
    interpThread.start()
 
207
    expiry.ping()
 
208
 
 
209
def dispatch_msg(msg):
 
210
    expiry.ping()
 
211
    lineQ.put({msg['cmd']:msg['text']})
 
212
    return cmdQ.get()
 
213
 
 
214
def format_exc_start(start=0):
 
215
    etype, value, tb = sys.exc_info()
 
216
    tbbits = traceback.extract_tb(tb)[start:]
 
217
    list = ['Traceback (most recent call last):\n']
 
218
    list = list + traceback.format_list(tbbits)
 
219
    list = list + traceback.format_exception_only(etype, value)
 
220
    return ''.join(list)
 
221
 
 
222
if __name__ == "__main__":
 
223
    port = int(sys.argv[1])
 
224
    magic = sys.argv[2]
 
225
    if len(sys.argv) >= 4:
 
226
        # working_dir
 
227
        os.chdir(sys.argv[3])
 
228
        # Make python's search path follow the cwd
 
229
        sys.path[0] = ''
 
230
        os.environ['HOME'] = sys.argv[3]
 
231
 
 
232
    common.chat.start_server(port, magic, True, dispatch_msg, initializer)