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

418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
1
#!/usr/bin/python
2
3
# usage:
603 by mattgiuca
Console now starts up in the user's home directory.
4
#   python-console <port> <magic> [<working-dir>]
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
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
526 by drtomc
python-console: trivial bugfix - missing import.
15
from functools import partial
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
16
432 by drtomc
usrmgt: more work on this. Still some work to go.
17
import common.chat
18
598 by drtomc
console: send output back to the browser progressively.
19
class Interrupt(Exception):
628 by drtomc
console: Add output based interrupt. This allows users to interrupt long
20
    def __init__(self):
21
        Exception.__init__(self, "Interrupted!")
598 by drtomc
console: send output back to the browser progressively.
22
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
23
class ExpiryTimer(object):
24
    def __init__(self, idle):
25
        self.idle = idle
564 by drtomc
python-console: Fix the timeout code.
26
        signal.signal(signal.SIGALRM, partial(self.timeout))
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
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
        
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
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})
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
47
        expiry.ping()
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
48
        ln = self.lineQ.get()
49
        if 'chat' in ln:
50
            return ln['chat']
648 by drtomc
console: fix a trivial bug which caused it to loop if you got a syntax error in your first command.
51
        if 'interrupt' in ln:
52
            raise Interrupt()
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
53
598 by drtomc
console: send output back to the browser progressively.
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):
660 by drtomc
console: buffering now tries to buffer enough, but not too much.
61
        self.remainder = self.remainder + stuff
62
63
        # if there's less than 128 bytes, buffer
64
        if len(self.remainder) < 128:
641 by drtomc
console: slightly more aggressive output buffering - wait till we've at least
65
            return
66
660 by drtomc
console: buffering now tries to buffer enough, but not too much.
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")
598 by drtomc
console: send output back to the browser progressively.
78
        self.remainder = lines[-1]
79
        del lines[-1]
80
81
        if len(lines) > 0:
599 by drtomc
console: improve end of line handling.
82
            lines.append('')
83
            text = "\n".join(lines)
84
            self.cmdQ.put({"output":text})
598 by drtomc
console: send output back to the browser progressively.
85
            expiry.ping()
86
            ln = self.lineQ.get()
87
            if 'interrupt' in ln:
88
                raise Interrupt()
89
90
    def flush(self):
599 by drtomc
console: improve end of line handling.
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()
598 by drtomc
console: send output back to the browser progressively.
98
750 by dcoles
Console: Flush current output before requesting input from Web
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
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
120
class PythonRunner(Thread):
121
    def __init__(self, cmdQ, lineQ):
122
        self.cmdQ = cmdQ
123
        self.lineQ = lineQ
750 by dcoles
Console: Flush current output before requesting input from Web
124
        self.webio = WebIO(self.cmdQ, self.lineQ)
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
125
        Thread.__init__(self)
126
127
    def execCmd(self, cmd):
128
        try:
750 by dcoles
Console: Flush current output before requesting input from Web
129
            sys.stdin = self.webio
130
            sys.stdout = self.webio
131
            sys.stderr = self.webio
691 by mattgiuca
python-console: Removed "locls" dictionary when calling exec.
132
            res = eval(cmd, self.globs)
750 by dcoles
Console: Flush current output before requesting input from Web
133
            self.webio.flush()
599 by drtomc
console: improve end of line handling.
134
            self.cmdQ.put({"okay":res})
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
135
            self.curr_cmd = ''
136
        except Exception, exc:
750 by dcoles
Console: Flush current output before requesting input from Web
137
            self.webio.flush()
738 by dcoles
Console: Prints the Exception type as well as the message when code that
138
            exc_classname = exc.__class__.__name__
741 by dcoles
Console: Use __str__ of exception rather than message. Some exceptions don't
139
            self.cmdQ.put({"exc": exc_classname + ": " + str(exc)})
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
140
            self.curr_cmd = ''
141
142
    def run(self):
598 by drtomc
console: send output back to the browser progressively.
143
        self.globs = {}
144
        self.globs['__builtins__'] = globals()['__builtins__']
145
        self.curr_cmd = ''
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
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:
750 by dcoles
Console: Flush current output before requesting input from Web
165
                    self.webio.flush()
599 by drtomc
console: improve end of line handling.
166
                    self.cmdQ.put({"exc":str(exc)})
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
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:
750 by dcoles
Console: Flush current output before requesting input from Web
174
                    self.webio.flush()
599 by drtomc
console: improve end of line handling.
175
                    self.cmdQ.put({"exc":str(exc)})
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
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
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
195
# Default expiry time of 15 minutes
196
expiry = ExpiryTimer(15 * 60)
197
432 by drtomc
usrmgt: more work on this. Still some work to go.
198
def initializer():
199
    interpThread.setDaemon(True)
200
    interpThread.start()
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
201
    expiry.ping()
432 by drtomc
usrmgt: more work on this. Still some work to go.
202
203
def dispatch_msg(msg):
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
204
    expiry.ping()
432 by drtomc
usrmgt: more work on this. Still some work to go.
205
    lineQ.put({msg['cmd']:msg['text']})
206
    return cmdQ.get()
207
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
208
if __name__ == "__main__":
209
    port = int(sys.argv[1])
210
    magic = sys.argv[2]
603 by mattgiuca
Console now starts up in the user's home directory.
211
    if len(sys.argv) >= 4:
212
        # working_dir
213
        os.chdir(sys.argv[3])
749 by dcoles
Console: Override the sys.path on the console process so it's search path follows the cwd of the console (like how the commandline console works in interactive mode). This means you can now import modules in the $HOME directory. Ideally this should be the cwd of the browser (or just $HOME if it's a stand alone console), but at present there is no facility to set the cwd of the console when initilized. (So, we'll just stick with standalone mode for the moment)
214
        # Make python's search path follow the cwd
215
        sys.path[0] = ''
662 by drtomc
console: set the environment variable HOME so matplotlib works in the console.
216
        os.environ['HOME'] = sys.argv[3]
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
217
432 by drtomc
usrmgt: more work on this. Still some work to go.
218
    common.chat.start_server(port, magic, True, dispatch_msg, initializer)