4
# python-console <port> <magic> [<working-dir>]
15
from threading import Thread
16
from functools import partial
20
class Interrupt(Exception):
22
Exception.__init__(self, "Interrupted!")
24
class ExpiryTimer(object):
25
def __init__(self, idle):
27
signal.signal(signal.SIGALRM, partial(self.timeout))
30
signal.alarm(self.idle)
32
def start(self, time):
38
def timeout(self, signum, frame):
41
class StdinFromWeb(object):
42
def __init__(self, cmdQ, lineQ):
47
self.cmdQ.put({"input":None})
58
class StdoutToWeb(object):
59
def __init__(self, cmdQ, lineQ):
64
def write(self, stuff):
65
self.remainder = self.remainder + stuff
67
# if there's less than 128 bytes, buffer
68
if len(self.remainder) < 128:
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})
77
self.remainder = self.remainder[512:]
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]
87
text = "\n".join(lines)
88
self.cmdQ.put({"output":text})
95
if len(self.remainder) > 0:
96
self.cmdQ.put({"output":self.remainder})
100
if 'interrupt' in ln:
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()"""
108
def __init__(self, cmdQ, lineQ):
111
self.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
112
self.stdout = StdoutToWeb(self.cmdQ, self.lineQ)
114
def write(self, stuff):
115
self.stdout.write(stuff)
122
return self.stdin.readline()
124
class PythonRunner(Thread):
125
def __init__(self, cmdQ, lineQ):
128
self.webio = WebIO(self.cmdQ, self.lineQ)
129
Thread.__init__(self)
131
def execCmd(self, cmd):
133
sys.stdin = self.webio
134
sys.stdout = self.webio
135
sys.stderr = self.webio
136
res = eval(cmd, self.globs)
138
self.cmdQ.put({"okay":res})
141
tb = format_exc_start(start=1)
143
self.cmdQ.put({"exc": ''.join(tb)})
148
self.globs['__builtins__'] = globals()['__builtins__']
150
compiler = codeop.CommandCompiler()
153
ln = self.lineQ.get()
155
if self.curr_cmd == '':
156
self.curr_cmd = ln['chat']
158
self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
160
cmd = compiler(self.curr_cmd)
162
# The command was incomplete,
163
# so send back a None, so the
164
# client can print a '...'
165
self.cmdQ.put({"more":None})
169
tb = format_exc_start(start=3)
170
self.cmdQ.put({"exc": ''.join(tb)})
174
# throw away a partial command.
176
cmd = compile(ln['block'], "<web session>", 'exec');
179
tb = format_exc_start()
181
self.cmdQ.put({"exc": ''.join(tb)})
185
if os.fork(): # launch child and...
186
os._exit(0) # kill off parent
188
if os.fork(): # launch child and...
189
os._exit(0) # kill off parent again.
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.
198
lineQ = Queue.Queue()
199
interpThread = PythonRunner(cmdQ, lineQ)
201
# Default expiry time of 15 minutes
202
expiry = ExpiryTimer(15 * 60)
205
interpThread.setDaemon(True)
209
def dispatch_msg(msg):
211
lineQ.put({msg['cmd']:msg['text']})
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)
222
if __name__ == "__main__":
223
port = int(sys.argv[1])
225
if len(sys.argv) >= 4:
227
os.chdir(sys.argv[3])
228
# Make python's search path follow the cwd
230
os.environ['HOME'] = sys.argv[3]
232
common.chat.start_server(port, magic, True, dispatch_msg, initializer)