Implementation of the basic Thrift stack in Python

Summary: Framework, install script, base classes, TSocket, TBinaryProtocol

Notes: Code-gen is coming around the bend...


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664778 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/py/setup.py b/lib/py/setup.py
new file mode 100644
index 0000000..4beaba7
--- /dev/null
+++ b/lib/py/setup.py
@@ -0,0 +1,12 @@
+from distutils.core import setup
+
+setup(name = 'Thrift',
+      version = '1.0',
+      description = 'Thrift Python Libraries',
+      author = ['Mark Slee'],
+      author_email = ['mcslee@facebook.com'],
+      url = 'http://code.facebook.com/thrift',
+      packages = ['thrift', 'thrift.protocol', 'thrift.transport']
+      package_dir = {'thrift' : 'src'},
+      )
+
diff --git a/lib/py/src/Thrift.py b/lib/py/src/Thrift.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/py/src/Thrift.py
diff --git a/lib/py/src/__init__.py b/lib/py/src/__init__.py
new file mode 100644
index 0000000..9ddc3ce
--- /dev/null
+++ b/lib/py/src/__init__.py
@@ -0,0 +1 @@
+__all__ = ["Thrift"]
diff --git a/lib/py/src/protocol/TBinaryProtocol.py b/lib/py/src/protocol/TBinaryProtocol.py
new file mode 100644
index 0000000..c089ac5
--- /dev/null
+++ b/lib/py/src/protocol/TBinaryProtocol.py
@@ -0,0 +1,159 @@
+from TProtocol import *
+from struct import pack, unpack
+
+class TBinaryProtocol(TProtocolBase):
+
+  """Binary implementation of the Thrift protocol driver."""
+
+  def writeMessageBegin(self, otrans, name, type, seqid):
+    self.writeString(otrans, name)
+    self.writeByte(otrans, type)
+    self.writeI32(otrans, seqid)
+
+  def writeMessageEnd(self, otrans):
+    pass
+
+  def writeStructBegin(self, otrans, name):
+    pass
+
+  def writeStructEnd(self, otrans):
+    pass
+
+  def writeFieldBegin(self, otrans, name, type, id):
+    self.writeByte(otrans, type)
+    self.writeI16(otrans, id)
+
+  def writeFieldEnd(self, otrans):
+    pass
+
+  def writeFieldStop(self, otrans):
+    self.writeByte(otrans, TType.STOP);
+
+  def writeMapBegin(self, otrans, ktype, vtype, size):
+    self.writeByte(otrans, ktype)
+    self.writeByte(otrans, vtype)
+    self.writeI32(otrans, size)
+
+  def writeMapEnd(self, otrans):
+    pass
+
+  def writeListBegin(self, otrans, etype, size):
+    self.writeByte(otrans, etype)
+    self.writeI32(otrans, size)
+
+  def writeListEnd(self, otrans):
+    pass
+
+  def writeSetBegin(self, otrans, etype, size):
+    self.writeByte(otrans, etype)
+    self.writeI32(otrans, size)
+
+  def writeSetEnd(self, otrans):
+    pass
+
+  def writeBool(self, otrans, bool):
+    if bool:
+      self.writeByte(otrans, 1)
+    else:
+      self.writeByte(otrans, 0)
+    
+  def writeByte(self, otrans, byte):
+    buff = pack("!b", byte)
+    otrans.write(buff)
+
+  def writeI16(self, otrans, i16):
+    buff = pack("!h", i16)
+    otrans.write(buff)
+
+  def writeI32(self, otrans, i32):
+    buff = pack("!i", i32)
+    otrans.write(buff)
+    
+  def writeI64(self, otrans, i64):
+    buff = pack("!l", i64)
+    otrans.write(buff)
+
+  def writeString(self, otrans, str):
+    self.writeI32(otrans, len(str))
+    otrans.write(str)
+
+  def readMessageBegin(self, itrans):
+    name = self.readString(itrans)
+    type = self.readByte(itrans)
+    seqid = self.readI32(itrans)
+    return (name, type, seqid)
+
+  def readMessageEnd(self, itrans):
+    pass
+
+  def readStructBegin(self, itrans):
+    pass
+
+  def readStructEnd(self, itrans):
+    pass
+
+  def readFieldBegin(self, itrans):
+    type = self.readByte(itrans)
+    if type == TType.STOP:
+      return (None, type, 0)
+    id = self.readI16(itrans)
+    return (None, type, id)
+
+  def readFieldEnd(self, itrans):
+    pass
+
+  def readMapBegin(self, itrans):
+    ktype = self.readByte(itrans)
+    vtype = self.readByte(itrans)
+    size = self.readI32(itrans)
+    return (ktype, vtype, size)
+
+  def readMapEnd(self, itrans):
+    pass
+
+  def readListBegin(self, itrans):
+    etype = self.readByte(itrans)
+    size = self.readI32(itrans)
+    return (etype, size)
+
+  def readListEnd(self, itrans):
+    pass
+
+  def readSetBegin(self, itrans):
+    etype = self.readByte(itrans)
+    size = self.readI32(itrans)
+    return (etype, size)
+
+  def readSetEnd(self, itrans):
+    pass
+
+  def readBool(self, itrans):
+    byte = self.readByte(itrans)
+    if byte == 0:
+      return False
+    return True
+
+  def readByte(self, itrans):
+    buff = itrans.readAll(1)
+    val, = unpack('!b', buff)
+    return val
+
+  def readI16(self, itrans):
+    buff = itrans.readAll(2)
+    val, = unpack('!h', buff)
+    return val
+
+  def readI32(self, itrans):
+    buff = itrans.readAll(4)
+    val, = unpack('!i', buff)
+    return val
+
+  def readI64(self, itrans):
+    buff = itrans.readAll(8)
+    val, = unpack('!l', buff)
+    return val
+
+  def readString(self, itrans):
+    len = self.readI32(itrans)
+    str = itrans.readAll(len)
+    return str
diff --git a/lib/py/src/protocol/TProtocol.py b/lib/py/src/protocol/TProtocol.py
new file mode 100644
index 0000000..3105e25
--- /dev/null
+++ b/lib/py/src/protocol/TProtocol.py
@@ -0,0 +1,180 @@
+class TType:
+  STOP   = 0
+  VOID   = 1
+  BOOL   = 2
+  BYTE   = 3
+  I08    = 4
+  I16    = 6
+  I32    = 8
+  I64    = 10
+  STRING = 11
+  UTF7   = 11
+  STRUCT = 12
+  MAP    = 13
+  SET    = 14
+  LIST   = 15
+  UTF8   = 16
+  UTF16  = 17
+
+class TMessageType:
+  CALL  = 1
+  REPLY = 2
+
+class TProtocolBase:
+
+  """Base class for Thrift protocol driver."""
+
+  def writeMessageBegin(self, otrans, name, type, seqid):
+    pass
+
+  def writeMessageEnd(self, otrans):
+    pass
+
+  def writeStructBegin(self, otrans, name):
+    pass
+
+  def writeStructEnd(self, otrans):
+    pass
+
+  def writeFieldBegin(self, otrans, name, type, id):
+    pass
+
+  def writeFieldEnd(self, otrans):
+    pass
+
+  def writeFieldStop(self, otrans):
+    pass
+
+  def writeMapBegin(self, otrans, ktype, vtype, size):
+    pass
+
+  def writeMapEnd(self, otrans):
+    pass
+
+  def writeListBegin(self, otrans, etype, size):
+    pass
+
+  def writeListEnd(self, otrans):
+    pass
+
+  def writeSetBegin(self, otrans, etype, size):
+    pass
+
+  def writeSetEnd(self, otrans):
+    pass
+
+  def writeBool(self, otrans, bool):
+    pass
+
+  def writeByte(self, otrans, byte):
+    pass
+
+  def writeI16(self, otrans, i16):
+    pass
+
+  def writeI32(self, otrans, i32):
+    pass
+
+  def writeI64(self, otrans, i64):
+    pass
+
+  def writeString(self, otrans, str):
+    pass
+
+  def readMessageBegin(self, itrans):
+    pass
+
+  def readMessageEnd(self, itrans):
+    pass
+
+  def readStructBegin(self, itrans):
+    pass
+
+  def readStructEnd(self, itrans):
+    pass
+
+  def readFieldBegin(self, itrans):
+    pass
+
+  def readFieldEnd(self, itrans):
+    pass
+
+  def readMapBegin(self, itrans):
+    pass
+
+  def readMapEnd(self, itrans):
+    pass
+
+  def readListBegin(self, itrans):
+    pass
+
+  def readListEnd(self, itrans):
+    pass
+
+  def readSetBegin(self, itrans):
+    pass
+
+  def readSetEnd(self, itrans):
+    pass
+
+  def readBool(self, itrans):
+    pass
+
+  def readByte(self, itrans):
+    pass
+
+  def readI16(self, itrans):
+    pass
+
+  def readI32(self, itrans):
+    pass
+
+  def readI64(self, itrans):
+    pass
+
+  def readString(self, itrans):
+    pass
+
+  def skip(self, itrans, type):
+    if type == TType.STOP:
+      return
+    elif type == TType.BOOL:
+      self.readBool(itrans)
+    elif type == TType.BYTE:
+      self.readByte(itrans)
+    elif type == TType.I16:
+      self.readI16(itrans)
+    elif type == TType.I32:
+      self.readI32(itrans)
+    elif type == TType.I64:
+      self.readI64(itrans)
+    elif type == TType.STRING:
+      self.readString(itrans)
+    elif type == TType.STRUCT:
+      name = self.readStructBegin(itrans)
+      while True:
+        (name, type, id) = self.readFieldBegin(itrans)
+        if type == TType.STOP:
+          break
+        self.skip(itrans, type)
+        self.readFieldEnd(itrans)
+      self.readStructEnd(itrans)
+    elif type == TType.MAP:
+      (ktype, vtype, size) = self.readMapBegin(itrans)
+      for i in range(size):
+        self.skip(itrans, ktype)
+        self.skip(itrans, vtype)
+      self.readMapEnd(itrans)
+    elif type == TType.SET:
+      (etype, size) = self.readSetBegin(itrans)
+      for i in range(size):
+        self.skip(itrans, etype)
+      self.readSetEnd(itrans)
+    elif type == TType.LIST:
+      (etype, size) = self.readListBegin(itrans)
+      for i in range(size):
+        self.skip(itrans, etype)
+      self.readListEnd(itrans)
+
+
+    
diff --git a/lib/py/src/protocol/__init__.py b/lib/py/src/protocol/__init__.py
new file mode 100644
index 0000000..e0c1c5e
--- /dev/null
+++ b/lib/py/src/protocol/__init__.py
@@ -0,0 +1 @@
+__all__ = ["TProtocol", "TBinaryProtocol"]
diff --git a/lib/py/src/transport/TSocket.py b/lib/py/src/transport/TSocket.py
new file mode 100644
index 0000000..52c6118
--- /dev/null
+++ b/lib/py/src/transport/TSocket.py
@@ -0,0 +1,45 @@
+from TTransport import *
+import socket
+
+class TSocket(TTransportBase):
+
+  """Socket implementation of TTransport base."""
+
+  handle = None
+  host = "localhost"
+  port = 9090
+
+  def __init__(self, host, port):
+    self.host = host
+    self.port = port
+    self.handle = None
+
+  def isOpen(self):
+    return handle != None
+
+  def open(self):
+    self.handle = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    self.handle.connect((self.host, self.port))
+
+  def close(self):
+    self.handle.close()
+    self.handle = None
+
+  def readAll(self, sz):
+    buff = ''
+    have = 0
+    while (have < sz):
+      chunk = self.read(sz-have)
+      have += len(chunk)
+      buff += chunk
+    return buff
+
+  def read(self, sz):
+    buff = self.handle.recv(sz)
+    return buff
+
+  def write(self, buff):
+    self.handle.sendall(buff)
+
+  def flush(self):
+    pass
diff --git a/lib/py/src/transport/TTransport.py b/lib/py/src/transport/TTransport.py
new file mode 100644
index 0000000..e9e36d7
--- /dev/null
+++ b/lib/py/src/transport/TTransport.py
@@ -0,0 +1,25 @@
+class TTransportBase:
+
+  """Base class for Thrift transport layer."""
+
+  def isOpen(self):
+    pass
+
+  def open(self):
+    pass
+
+  def close(self):
+    pass
+
+  def read(self, sz):
+    pass
+
+  def readAll(self, sz):
+    pass
+
+  def write(self, buf):
+    pass
+
+  def flush():
+    pass
+
diff --git a/lib/py/src/transport/__init__.py b/lib/py/src/transport/__init__.py
new file mode 100644
index 0000000..fb50c8b
--- /dev/null
+++ b/lib/py/src/transport/__init__.py
@@ -0,0 +1 @@
+__all__ = ["TTransport", "TSocket"]