[shpy-commit] r2873 - in shpy/trunk/dist/shpy: . net

arigo@codespeak.net arigo@codespeak.net
Wed, 21 Jan 2004 16:33:36 +0100 (MET)


Author: arigo
Date: Wed Jan 21 16:33:35 2004
New Revision: 2873

Added:
   shpy/trunk/dist/shpy/net/inputoutput.py
Modified:
   shpy/trunk/dist/shpy/net/gateway.py
   shpy/trunk/dist/shpy/net/register.py
   shpy/trunk/dist/shpy/net/shell.py
   shpy/trunk/dist/shpy/ui_pygame.py
Log:
Introduced execution server.  You must run a startserver.py process
locally; pressing ALT-ENTER in a cell executes its content in that
process.

Rearranged net/ a bit.


Modified: shpy/trunk/dist/shpy/net/gateway.py
==============================================================================
--- shpy/trunk/dist/shpy/net/gateway.py	(original)
+++ shpy/trunk/dist/shpy/net/gateway.py	Wed Jan 21 16:33:35 2004
@@ -2,33 +2,6 @@
 
 from unittest2.tool import dyncode
 
-class SocketIO:
-    def __init__(self, sock):
-        self.sock = sock
-        try:
-            sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
-            sock.setsockopt(socket.SOL_IP, socket.IP_TOS, 0x10)  # IPTOS_LOWDELAY
-        except socket.error, e:
-            print "Cannot set socket option:", str(e)
-
-    def read(self, bytes):
-        "Read exactly 'bytes' bytes from the socket."
-        buf = ""
-        while len(buf) < bytes:
-            t = self.sock.recv(bytes - len(buf))
-            print 'recv -->', len(t)
-            if not t:
-                raise EOFError
-            buf += t
-        return buf
-
-    def write(self, data):
-        self.sock.sendall(data)
-
-    def close(self):
-        self.sock.shutdown(2)
-
-
 class Gateway:
     def __init__(self, io, ns = None):
         if ns is None:

Added: shpy/trunk/dist/shpy/net/inputoutput.py
==============================================================================
--- (empty file)
+++ shpy/trunk/dist/shpy/net/inputoutput.py	Wed Jan 21 16:33:35 2004
@@ -0,0 +1,39 @@
+import socket
+
+def connect(addr):
+    if isinstance(addr, str):
+        host, port = addr.split(':')
+        port = int(port)
+        addr = (host, port)
+    assert len(addr) == 2
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.connect(addr)
+    return SocketIO(sock)
+    
+class SocketIO:
+    def __init__(self, sock):
+        self.sock = sock
+        try:
+            sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
+            sock.setsockopt(socket.SOL_IP, socket.IP_TOS, 0x10)  # IPTOS_LOWDELAY
+        except socket.error, e:
+            print "Cannot set socket option:", str(e)
+
+    def read(self, bytes):
+        "Read exactly 'bytes' bytes from the socket."
+        buf = ""
+        while len(buf) < bytes:
+            t = self.sock.recv(bytes - len(buf))
+            print 'recv -->', len(t)
+            if not t:
+                raise EOFError
+            buf += t
+        return buf
+
+    def write(self, data):
+        self.sock.sendall(data)
+
+    def close(self):
+        self.sock.shutdown(2)
+
+

Modified: shpy/trunk/dist/shpy/net/register.py
==============================================================================
--- shpy/trunk/dist/shpy/net/register.py	(original)
+++ shpy/trunk/dist/shpy/net/register.py	Wed Jan 21 16:33:35 2004
@@ -1,34 +1,19 @@
 
 import autopath, os
 import inspect, socket, pickle, thread
-from shpy.net import gateway, structure
+from shpy.net import gateway, structure, inputoutput
 
 
-class ServerGateway(gateway.Gateway):
-
-    def __init__(self, hostport = ':8888', ns = None):
-        if isinstance(hostport, str):
-            host, port = hostport.split(':')
-            hostport = (host, int(port))
-
-        source = [inspect.getsource(gateway)]
+class InstallableGateway(gateway.Gateway):
+    def __init__(self, addr, ns = None):
+        source = [inspect.getsource(inputoutput),
+                  inspect.getsource(gateway),
+                  ]
         source.append('thread = Gateway(SocketIO(clientsock))')
