4
# python-console <port> <magic> [<working-dir>]
14
from threading import Thread
15
from functools import partial
19
class Interrupt(Exception):
21
Exception.__init__(self, "Interrupted!")
23
class ExpiryTimer(object):
24
def __init__(self, idle):
26
signal.signal(signal.SIGALRM, partial(self.timeout))
29
signal.alarm(self.idle)
31
def start(self, time):
37
def timeout(self, signum, frame):
40
class StdinFromWeb(object):
41
def __init__(self, cmdQ, lineQ):
46
self.cmdQ.put({"input":None})
54
class StdoutToWeb(object):
55
def __init__(self, cmdQ, lineQ):
60
def write(self, stuff):
61
self.remainder = self.remainder + stuff
63
# if there's less than 128 bytes, buffer
64
if len(self.remainder) < 128:
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})
73
self.remainder = self.remainder[512:]
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]
83
text = "\n".join(lines)
84
self.cmdQ.put({"output":text})
91
if len(self.remainder) > 0:
92
self.cmdQ.put({"output":self.remainder})
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()"""
104
def __init__(self, cmdQ, lineQ):
107
self.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
108
self.stdout = StdoutToWeb(self.cmdQ, self.lineQ)
110
def write(self, stuff):
111
self.stdout.write(stuff)
118
return self.stdin.readline()
120
class PythonRunner(Thread):
121
def __init__(self, cmdQ, lineQ):
124
self.webio = WebIO(self.cmdQ, self.lineQ)
125
Thread.__init__(self)
127
def execCmd(self, cmd):
129
sys.stdin = self.webio
130
sys.stdout = self.webio
131
sys.stderr = self.webio
132
res = eval(cmd, self.globs)
134
self.cmdQ.put({"okay":res})
136
except Exception, exc:
138
exc_classname = exc.__class__.__name__
139
self.cmdQ.put({"exc": exc_classname + ": " + str(exc)})
144
self.globs['__builtins__'] = globals()['__builtins__']
146
compiler = codeop.CommandCompiler()
149
ln = self.lineQ.get()
151
if self.curr_cmd == '':
152
self.curr_cmd = ln['chat']
154
self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
156
cmd = compiler(self.curr_cmd)
158
# The command was incomplete,
159
# so send back a None, so the
160
# client can print a '...'
161
self.cmdQ.put({"more":None})
164
except Exception, exc:
166
self.cmdQ.put({"exc":str(exc)})
169
# throw away a partial command.
171
cmd = compile(ln['block'], "<web session>", 'exec');
173
except Exception, exc:
175
self.cmdQ.put({"exc":str(exc)})
179
if os.fork(): # launch child and...
180
os._exit(0) # kill off parent
182
if os.fork(): # launch child and...
183
os._exit(0) # kill off parent again.
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.
192
lineQ = Queue.Queue()
193
interpThread = PythonRunner(cmdQ, lineQ)
195
# Default expiry time of 15 minutes
196
expiry = ExpiryTimer(15 * 60)
199
interpThread.setDaemon(True)
203
def dispatch_msg(msg):
205
lineQ.put({msg['cmd']:msg['text']})
208
if __name__ == "__main__":
209
port = int(sys.argv[1])
211
if len(sys.argv) >= 4:
213
os.chdir(sys.argv[3])
214
# Make python's search path follow the cwd
216
os.environ['HOME'] = sys.argv[3]
218
common.chat.start_server(port, magic, True, dispatch_msg, initializer)