THRIFT-5923: UUID python
Client: py
Patch: CJCombrink

This closes #3330
diff --git a/lib/py/src/protocol/TBinaryProtocol.py b/lib/py/src/protocol/TBinaryProtocol.py
index af64ec1..b73e3c9 100644
--- a/lib/py/src/protocol/TBinaryProtocol.py
+++ b/lib/py/src/protocol/TBinaryProtocol.py
@@ -18,6 +18,7 @@
 #
 
 from struct import pack, unpack
+import uuid
 
 from .TProtocol import TType, TProtocolBase, TProtocolException, TProtocolFactory
 
@@ -131,6 +132,9 @@
         self.writeI32(len(str))
         self.trans.write(str)
 
+    def writeUuid(self, uuid):
+        self.trans.write(uuid.bytes)
+
     def readMessageBegin(self):
         sz = self.readI32()
         if sz < 0:
@@ -235,6 +239,11 @@
         s = self.trans.readAll(size)
         return s
 
+    def readUuid(self):
+        buff = self.trans.readAll(16)
+        val = uuid.UUID(bytes=buff)
+        return val
+
 
 class TBinaryProtocolFactory(TProtocolFactory):
     def __init__(self, strictRead=False, strictWrite=True, **kwargs):
diff --git a/lib/py/src/protocol/TCompactProtocol.py b/lib/py/src/protocol/TCompactProtocol.py
index a3527cd..b58095a 100644
--- a/lib/py/src/protocol/TCompactProtocol.py
+++ b/lib/py/src/protocol/TCompactProtocol.py
@@ -19,6 +19,7 @@
 
 from .TProtocol import TType, TProtocolBase, TProtocolException, TProtocolFactory, checkIntegerLimits
 from struct import pack, unpack
+import uuid
 
 __all__ = ['TCompactProtocol', 'TCompactProtocolFactory']
 
@@ -80,6 +81,7 @@
         shift += 7
 
 
+# As per TCompactProtocol.tcc
 class CompactType(object):
     STOP = 0x00
     TRUE = 0x01
@@ -94,6 +96,7 @@
     SET = 0x0A
     MAP = 0x0B
     STRUCT = 0x0C
+    UUID = 0x0D
 
 
 CTYPES = {
@@ -109,6 +112,7 @@
     TType.LIST: CompactType.LIST,
     TType.SET: CompactType.SET,
     TType.MAP: CompactType.MAP,
+    TType.UUID: CompactType.UUID,
 }
 
 TTYPES = {}
@@ -276,6 +280,10 @@
     def writeDouble(self, dub):
         self.trans.write(pack('<d', dub))
 
+    @writer
+    def writeUuid(self, uuid):
+        self.trans.write(uuid.bytes)
+
     def __writeBinary(self, s):
         self.__writeSize(len(s))
         self.trans.write(s)
@@ -416,6 +424,12 @@
         val, = unpack('<d', buff)
         return val
 
+    @reader
+    def readUuid(self):
+        buff = self.trans.readAll(16)
+        val = uuid.UUID(bytes=buff)
+        return val
+
     def __readBinary(self):
         size = self.__readSize()
         self._check_string_length(size)
diff --git a/lib/py/src/protocol/TJSONProtocol.py b/lib/py/src/protocol/TJSONProtocol.py
index 004a40a..6782eeb 100644
--- a/lib/py/src/protocol/TJSONProtocol.py
+++ b/lib/py/src/protocol/TJSONProtocol.py
@@ -21,6 +21,7 @@
                         TProtocolFactory, checkIntegerLimits)
 import base64
 import math
+import uuid
 
 
 __all__ = ['TJSONProtocol',
@@ -64,6 +65,7 @@
 }
 NUMERIC_CHAR = b'+-.0123456789Ee'
 
+# Type names as TJSONProtocol.cpp
 CTYPES = {
     TType.BOOL: 'tf',
     TType.BYTE: 'i8',
@@ -76,6 +78,7 @@
     TType.LIST: 'lst',
     TType.SET: 'set',
     TType.MAP: 'map',
+    TType.UUID: 'uid',
 }
 
 JTYPES = {}
@@ -480,6 +483,11 @@
     def readBinary(self):
         return self.readJSONBase64()
 
+    def readUuid(self):
+        buff = self.readJSONString(False)
+        val = uuid.UUID(buff)
+        return val
+
     def writeMessageBegin(self, name, request_type, seqid):
         self.resetWriteContext()
         self.writeJSONArrayStart()
@@ -565,6 +573,9 @@
     def writeBinary(self, binary):
         self.writeJSONBase64(binary)
 
+    def writeUuid(self, uuid):
+        self.writeJSONString(str(uuid))
+
 
 class TJSONProtocolFactory(TProtocolFactory):
     def getProtocol(self, trans):
@@ -659,6 +670,9 @@
     def writeBinary(self, binary):
         self.writeJSONBase64(binary)
 
+    def writeUuid(self, uuid):
+        self.writeJSONString(str(uuid))
+
 
 class TSimpleJSONProtocolFactory(TProtocolFactory):
 
diff --git a/lib/py/src/protocol/TProtocol.py b/lib/py/src/protocol/TProtocol.py
index a0d1452..975cbf5 100644
--- a/lib/py/src/protocol/TProtocol.py
+++ b/lib/py/src/protocol/TProtocol.py
@@ -117,7 +117,10 @@
     def writeString(self, str_val):
         self.writeBinary(bytes(str_val, 'utf-8'))
 
-    def writeBinary(self, str_val):
+    def writeBinary(self, uuid):
+        pass
+
+    def writeUuid(self, str_val):
         pass
 
     def readMessageBegin(self):
@@ -180,6 +183,9 @@
     def readBinary(self):
         pass
 
+    def readUuid(self):
+        pass
+
     def skip(self, ttype):
         if ttype == TType.BOOL:
             self.readBool()
@@ -220,6 +226,8 @@
             for i in range(size):
                 self.skip(etype)
             self.readListEnd()
+        elif ttype == TType.UUID:
+            self.readUuid()
         else:
             raise TProtocolException(
                 TProtocolException.INVALID_DATA,
@@ -243,8 +251,7 @@
         ('readContainerMap', 'writeContainerMap', True),  # 13 TType.MAP
         ('readContainerSet', 'writeContainerSet', True),  # 14 TType.SET
         ('readContainerList', 'writeContainerList', True),  # 15 TType.LIST
-        (None, None, False),  # 16 TType.UTF8 # TODO: handle utf8 types?
-        (None, None, False)  # 17 TType.UTF16 # TODO: handle utf16 types?
+        ('readUuid', 'writeUuid', False),  # 16 TType.UUID
     )
 
     def _ttype_handlers(self, ttype, spec):