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

« back to all changes in this revision

Viewing changes to scripts/python-console

  • Committer: William Grant
  • Date: 2012-06-28 01:52:02 UTC
  • Revision ID: me@williamgrant.id.au-20120628015202-f6ru7o367gt6nvgz
Hah

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