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

« back to all changes in this revision

Viewing changes to scripts/python-console

  • Committer: William Grant
  • Date: 2010-02-26 01:09:49 UTC
  • Revision ID: grantw@unimelb.edu.au-20100226010949-xka8c9s6y7aq4id1
Scroll to the end of the console output after *every* output, not just some. This removes some artifiacts resulting from scrolling a fraction of a second too late.

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 PythonRunner(Thread):
100
 
    def __init__(self, cmdQ, lineQ):
101
 
        self.cmdQ = cmdQ
102
 
        self.lineQ = lineQ
103
 
        self.stdout = StdoutToWeb(self.cmdQ, self.lineQ)
104
 
        Thread.__init__(self)
105
 
 
106
 
    def execCmd(self, cmd):
107
 
        try:
108
 
            sys.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
109
 
            sys.stdout = self.stdout
110
 
            sys.stderr = self.stdout
111
 
            res = eval(cmd, self.globs)
112
 
            self.stdout.flush()
113
 
            self.cmdQ.put({"okay":res})
114
 
            self.curr_cmd = ''
115
 
        except Exception, exc:
116
 
            self.stdout.flush()
117
 
            exc_classname = exc.__class__.__name__
118
 
            self.cmdQ.put({"exc": exc_classname + ": " + exc.message})
119
 
            self.curr_cmd = ''
120
 
 
121
 
    def run(self):
122
 
        self.globs = {}
123
 
        self.globs['__builtins__'] = globals()['__builtins__']
124
 
        self.curr_cmd = ''
125
 
        compiler = codeop.CommandCompiler()
126
 
 
127
 
        while True:
128
 
            ln = self.lineQ.get()
129
 
            if 'chat' in ln:
130
 
                if self.curr_cmd == '':
131
 
                    self.curr_cmd = ln['chat']
132
 
                else:
133
 
                    self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
134
 
                try:
135
 
                    cmd = compiler(self.curr_cmd)
136
 
                    if cmd is None:
137
 
                        # The command was incomplete,
138
 
                        # so send back a None, so the
139
 
                        # client can print a '...'
140
 
                        self.cmdQ.put({"more":None})
141
 
                    else:
142
 
                        self.execCmd(cmd)
143
 
                except Exception, exc:
144
 
                    self.stdout.flush()
145
 
                    self.cmdQ.put({"exc":str(exc)})
146
 
                    self.curr_cmd = ''
147
 
            if 'block' in ln:
148
 
                # throw away a partial command.
149
 
                try:
150
 
                    cmd = compile(ln['block'], "<web session>", 'exec');
151
 
                    self.execCmd(cmd)
152
 
                except Exception, exc:
153
 
                    self.stdout.flush()
154
 
                    self.cmdQ.put({"exc":str(exc)})
155
 
                    self.curr_cmd = ''
156
 
 
157
 
def daemonize():
158
 
    if os.fork():   # launch child and...
159
 
        os._exit(0) # kill off parent
160
 
    os.setsid()
161
 
    if os.fork():   # launch child and...
162
 
        os._exit(0) # kill off parent again.
163
 
    os.umask(077)
164
 
 
165
 
# The global 'magic' is the secret that the client and server share
166
 
# which is used to create and md5 digest to authenticate requests.
167
 
# It is assigned a real value at startup.
168
 
magic = ''
169
 
 
170
 
cmdQ = Queue.Queue()
171
 
lineQ = Queue.Queue()
172
 
interpThread = PythonRunner(cmdQ, lineQ)
173
 
 
174
 
# Default expiry time of 15 minutes
175
 
expiry = ExpiryTimer(15 * 60)
176
 
 
177
 
def initializer():
178
 
    interpThread.setDaemon(True)
179
 
    interpThread.start()
180
 
    expiry.ping()
181
 
 
182
 
def dispatch_msg(msg):
183
 
    expiry.ping()
184
 
    lineQ.put({msg['cmd']:msg['text']})
185
 
    return cmdQ.get()
186
 
 
187
 
if __name__ == "__main__":
188
 
    port = int(sys.argv[1])
189
 
    magic = sys.argv[2]
190
 
    if len(sys.argv) >= 4:
191
 
        # working_dir
192
 
        os.chdir(sys.argv[3])
193
 
        os.environ['HOME'] = sys.argv[3]
194
 
 
195
 
    common.chat.start_server(port, magic, True, dispatch_msg, initializer)