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})
55
class StdoutToWeb(object):
56
def __init__(self, cmdQ, lineQ):
61
def write(self, stuff):
62
self.remainder = self.remainder + stuff
64
# if there's less than 128 bytes, buffer
65
if len(self.remainder) < 128:
68
# if there's lots, then send it in 1/2K blocks
69
while len(self.remainder) > 512:
70
blk = self.remainder[0:512]
71
self.cmdQ.put({"output":blk})
74
self.remainder = self.remainder[512:]
76
# Finally, split the remainder up into lines, and ship all the
77
# completed lines off to the server.
78
lines = self.remainder.split("\n")
79
self.remainder = lines[-1]
84
text = "\n".join(lines)
85
self.cmdQ.put({"output":text})
92
if len(self.remainder) > 0:
93
self.cmdQ.put({"output":self.remainder})
101
"""Provides a file like interface to the Web front end of the console.
102
You may print text to the console using write(), flush any buffered output
103
using flush(), or request text from the console using readline()"""
105
def __init__(self, cmdQ, lineQ):
108
self.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
109
self.stdout = StdoutToWeb(self.cmdQ, self.lineQ)
111
def write(self, stuff):
112
self.stdout.write(stuff)
119
return self.stdin.readline()
121
class PythonRunner(Thread):
122
def __init__(self, cmdQ, lineQ):
125
self.webio = WebIO(self.cmdQ, self.lineQ)
126
Thread.__init__(self)
128
def execCmd(self, cmd):
130
sys.stdin = self.webio
131
sys.stdout = self.webio
132
sys.stderr = self.webio
133
res = eval(cmd, self.globs)
135
self.cmdQ.put({"okay":res})
138
tb = format_exc_start(start=1)
140
self.cmdQ.put({"exc": ''.join(tb)})
145
self.globs['__builtins__'] = globals()['__builtins__']
147
compiler = codeop.CommandCompiler()
150
ln = self.lineQ.get()
152
if self.curr_cmd == '':
153
self.curr_cmd = ln['chat']
155
self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
157
cmd = compiler(self.curr_cmd)
159
# The command was incomplete,
160
# so send back a None, so the
161
# client can print a '...'
162
self.cmdQ.put({"more":None})
166
tb = format_exc_start(start=3)
167
self.cmdQ.put({"exc": ''.join(tb)})
171
# throw away a partial command.
173
cmd = compile(ln['block'], "<web session>", 'exec');
176
tb = format_exc_start()
178
self.cmdQ.put({"exc": ''.join(tb)})
182
if os.fork(): # launch child and...
183
os._exit(0) # kill off parent
185
if os.fork(): # launch child and...
186
os._exit(0) # kill off parent again.
189
# The global 'magic' is the secret that the client and server share
190
# which is used to create and md5 digest to authenticate requests.
191
# It is assigned a real value at startup.
195
lineQ = Queue.Queue()
196
interpThread = PythonRunner(cmdQ, lineQ)
198
# Default expiry time of 15 minutes
199
expiry = ExpiryTimer(15 * 60)
202
interpThread.setDaemon(True)
206
def dispatch_msg(msg):
208
lineQ.put({msg['cmd']:msg['text']})
211
def format_exc_start(start=0):
212
etype, value, tb = sys.exc_info()
213
tbbits = traceback.extract_tb(tb)[start:]
214
list = ['Traceback (most recent call last):\n']
215
list = list + traceback.format_list(tbbits)
216
list = list + traceback.format_exception_only(etype, value)
219
if __name__ == "__main__":
220
port = int(sys.argv[1])
222
if len(sys.argv) >= 4:
224
os.chdir(sys.argv[3])
225
# Make python's search path follow the cwd
227
os.environ['HOME'] = sys.argv[3]
229
common.chat.start_server(port, magic, True, dispatch_msg, initializer)