Thrift Python server code generation

Summary: Yep, it's up and running. We now have full client/server support in all of C++ Java PHP and Python. Well, not quite... there's no PHP server, but honestly who wants one? Actually, if we do want one the framework will support writing is as a PHP file that can be served in apache like a web service (i.e. restserver.php would be thriftserver.php). But now that's rambling and nothing to do with this commit.

Notes: cheever, let's chat about porting your multithreaded Pillar Python server over to Thrift


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664783 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/py/src/Thrift.py b/lib/py/src/Thrift.py
index e69de29..0c4a458 100644
--- a/lib/py/src/Thrift.py
+++ b/lib/py/src/Thrift.py
@@ -0,0 +1,6 @@
+class TProcessor:
+
+  """Base class for procsessor, which works on two streams."""
+
+  def process(itrans, otrans):
+    pass
diff --git a/lib/py/src/protocol/TBinaryProtocol.py b/lib/py/src/protocol/TBinaryProtocol.py
index c089ac5..860f461 100644
--- a/lib/py/src/protocol/TBinaryProtocol.py
+++ b/lib/py/src/protocol/TBinaryProtocol.py
@@ -70,7 +70,7 @@
     otrans.write(buff)
     
   def writeI64(self, otrans, i64):
-    buff = pack("!l", i64)
+    buff = pack("!q", i64)
     otrans.write(buff)
 
   def writeString(self, otrans, str):
@@ -150,7 +150,7 @@
 
   def readI64(self, itrans):
     buff = itrans.readAll(8)
-    val, = unpack('!l', buff)
+    val, = unpack('!q', buff)
     return val
 
   def readString(self, itrans):
diff --git a/lib/py/src/server/TServer.py b/lib/py/src/server/TServer.py
new file mode 100644
index 0000000..69be260
--- /dev/null
+++ b/lib/py/src/server/TServer.py
@@ -0,0 +1,32 @@
+from thrift.Thrift import TProcessor
+from thrift.transport import TTransport
+
+class TServer:
+
+  """Base interface for a server, which must have a run method."""
+
+  def __init__(self, proc):
+    self.processor = proc
+
+  def run(self):
+    pass
+
+class TSimpleServer(TServer):
+
+  """Simple single-threaded server that just pumps around one transport."""
+
+  def __init__(self, proc, trans):
+    TServer.__init__(self, proc)
+    self.transport = trans
+
+  def run(self):
+    self.transport.listen()
+    while True:
+      client = TTransport.TBufferedTransport(self.transport.accept())
+      try:
+        while True:
+          self.processor.process(client, client)
+      except Exception, x:
+        print x
+        print 'Client died.'
+      client.close()
diff --git a/lib/py/src/server/__init__.py b/lib/py/src/server/__init__.py
new file mode 100644
index 0000000..f7e08be
--- /dev/null
+++ b/lib/py/src/server/__init__.py
@@ -0,0 +1 @@
+__all__ = ["TServer"]
diff --git a/lib/py/src/transport/TSocket.py b/lib/py/src/transport/TSocket.py
index 52c6118..4ef35d9 100644
--- a/lib/py/src/transport/TSocket.py
+++ b/lib/py/src/transport/TSocket.py
@@ -5,15 +5,14 @@
 
   """Socket implementation of TTransport base."""
 
-  handle = None
-  host = "localhost"
-  port = 9090
-
-  def __init__(self, host, port):
+  def __init__(self, host='localhost', port=9090):
     self.host = host
     self.port = port
     self.handle = None
 
+  def set_handle(self, h):
+    self.handle = h
+
   def isOpen(self):
     return handle != None
 
@@ -36,10 +35,42 @@
 
   def read(self, sz):
     buff = self.handle.recv(sz)
+    if len(buff) == 0:
+      raise Exception('TScket read 0 bytes')
     return buff
 
   def write(self, buff):
-    self.handle.sendall(buff)
+    sent = 0
+    have = len(buff)
+    while sent < have:
+      plus = self.handle.send(buff)
+      if plus == 0:
+        raise Exception('sent 0 bytes')
+      sent += plus
+      buff = buff[plus:]
 
   def flush(self):
     pass
+
+class TServerSocket(TServerTransportBase):
+
+  """Socket implementation of TServerTransport base."""
+
+  def __init__(self, port):
+    self.port = port
+    self.handle = None
+ 
+  def listen(self):
+    self.handle = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    self.handle.bind(('', self.port))
+    self.handle.listen(128)
+
+  def accept(self):
+    (client, addr) = self.handle.accept()
+    result = TSocket()
+    result.set_handle(client)
+    return result
+
+  def close(self):
+    self.handle.close()
+    self.handle = None
diff --git a/lib/py/src/transport/TTransport.py b/lib/py/src/transport/TTransport.py
index e9e36d7..1e8b6c6 100644
--- a/lib/py/src/transport/TTransport.py
+++ b/lib/py/src/transport/TTransport.py
@@ -20,6 +20,48 @@
   def write(self, buf):
     pass
 
-  def flush():
+  def flush(self):
     pass
 
+class TServerTransportBase:
+
+  """Base class for Thrift server transports."""
+
+  def listen(self):
+    pass
+
+  def accept(self):
+    pass
+
+  def close(self):
+    pass
+
+class TBufferedTransport(TTransportBase):
+
+  """Class that wraps another transport and buffers its I/O."""
+
+  def __init__(self, trans):
+    self.__trans = trans
+    self.__buf = ''
+
+  def isOpen(self):
+    return self.__trans.isOpen()
+
+  def open(self):
+    return self.__trans.open()
+
+  def close(self):
+    return self.__trans.close()
+
+  def read(self, sz):
+    return self.__trans.read(sz)
+
+  def readAll(self, sz):
+    return self.__trans.readAll(sz)
+
+  def write(self, buf):
+    self.__buf += buf
+
+  def flush(self):
+    self.__trans.write(self.__buf)
+    self.__buf = ''