-        source.append('pickler.dump("ok")')
         source = "\n".join(source)
-
-        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        sock.connect(hostport)
-        sockfile = sock.makefile('wb',0)
-        p = pickle.Pickler(sockfile, bin=True)
-        p.dump(source)
-        sockfile.close()
-        sockfile = sock.makefile('rb',0)
-        u = pickle.Unpickler(sockfile)
-        res = u.load()
-        sockfile.close()
-        assert res=='ok', ("could not establish server gateway %r" %
-                           hostport)
-        gateway.Gateway.__init__(self, gateway.SocketIO(sock), ns)
+        sockio = inputoutput.connect(addr)
+        sockio.write(pickle.dumps(source, bin=True))
+        gateway.Gateway.__init__(self, sockio, ns)
 
     def import_remote(self, filename, modname, reload=False):
         filename = os.path.join(autopath.thisdir, filename)
@@ -49,6 +34,8 @@
         """)
         self.exec_remote("".join(l) % locals())
 
+
+class ServerGateway(InstallableGateway):
     def registerclient(self, reload=False):
         self.ns['getstructure'] = structure.getstructure
         self.import_remote('shared.py', 'shared', reload)
@@ -72,3 +59,71 @@
         if 'root' in l:
             self.root = l['root']
             self.waiting_for_root.release()
+
+    def notifychanges(self, *structures):
+        lines = [structure.representstructure(s) for s in structures]
+        lines.append('''if 1:
+            for c in shared.getclientlist():
+                if c is not gateway:
+                    try:
+                        rerepresent = c.ns['representstructure']
+                        relines = []
+                        for structid in %r:
+                            obj = getstructure(structid)
+                            relines.append(rerepresent(obj))
+                        relines.append("terminal.postrepaintevent()")
+                        c.exec_remote('\\n'.join(relines))
+                    except:
+                        import traceback
+                        traceback.print_exc()
+        ''' % [structure.getstructureid(s) for s in structures])
+        self.exec_remote('\n'.join(lines))
+
+
+class ExecGateway(InstallableGateway):
+    def userexec_remote(self, lines, callback):
+        # hack: turn the content of the cell into
+        #
+        # if 1:
+        #    line1
+        #    line2
+        #    ...
+        #
+        lines = ['   ' + line for line in lines]
+        lines.insert(0, 'if 1:')
+        lines.append('')
+        sourcecode = '\n'.join(lines)
+        try:
+            callbacks = self.callbacks
+        except AttributeError:
+            callbacks = self.callbacks = {}
+        answerid = id(callback)
+        self.callbacks[answerid] = callback
+        
+        self.exec_remote('''
+            import sys, StringIO
+            try:
+                execns
+            except:
+                execns = {}
+            oldout, olderr = sys.stdout, sys.stderr
+            try:
+                buffer = StringIO.StringIO()
+                sys.stdout = sys.stderr = buffer
+                try:
+                    exec compile(%(sourcecode)r, '<remote stdin>', 'single') in execns
+                except:
+                    import traceback
+                    traceback.print_exc()
+            finally:
+                sys.stdout=oldout
+                sys.stderr=olderr
+            # fiddle us (the caller) into executing the callback on remote answers
+            gateway.exec_remote(
+                "gateway.invoke_callback(%(answerid)r, %%r)" %% buffer.getvalue())
+        ''' % locals())
+
+    def invoke_callback(self, answerid, value):
+        callback = self.callbacks[answerid]
+        del self.callbacks[answerid]
+        callback(value)

Modified: shpy/trunk/dist/shpy/net/shell.py
==============================================================================
--- shpy/trunk/dist/shpy/net/shell.py	(original)
+++ shpy/trunk/dist/shpy/net/shell.py	Wed Jan 21 16:33:35 2004
@@ -67,7 +67,7 @@
                         try:
                             exec compile(line + '\n','<remote stdin>', 'single')
                         except:
-                            print print_exc()
+                            print_exc()
                     finally:
                         sys.stdout=oldout
                         sys.stderr=olderr

Modified: shpy/trunk/dist/shpy/ui_pygame.py
==============================================================================
--- shpy/trunk/dist/shpy/ui_pygame.py	(original)
+++ shpy/trunk/dist/shpy/ui_pygame.py	Wed Jan 21 16:33:35 2004
@@ -7,8 +7,8 @@
 from shpy.net.structure import Structure, representstructure, getstructureid
 from shpy import info 
 
-#RESOLUTION = (768, 512)
-RESOLUTION = (300, 300)
+RESOLUTION = (768, 512)
+#RESOLUTION = (300, 300)
 #FONT = 'lucon.ttf'
 FONT = None
 HEIGHT = 24
@@ -19,8 +19,7 @@
 KEYMAP = {}
 for name, value in pygame.locals.__dict__.items():
     if name.startswith('K_'):
-        KEYMAP[(value, False)] = name
-        KEYMAP[(value, True)] = 'CTRL_' + name
+        KEYMAP[value] = name
 
 ##LINECACHE = {}
 ##def rendertext(font, text, fgcolor, bgcolor=(255,255,255,255)):
@@ -34,9 +33,11 @@
 
 class Terminal:
     
-    def __init__(self, hostport):
+    def __init__(self, sharedserver, execserver):
         ns = {'terminal': self}
-        self.servergateway = shpy.net.register.ServerGateway(hostport, ns)
+        self.servergateway = shpy.net.register.ServerGateway(sharedserver, ns)
+        self.execgateway = shpy.net.register.ExecGateway(execserver)
+        
         pygame.init()
         pygame.key.set_repeat(500,30)
         self.screen = pygame.display.set_mode(RESOLUTION, RESIZABLE)
@@ -51,9 +52,8 @@
             self.cursor = Structure(x=0, color=info.getcolor())
             setattr(self.root.users, username, self.cursor)
         #print self.root.users.__dict__
-        self.celllist = self.root.cells.list
-        if not hasattr(self.cursor, 'cell') or self.cursor.cell not in self.celllist:
-            self.cursor.cell = self.celllist[-1]
+        if not hasattr(self.cursor, 'cell') or self.cursor.cell not in self.root.cells.list:
+            self.cursor.cell = self.root.cells.list[-1]
         if not hasattr(self.cursor, 'line') or self.cursor.line not in self.cursor.cell.lines:
             self.cursor.line = self.cursor.cell.lines[-1]
         self.lastline_cell = self.cursor.cell
@@ -70,8 +70,8 @@
             lines = self.lastline_cell.lines[:index+1]
             ypos = self.drawlinesbackwards(lines)
             if ypos > 0:
-                index = self.celllist.index(self.lastline_cell)
-                remainingcells = self.celllist[:index]
+                index = self.root.cells.list.index(self.lastline_cell)
+                remainingcells = self.root.cells.list[:index]
                 while remainingcells and ypos > 0:
                     cell = remainingcells.pop()
                     ypos = self.drawspacing(ypos)
@@ -124,8 +124,8 @@
             cursorindex = self.cursor.cell.lines.index(self.cursor.line)
             lastlineindex = self.lastline_cell.lines.index(self.lastline)
         else:
-            cursorindex = self.celllist.index(self.cursor.cell)
-            lastlineindex = self.celllist.index(self.lastline_cell)
+            cursorindex = self.root.cells.list.index(self.cursor.cell)
+            lastlineindex = self.root.cells.list.index(self.lastline_cell)
         if cursorindex < lastlineindex:
             self.lastline, self.lastline_cell = self.previousline(self.lastline, self.lastline_cell)
         else:
@@ -137,9 +137,9 @@
             return cell.lines[index-1], cell
         else:
             # XXX this changes if we have output cells ...
-            index = self.celllist.index(cell)
+            index = self.root.cells.list.index(cell)
             if index > 0:
-                cell = self.celllist[index-1]
+                cell = self.root.cells.list[index-1]
                 return cell.lines[-1], cell
         return line, cell
 
@@ -149,9 +149,9 @@
             return cell.lines[index+1], cell
         else:
             # XXX this changes if we have output cells ...
-            index = self.celllist.index(cell)
-            if index + 1 < len(self.celllist):
-                cell = self.celllist[index+1]
+            index = self.root.cells.list.index(cell)
+            if index + 1 < len(self.root.cells.list):
+                cell = self.root.cells.list[index+1]
                 return cell.lines[0], cell
         return line, cell
 
@@ -255,11 +255,33 @@
         self.changed[line] = 1
         self.changed[newline] = 1
 
+    def ALT_K_RETURN(self, event):
+        inputcell = self.cursor.cell
+        lines = [line.content for line in inputcell.lines]
+        outputcell = getattr(inputcell, 'outputcell', None)
+        if outputcell is None:
+            outputcell = Structure()
+            inputcell.outputcell = outputcell
+            index = self.root.cells.list.index(inputcell)
+            self.root.cells.list.insert(index+1, outputcell)
+            self.changed[inputcell] = 1
+            self.changed[self.root.cells] = 1
+        outputcell.lines = [Structure(content="")]
+        self.changed[outputcell] = 1
+        self.changed[outputcell.lines[0]] = 1
+        def fill_output_cell(value):
+            lines = value.split('\n')
+            outputcell.lines = [Structure(content=line) for line in lines]
+            self.changed[outputcell] = 1
+            for s in outputcell.lines:
+                self.changed[s] = 1
+        self.execgateway.userexec_remote(lines, fill_output_cell)
+
     def run(self):
         self.invalid = True
         while 1:
             if self.changed:
-                self.notifychanges(*self.changed.keys())
+                self.servergateway.notifychanges(*self.changed.keys())
                 self.changed.clear()
                 self.invalid = True
             if self.invalid:
@@ -269,9 +291,13 @@
             print event
             if event.type == KEYDOWN:
                 keynames = []
-                k = (event.key, bool(event.mod&KMOD_CTRL))
-                if k in KEYMAP:
-                    keynames.append(KEYMAP[k])
+                if event.key in KEYMAP:
+                    keyname = KEYMAP[event.key]
+                    if event.mod & KMOD_ALT:
+                        keyname = 'ALT_' + keyname
+                    if event.mod & KMOD_CTRL:
+                        keyname = 'CTRL_' + keyname
+                    keynames.append(keyname)
                 if event.unicode:
                     keynames.append("PRINTABLE_KEY")
                 for keyname in keynames:
@@ -296,28 +322,14 @@
         self.invalid = True
         pygame.event.post(pygame.event.Event(WAKEUPEVENT))
 
-    def notifychanges(self, *structures):
-        lines = [representstructure(structure) for structure in structures]
-        lines.append('''if 1:
-            for c in shared.getclientlist():
-                if c is not gateway:
-                    try:
-                        rerepresent = c.ns['representstructure']
-                        relines = []
-                        for structid in %r:
-                            obj = getstructure(structid)
-                            relines.append(rerepresent(obj))
-                        relines.append("terminal.postrepaintevent()")
-                        c.exec_remote('\\n'.join(relines))
-                    except:
-                        import traceback
-                        traceback.print_exc()
-        ''' % [getstructureid(struct) for struct in structures])
-        self.servergateway.exec_remote('\n'.join(lines))
-
 
 if __name__ == '__main__':
-    t = Terminal(sys.argv[1])
+    sharedserver = sys.argv[1]
+    if len(sys.argv) > 2:
+        execserver = sys.argv[2]
+    else:
+        execserver = 'localhost:8888'
+    t = Terminal(sharedserver, execserver)
     try:
         t.run()
     finally: