From 36f80add4f98937b0450e61b2860e6b6bf4f8048 Mon Sep 17 00:00:00 2001 From: Carel Combrink Date: Thu, 30 Oct 2025 06:44:21 +0000 Subject: [PATCH 01/13] Starting to add UUID support for Python --- lib/py/src/Thrift.py | 2 ++ lib/py/src/protocol/TBinaryProtocol.py | 8 +++++++ lib/py/test/thrift_TBinaryProtocol.py | 17 +++++++++++++- test/py/Makefile.am | 32 +++++++++++++------------- test/py/TestClient.py | 8 +++++++ test/py/TestServer.py | 5 ++++ 6 files changed, 55 insertions(+), 17 deletions(-) diff --git a/lib/py/src/Thrift.py b/lib/py/src/Thrift.py index 81fe8cf33fe..bd03e533c9c 100644 --- a/lib/py/src/Thrift.py +++ b/lib/py/src/Thrift.py @@ -36,6 +36,7 @@ class TType(object): LIST = 15 UTF8 = 16 UTF16 = 17 + UUID = 18 _VALUES_TO_NAMES = ( 'STOP', @@ -56,6 +57,7 @@ class TType(object): 'LIST', 'UTF8', 'UTF16', + 'UUID', ) diff --git a/lib/py/src/protocol/TBinaryProtocol.py b/lib/py/src/protocol/TBinaryProtocol.py index af64ec10356..788a6640236 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 @@ def writeBinary(self, str): 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,10 @@ def readBinary(self): 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/test/thrift_TBinaryProtocol.py b/lib/py/test/thrift_TBinaryProtocol.py index c4777ad831d..e84bfe1e846 100644 --- a/lib/py/test/thrift_TBinaryProtocol.py +++ b/lib/py/test/thrift_TBinaryProtocol.py @@ -18,6 +18,7 @@ # import unittest +import uuid import _import_local_thrift # noqa from thrift.protocol.TBinaryProtocol import TBinaryProtocol @@ -52,6 +53,9 @@ def testNaked(type, data): if type.capitalize() == 'Bool': protocol.writeBool(data) + if type.capitalize() == 'Uuid': + protocol.writeUuid(data) + transport.flush() data_r = buf.getvalue() buf = TTransport.TMemoryBuffer(data_r) @@ -81,9 +85,12 @@ def testNaked(type, data): if type.capitalize() == 'Bool': return protocol.readBool() + if type.capitalize() == 'Uuid': + return protocol.readUuid() + def testField(type, data): - TType = {"Bool": 2, "Byte": 3, "Binary": 5, "I16": 6, "I32": 8, "I64": 10, "Double": 11, "String": 12} + TType = {"Bool": 2, "Byte": 3, "Binary": 5, "I16": 6, "I32": 8, "I64": 10, "Double": 11, "String": 12, "Uuid": 13} buf = TTransport.TMemoryBuffer() transport = TTransport.TBufferedTransportFactory().getTransport(buf) protocol = TBinaryProtocol(transport) @@ -113,6 +120,9 @@ def testField(type, data): if type.capitalize() == 'Bool': protocol.writeBool(data) + if type.capitalize() == 'Uuid': + protocol.writeUuid(data) + protocol.writeFieldEnd() protocol.writeStructEnd() @@ -148,6 +158,9 @@ def testField(type, data): if type.capitalize() == 'Bool': return protocol.readBool() + if type.capitalize() == 'Uuid': + return protocol.readUuid() + protocol.readFieldEnd() protocol.readStructEnd() @@ -245,6 +258,8 @@ def test_TBinaryProtocol_write_read(self): self.assertEqual(True, testField('Bool', True)) self.assertEqual(3.1415926, testNaked("Double", 3.1415926)) self.assertEqual("hello thrift", testNaked("String", "hello thrift")) + self.assertEqual(uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}'), testNaked("Uuid", uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}'))) + self.assertEqual(uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}'), testField("Uuid", uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}'))) TMessageType = {"T_CALL": 1, "T_REPLY": 2, "T_EXCEPTION": 3, "T_ONEWAY": 4} test_data = [("short message name", TMessageType['T_CALL'], 0), diff --git a/test/py/Makefile.am b/test/py/Makefile.am index 078ba02ddcf..df3a647fb08 100644 --- a/test/py/Makefile.am +++ b/test/py/Makefile.am @@ -77,50 +77,50 @@ TESTS= $(py_unit_tests) gen-py/%/__init__.py: ../%.thrift $(THRIFT) - test -f ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py ../v0.16/$(notdir $<) \ + test -f ../$(notdir $<) \ + && $(THRIFT) --gen py ../$(notdir $<) \ || $(THRIFT) --gen py $< gen-py-default/%/__init__.py: ../%.thrift $(THRIFT) test -d gen-py-default || $(MKDIR_P) gen-py-default - test -f ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py -out gen-py-default ../v0.16/$(notdir $<) \ + test -f ../$(notdir $<) \ + && $(THRIFT) --gen py -out gen-py-default ../$(notdir $<) \ || $(THRIFT) --gen py -out gen-py-default $< gen-py-slots/%/__init__.py: ../%.thrift $(THRIFT) test -d gen-py-slots || $(MKDIR_P) gen-py-slots - test ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py:slots -out gen-py-slots ../v0.16/$(notdir $<) \ + test ../$(notdir $<) \ + && $(THRIFT) --gen py:slots -out gen-py-slots ../$(notdir $<) \ || $(THRIFT) --gen py:slots -out gen-py-slots $< gen-py-oldstyle/%/__init__.py: ../%.thrift $(THRIFT) test -d gen-py-oldstyle || $(MKDIR_P) gen-py-oldstyle - test ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py:old_style -out gen-py-oldstyle ../v0.16/$(notdir $<) \ + test ../$(notdir $<) \ + && $(THRIFT) --gen py:old_style -out gen-py-oldstyle ../$(notdir $<) \ || $(THRIFT) --gen py:old_style -out gen-py-oldstyle $< gen-py-no_utf8strings/%/__init__.py: ../%.thrift $(THRIFT) test -d gen-py-no_utf8strings || $(MKDIR_P) gen-py-no_utf8strings - test ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py:no_utf8strings -out gen-py-no_utf8strings ../v0.16/$(notdir $<) \ + test ../$(notdir $<) \ + && $(THRIFT) --gen py:no_utf8strings -out gen-py-no_utf8strings ../$(notdir $<) \ || $(THRIFT) --gen py:no_utf8strings -out gen-py-no_utf8strings $< gen-py-dynamic/%/__init__.py: ../%.thrift $(THRIFT) test -d gen-py-dynamic || $(MKDIR_P) gen-py-dynamic - test ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py:dynamic -out gen-py-dynamic ../v0.16/$(notdir $<) \ + test ../$(notdir $<) \ + && $(THRIFT) --gen py:dynamic -out gen-py-dynamic ../$(notdir $<) \ || $(THRIFT) --gen py:dynamic -out gen-py-dynamic $< gen-py-dynamicslots/%/__init__.py: ../%.thrift $(THRIFT) test -d gen-py-dynamicslots || $(MKDIR_P) gen-py-dynamicslots - test ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py:dynamic,slots -out gen-py-dynamicslots ../v0.16/$(notdir $<) \ + test ../$(notdir $<) \ + && $(THRIFT) --gen py:dynamic,slots -out gen-py-dynamicslots ../$(notdir $<) \ || $(THRIFT) --gen py:dynamic,slots -out gen-py-dynamicslots $< gen-py-enum/%/__init__.py: ../%.thrift $(THRIFT) test -d gen-py-enum || $(MKDIR_P) gen-py-enum - test ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py:enum -out gen-py-enum ../v0.16/$(notdir $<) \ + test ../$(notdir $<) \ + && $(THRIFT) --gen py:enum -out gen-py-enum ../$(notdir $<) \ || $(THRIFT) --gen py:enum -out gen-py-enum $< gen-py-type_hints/%/__init__.py: ../%.thrift $(THRIFT) diff --git a/test/py/TestClient.py b/test/py/TestClient.py index f608e4e35e2..07b39a0dc9c 100755 --- a/test/py/TestClient.py +++ b/test/py/TestClient.py @@ -24,6 +24,7 @@ import sys import time import unittest +import uuid from optparse import OptionParser from util import local_libpath @@ -160,6 +161,13 @@ def testBinary(self): val = bytearray([i for i in range(0, 256)]) self.assertEqual(bytearray(self.client.testBinary(bytes(val))), val) + def testUuid(self): + print('testUuid') + val1 = uuid.UUID('00112233-4455-6677-8899-aabbccddeeff') + val2 = uuid.uuid4() + self.assertEqual(self.client.testUuid(val1), val1) + self.assertEqual(self.client.testUuid(val2), val2) + def testStruct(self): print('testStruct') x = Xtruct() diff --git a/test/py/TestServer.py b/test/py/TestServer.py index 0108c9882dd..4327daa8713 100755 --- a/test/py/TestServer.py +++ b/test/py/TestServer.py @@ -82,6 +82,11 @@ def testBinary(self, thing): logging.info('testBinary()') # TODO: hex output return thing + def testUuid(self, thing): + if options.verbose > 1: + logging.info('testUuid(%s)' % thing) + return thing + def testStruct(self, thing): if self.options.verbose > 1: logging.info('testStruct({%s, %s, %s, %s})' % (thing.string_thing, thing.byte_thing, thing.i32_thing, thing.i64_thing)) From 9c6b6c63b4e17c6afda4c1ea9305419f44116f11 Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Mon, 2 Mar 2026 06:53:25 +0000 Subject: [PATCH 02/13] Remove commented out code and duplicate call to make --- .github/workflows/build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47b2dfd993a..c2a7dde1285 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -577,12 +577,6 @@ jobs: - name: Run make install for python run: sudo make -C lib/py install - # - name: Run make install-exec-hook for python - # run: sudo make -C lib/py install-exec-hook - - - name: Run make for python libs - run: make -C lib/py - - name: Run make check for python libs run: make -C lib/py check From f8242ae5ba4a84ddfc9e4d4046dc5f22dbc01367 Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Mon, 2 Mar 2026 11:39:14 +0000 Subject: [PATCH 03/13] Update script to detect changes and handle failures more consistent - Correctly fail with error code when there are errors so that cmake/make fails - Set up dependencies so that changes are built for any affected file --- lib/py/setup.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/py/setup.py b/lib/py/setup.py index 2dd2a77aa32..3cf3f74e35e 100644 --- a/lib/py/setup.py +++ b/lib/py/setup.py @@ -85,6 +85,14 @@ def run_setup(with_binary): 'src/ext/binary.cpp', 'src/ext/compact.cpp', ], + depends=[ + 'src/ext/binary.h', + 'src/ext/compact.h', + 'src/ext/endian.h', + 'src/ext/protocol.h', + 'src/ext/protocol.tcc', + 'src/ext/types.h', + ], include_dirs=include_dirs, ) ], @@ -138,6 +146,8 @@ def run_setup(with_binary): try: with_binary = True run_setup(with_binary) + sys.exit(0) + except BuildFailed: print() print('*' * 80) @@ -146,4 +156,16 @@ def run_setup(with_binary): print('*' * 80) print() +# Retry but without the binary +try: run_setup(False) + sys.exit(0) + +except : + print() + print('*' * 80) + print("An error occurred while trying to compile without the C extension enabled") + print("Build failed") + print('*' * 80) + print() + sys.exit(1) From 6fca12bc61eb4156282e14640c8e06003ae325db Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Mon, 2 Mar 2026 11:46:58 +0000 Subject: [PATCH 04/13] Implement compact protocol --- lib/py/src/protocol/TCompactProtocol.py | 13 +++++++++++++ lib/py/test/thrift_TCompactProtocol.py | 20 +++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/py/src/protocol/TCompactProtocol.py b/lib/py/src/protocol/TCompactProtocol.py index a3527cd47a3..1bc3da703a2 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'] @@ -94,6 +95,7 @@ class CompactType(object): SET = 0x0A MAP = 0x0B STRUCT = 0x0C + UUID = 0x0D CTYPES = { @@ -109,6 +111,7 @@ class CompactType(object): TType.LIST: CompactType.LIST, TType.SET: CompactType.SET, TType.MAP: CompactType.MAP, + TType.UUID: CompactType.UUID, } TTYPES = {} @@ -276,6 +279,10 @@ def writeI64(self, i64): def writeDouble(self, dub): self.trans.write(pack(' Date: Tue, 3 Mar 2026 10:54:20 +0000 Subject: [PATCH 05/13] Further implement and fix UUID support for thrift - Tested against itself and C++ to start with - Cleaned out known_failures (some had unsupported protocols even) --- lib/py/src/Thrift.py | 8 +- lib/py/src/ext/binary.h | 12 ++ lib/py/src/ext/compact.cpp | 37 ++-- lib/py/src/ext/compact.h | 19 +- lib/py/src/ext/protocol.tcc | 62 ++++++- lib/py/src/ext/types.h | 5 +- lib/py/src/protocol/TCompactProtocol.py | 2 +- lib/py/src/protocol/TJSONProtocol.py | 14 ++ lib/py/src/protocol/TProtocol.py | 13 +- test/known_failures_Linux.json | 233 +----------------------- test/py/Makefile.am | 4 +- test/py/TestTypes.py | 4 + test/tests.json | 2 +- 13 files changed, 151 insertions(+), 264 deletions(-) diff --git a/lib/py/src/Thrift.py b/lib/py/src/Thrift.py index bd03e533c9c..9c051d6d34d 100644 --- a/lib/py/src/Thrift.py +++ b/lib/py/src/Thrift.py @@ -34,9 +34,9 @@ class TType(object): MAP = 13 SET = 14 LIST = 15 - UTF8 = 16 - UTF16 = 17 - UUID = 18 + UTF8 = 11 + UTF16 = 11 + UUID = 16 _VALUES_TO_NAMES = ( 'STOP', @@ -57,7 +57,7 @@ class TType(object): 'LIST', 'UTF8', 'UTF16', - 'UUID', + 'UUID', ) diff --git a/lib/py/src/ext/binary.h b/lib/py/src/ext/binary.h index 960b0d003a4..dd7750b49a8 100644 --- a/lib/py/src/ext/binary.h +++ b/lib/py/src/ext/binary.h @@ -88,6 +88,10 @@ class BinaryProtocol : public ProtocolBase { return encodeValue(value, parsedspec.type, parsedspec.typeargs); } + void writeUuid(char* value) { + writeBuffer(value, 16); + } + void writeFieldStop() { writeByte(static_cast(T_STOP)); } bool readBool(bool& val) { @@ -159,6 +163,13 @@ class BinaryProtocol : public ProtocolBase { return len; } + int32_t readUuid(char** buf) { + if (!readBytes(buf, 16)) { + return -1; + } + return 16; + } + int32_t readListBegin(TType& etype) { int32_t len; uint8_t b = 0; @@ -206,6 +217,7 @@ class BinaryProtocol : public ProtocolBase { } SKIPBYTES(len); } + bool skipUuid() { SKIPBYTES(16); } #undef SKIPBYTES private: diff --git a/lib/py/src/ext/compact.cpp b/lib/py/src/ext/compact.cpp index ae89f2a6558..8d13d3d196f 100644 --- a/lib/py/src/ext/compact.cpp +++ b/lib/py/src/ext/compact.cpp @@ -24,23 +24,26 @@ namespace apache { namespace thrift { namespace py { +/** Mapping of Compact type to Thrift Type according. + * This list must match the TType enum in TEnum.h */ const uint8_t CompactProtocol::TTypeToCType[] = { - CT_STOP, // T_STOP - 0, // unused - CT_BOOLEAN_TRUE, // T_BOOL - CT_BYTE, // T_BYTE - CT_DOUBLE, // T_DOUBLE - 0, // unused - CT_I16, // T_I16 - 0, // unused - CT_I32, // T_I32 - 0, // unused - CT_I64, // T_I64 - CT_BINARY, // T_STRING - CT_STRUCT, // T_STRUCT - CT_MAP, // T_MAP - CT_SET, // T_SET - CT_LIST, // T_LIST +/* 0 */ CT_STOP, // T_STOP +/* 1 */ 0, // unused +/* 2 */ CT_BOOLEAN_TRUE, // T_BOOL +/* 3 */ CT_BYTE, // T_BYTE +/* 4 */ CT_DOUBLE, // T_DOUBLE +/* 5 */ 0, // unused +/* 6 */ CT_I16, // T_I16 +/* 7 */ 0, // unused +/* 8 */ CT_I32, // T_I32 +/* 9 */ 0, // unused +/* 10 */ CT_I64, // T_I64 +/* 11 */ CT_BINARY, // T_STRING +/* 12 */ CT_STRUCT, // T_STRUCT +/* 13 */ CT_MAP, // T_MAP +/* 14 */ CT_SET, // T_SET +/* 15 */ CT_LIST, // T_LIST +/* 16 */ CT_UUID, // T_UUID }; bool CompactProtocol::readFieldBegin(TType& type, int16_t& tag) { @@ -98,6 +101,8 @@ TType CompactProtocol::getTType(uint8_t type) { return T_MAP; case CT_STRUCT: return T_STRUCT; + case CT_UUID: + return T_UUID; default: PyErr_Format(PyExc_TypeError, "don't know what type: %d", type); return static_cast(-1); diff --git a/lib/py/src/ext/compact.h b/lib/py/src/ext/compact.h index 8f72b092596..0d8946b3441 100644 --- a/lib/py/src/ext/compact.h +++ b/lib/py/src/ext/compact.h @@ -104,6 +104,10 @@ class CompactProtocol : public ProtocolBase { void writeFieldStop() { writeByte(0); } + void writeUuid(char* value) { + writeBuffer(value, 16); + } + bool readBool(bool& val) { if (readBool_.exists) { readBool_.exists = false; @@ -231,6 +235,13 @@ class CompactProtocol : public ProtocolBase { } bool readFieldBegin(TType& type, int16_t& tag); + bool readUuid(char** buf) { + if (!readBytes(buf, 16)) { + return false; + } + return true; + } + bool skipBool() { bool val; return readBool(val); @@ -263,6 +274,9 @@ class CompactProtocol : public ProtocolBase { } SKIPBYTES(len); } + bool skipUuid() { + SKIPBYTES(16); + } #undef SKIPBYTES private: @@ -279,7 +293,8 @@ class CompactProtocol : public ProtocolBase { CT_LIST = 0x09, CT_SET = 0x0A, CT_MAP = 0x0B, - CT_STRUCT = 0x0C + CT_STRUCT = 0x0C, + CT_UUID = 0x0D, }; static const uint8_t TTypeToCType[]; @@ -288,7 +303,7 @@ class CompactProtocol : public ProtocolBase { int toCompactType(TType type) { int i = static_cast(type); - return i < 16 ? TTypeToCType[i] : -1; + return i <= 16 ? TTypeToCType[i] : -1; } uint32_t toZigZag(int32_t val) { return (val >> 31) ^ (val << 1); } diff --git a/lib/py/src/ext/protocol.tcc b/lib/py/src/ext/protocol.tcc index aad5a3c88e5..e92580eacdb 100644 --- a/lib/py/src/ext/protocol.tcc +++ b/lib/py/src/ext/protocol.tcc @@ -542,10 +542,32 @@ bool ProtocolBase::encodeValue(PyObject* value, TType type, PyObject* type return true; } + case T_UUID: { + ScopedPyObject instval(PyObject_GetAttrString(value, "bytes")); + if (!instval) { + return false; + } + + Py_ssize_t size; + char* buffer; + + if (PyBytes_AsStringAndSize(instval.get(), &buffer, &size) < 0) { + // Python exception is already set (e.g. TypeError, MemoryError) + // Thrift accelerator functions usually just return NULL here + return false; // or whatever your writeXXX method returns on error + } + // Optional but strongly recommended for safety: + if (size != 16) { + PyErr_SetString(PyExc_TypeError, "uuid.bytes must be exactly 16 bytes long"); + return false; + } + + impl()->writeUuid(buffer); + return true; + } + case T_STOP: case T_VOID: - case T_UTF16: - case T_UTF8: case T_U64: default: PyErr_Format(PyExc_TypeError, "Unexpected TType for encodeValue: %d", type); @@ -625,11 +647,12 @@ bool ProtocolBase::skip(TType type) { } return true; } + case T_UUID: { + return impl()->skipUuid(); + } case T_STOP: case T_VOID: - case T_UTF16: - case T_UTF8: case T_U64: default: PyErr_Format(PyExc_TypeError, "Unexpected TType for skip: %d", type); @@ -816,10 +839,37 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { return readStruct(Py_None, parsedargs.klass, parsedargs.spec); } + case T_UUID: { + char* buf = nullptr; + if(!impl()->readUuid(&buf)) { + return nullptr; + } + + PyObject* uuid_mod = PyImport_ImportModule("uuid"); + if (!uuid_mod) return NULL; + + PyObject* UUID_type = PyObject_GetAttrString(uuid_mod, "UUID"); + Py_DECREF(uuid_mod); + if (!UUID_type) return NULL; + + PyObject* py_bytes = PyBytes_FromStringAndSize(buf, 16); + if (!py_bytes) { + Py_DECREF(UUID_type); + return NULL; + } + + PyObject* kwargs = Py_BuildValue("{s:O}", "bytes", py_bytes); + Py_DECREF(py_bytes); + + PyObject* uuid_obj = PyObject_Call(UUID_type, PyTuple_New(0), kwargs); + Py_DECREF(kwargs); + Py_DECREF(UUID_type); + + return uuid_obj; + } + case T_STOP: case T_VOID: - case T_UTF16: - case T_UTF8: case T_U64: default: PyErr_Format(PyExc_TypeError, "Unexpected TType for decodeValue: %d", type); diff --git a/lib/py/src/ext/types.h b/lib/py/src/ext/types.h index 9b45dd065f5..ba75a29a683 100644 --- a/lib/py/src/ext/types.h +++ b/lib/py/src/ext/types.h @@ -76,8 +76,9 @@ enum TType { T_MAP = 13, T_SET = 14, T_LIST = 15, - T_UTF8 = 16, - T_UTF16 = 17 + T_UTF8 = 11, + T_UTF16 = 11, + T_UUID = 16, }; // replace with unique_ptr when we're OK with C++11 diff --git a/lib/py/src/protocol/TCompactProtocol.py b/lib/py/src/protocol/TCompactProtocol.py index 1bc3da703a2..13e1904018e 100644 --- a/lib/py/src/protocol/TCompactProtocol.py +++ b/lib/py/src/protocol/TCompactProtocol.py @@ -80,7 +80,7 @@ def readVarint(trans): return result shift += 7 - +# As per TCompactProtocol.tcc class CompactType(object): STOP = 0x00 TRUE = 0x01 diff --git a/lib/py/src/protocol/TJSONProtocol.py b/lib/py/src/protocol/TJSONProtocol.py index 004a40a0834..de590651749 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 readString(self): 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 writeString(self, string): 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 writeString(self, string): def writeBinary(self, binary): self.writeJSONBase64(binary) + def writeUuid(self, uuid): + self.writeJSONString(str(binary)) + class TSimpleJSONProtocolFactory(TProtocolFactory): diff --git a/lib/py/src/protocol/TProtocol.py b/lib/py/src/protocol/TProtocol.py index a0d145266d2..8a53d8c55ab 100644 --- a/lib/py/src/protocol/TProtocol.py +++ b/lib/py/src/protocol/TProtocol.py @@ -117,7 +117,10 @@ def writeDouble(self, dub): 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 readString(self): def readBinary(self): pass + def readUuid(self): + pass + def skip(self, ttype): if ttype == TType.BOOL: self.readBool() @@ -220,6 +226,8 @@ def skip(self, ttype): for i in range(size): self.skip(etype) self.readListEnd() + elif ttype == TType.UUID: + self.readUuid() else: raise TProtocolException( TProtocolException.INVALID_DATA, @@ -238,11 +246,12 @@ def skip(self, ttype): ('readI32', 'writeI32', False), # 8 TType.I32 (None, None, False), # 9 undefined ('readI64', 'writeI64', False), # 10 TType.I64 - ('readString', 'writeString', False), # 11 TType.STRING and UTF7 + ('readString', 'writeString', False), # 11 TType.STRING and UTF8 ('readContainerStruct', 'writeContainerStruct', True), # 12 *.STRUCT ('readContainerMap', 'writeContainerMap', True), # 13 TType.MAP ('readContainerSet', 'writeContainerSet', True), # 14 TType.SET ('readContainerList', 'writeContainerList', True), # 15 TType.LIST + ('readUuid', 'writeUuid', False), # 16 TType.UUID (None, None, False), # 16 TType.UTF8 # TODO: handle utf8 types? (None, None, False) # 17 TType.UTF16 # TODO: handle utf16 types? ) diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json index 6afe00c4cbe..aa3dcb1b4d6 100644 --- a/test/known_failures_Linux.json +++ b/test/known_failures_Linux.json @@ -27,42 +27,6 @@ "cl-rs_multi-binary_framed-ip", "cl-rs_multi_buffered-ip", "cl-rs_multi_framed-ip", - "cpp-cpp_binary_websocket-domain", - "cpp-cpp_binary_websocket-ip", - "cpp-cpp_binary_websocket-ip-ssl", - "cpp-cpp_compact_websocket-domain", - "cpp-cpp_compact_websocket-ip", - "cpp-cpp_compact_websocket-ip-ssl", - "cpp-cpp_header_websocket-domain", - "cpp-cpp_header_websocket-ip", - "cpp-cpp_header_websocket-ip-ssl", - "cpp-cpp_json_websocket-domain", - "cpp-cpp_json_websocket-ip", - "cpp-cpp_json_websocket-ip-ssl", - "cpp-cpp_multi-binary_websocket-domain", - "cpp-cpp_multi-binary_websocket-ip", - "cpp-cpp_multi-binary_websocket-ip-ssl", - "cpp-cpp_multi_websocket-domain", - "cpp-cpp_multi_websocket-ip", - "cpp-cpp_multi_websocket-ip-ssl", - "cpp-cpp_multic-compact_websocket-domain", - "cpp-cpp_multic-compact_websocket-ip", - "cpp-cpp_multic-compact_websocket-ip-ssl", - "cpp-cpp_multic_websocket-domain", - "cpp-cpp_multic_websocket-ip", - "cpp-cpp_multic_websocket-ip-ssl", - "cpp-cpp_multih-header_websocket-domain", - "cpp-cpp_multih-header_websocket-ip", - "cpp-cpp_multih-header_websocket-ip-ssl", - "cpp-cpp_multih_websocket-domain", - "cpp-cpp_multih_websocket-ip", - "cpp-cpp_multih_websocket-ip-ssl", - "cpp-cpp_multij-json_websocket-domain", - "cpp-cpp_multij-json_websocket-ip", - "cpp-cpp_multij-json_websocket-ip-ssl", - "cpp-cpp_multij_websocket-domain", - "cpp-cpp_multij_websocket-ip", - "cpp-cpp_multij_websocket-ip-ssl", "cpp-dart_binary_http-ip", "cpp-dart_compact_http-ip", "cpp-dart_json_http-ip", @@ -174,100 +138,57 @@ "cpp-py_binary-accel_http-domain", "cpp-py_binary-accel_http-ip", "cpp-py_binary-accel_http-ip-ssl", - "cpp-py_binary-accel_zlib-ip-ssl", "cpp-py_binary_http-domain", "cpp-py_binary_http-ip", "cpp-py_binary_http-ip-ssl", - "cpp-py_binary_zlib-ip-ssl", "cpp-py_compact-accelc_http-domain", "cpp-py_compact-accelc_http-ip", "cpp-py_compact-accelc_http-ip-ssl", - "cpp-py_compact-accelc_zlib-ip-ssl", "cpp-py_compact_http-domain", "cpp-py_compact_http-ip", "cpp-py_compact_http-ip-ssl", - "cpp-py_compact_zlib-ip-ssl", - "cpp-py_header_buffered-ip-ssl", - "cpp-py_header_framed-ip-ssl", "cpp-py_header_http-domain", "cpp-py_header_http-ip", "cpp-py_header_http-ip-ssl", - "cpp-py_header_zlib-ip-ssl", "cpp-py_json_http-domain", "cpp-py_json_http-ip", "cpp-py_json_http-ip-ssl", - "cpp-py_multi-accel_buffered-ip-ssl", - "cpp-py_multi-accel_framed-ip-ssl", "cpp-py_multi-accel_http-domain", "cpp-py_multi-accel_http-ip", "cpp-py_multi-accel_http-ip-ssl", - "cpp-py_multi-accel_zlib-ip-ssl", - "cpp-py_multi-binary_buffered-ip-ssl", - "cpp-py_multi-binary_framed-ip-ssl", "cpp-py_multi-binary_http-domain", "cpp-py_multi-binary_http-ip", "cpp-py_multi-binary_http-ip-ssl", - "cpp-py_multi-binary_zlib-ip-ssl", - "cpp-py_multi-multia_buffered-ip-ssl", - "cpp-py_multi-multia_framed-ip-ssl", "cpp-py_multi-multia_http-domain", "cpp-py_multi-multia_http-ip", "cpp-py_multi-multia_http-ip-ssl", - "cpp-py_multi-multia_zlib-ip-ssl", - "cpp-py_multi_buffered-ip-ssl", - "cpp-py_multi_framed-ip-ssl", "cpp-py_multi_http-domain", "cpp-py_multi_http-ip", "cpp-py_multi_http-ip-ssl", - "cpp-py_multi_zlib-ip-ssl", - "cpp-py_multic-accelc_buffered-ip-ssl", - "cpp-py_multic-accelc_framed-ip-ssl", "cpp-py_multic-accelc_http-domain", "cpp-py_multic-accelc_http-ip", "cpp-py_multic-accelc_http-ip-ssl", - "cpp-py_multic-accelc_zlib-ip-ssl", - "cpp-py_multic-compact_buffered-ip-ssl", - "cpp-py_multic-compact_framed-ip-ssl", "cpp-py_multic-compact_http-domain", "cpp-py_multic-compact_http-ip", "cpp-py_multic-compact_http-ip-ssl", - "cpp-py_multic-compact_zlib-ip-ssl", - "cpp-py_multic-multiac_buffered-ip-ssl", - "cpp-py_multic-multiac_framed-ip-ssl", "cpp-py_multic-multiac_http-domain", "cpp-py_multic-multiac_http-ip", "cpp-py_multic-multiac_http-ip-ssl", - "cpp-py_multic-multiac_zlib-ip-ssl", - "cpp-py_multic_buffered-ip-ssl", - "cpp-py_multic_framed-ip-ssl", "cpp-py_multic_http-domain", "cpp-py_multic_http-ip", "cpp-py_multic_http-ip-ssl", - "cpp-py_multic_zlib-ip-ssl", - "cpp-py_multih-header_buffered-ip-ssl", - "cpp-py_multih-header_framed-ip-ssl", "cpp-py_multih-header_http-domain", "cpp-py_multih-header_http-ip", "cpp-py_multih-header_http-ip-ssl", - "cpp-py_multih-header_zlib-ip-ssl", - "cpp-py_multih_buffered-ip-ssl", - "cpp-py_multih_framed-ip-ssl", "cpp-py_multih_http-domain", "cpp-py_multih_http-ip", "cpp-py_multih_http-ip-ssl", - "cpp-py_multih_zlib-ip-ssl", - "cpp-py_multij-json_buffered-ip-ssl", - "cpp-py_multij-json_framed-ip-ssl", "cpp-py_multij-json_http-domain", "cpp-py_multij-json_http-ip", "cpp-py_multij-json_http-ip-ssl", - "cpp-py_multij-json_zlib-ip-ssl", - "cpp-py_multij_buffered-ip-ssl", - "cpp-py_multij_framed-ip-ssl", "cpp-py_multij_http-domain", "cpp-py_multij_http-ip", "cpp-py_multij_http-ip-ssl", - "cpp-py_multij_zlib-ip-ssl", "d-cl_binary_buffered-ip", "d-cl_binary_framed-ip", "d-cpp_binary_buffered-ip", @@ -793,222 +714,78 @@ "perl-netstd_multi-binary_buffered-ip-ssl", "perl-netstd_multi-binary_framed-ip", "perl-netstd_multi-binary_framed-ip-ssl", - "py-cpp_accel-binary_buffered-domain", - "py-cpp_accel-binary_buffered-ip", - "py-cpp_accel-binary_buffered-ip-ssl", - "py-cpp_accel-binary_framed-domain", - "py-cpp_accel-binary_framed-ip", - "py-cpp_accel-binary_framed-ip-ssl", "py-cpp_accel-binary_http-domain", "py-cpp_accel-binary_http-ip", "py-cpp_accel-binary_http-ip-ssl", "py-cpp_accel-binary_zlib-domain", "py-cpp_accel-binary_zlib-ip", "py-cpp_accel-binary_zlib-ip-ssl", - "py-cpp_accelc-compact_buffered-domain", - "py-cpp_accelc-compact_buffered-ip", - "py-cpp_accelc-compact_buffered-ip-ssl", - "py-cpp_accelc-compact_framed-domain", - "py-cpp_accelc-compact_framed-ip", - "py-cpp_accelc-compact_framed-ip-ssl", "py-cpp_accelc-compact_http-domain", "py-cpp_accelc-compact_http-ip", "py-cpp_accelc-compact_http-ip-ssl", "py-cpp_accelc-compact_zlib-domain", "py-cpp_accelc-compact_zlib-ip", "py-cpp_accelc-compact_zlib-ip-ssl", - "py-cpp_binary_buffered-domain", - "py-cpp_binary_buffered-ip", - "py-cpp_binary_buffered-ip-ssl", - "py-cpp_binary_framed-domain", - "py-cpp_binary_framed-ip", - "py-cpp_binary_framed-ip-ssl", "py-cpp_binary_http-domain", "py-cpp_binary_http-ip", "py-cpp_binary_http-ip-ssl", - "py-cpp_binary_zlib-domain", - "py-cpp_binary_zlib-ip", - "py-cpp_binary_zlib-ip-ssl", - "py-cpp_compact_buffered-domain", - "py-cpp_compact_buffered-ip", - "py-cpp_compact_buffered-ip-ssl", - "py-cpp_compact_framed-domain", - "py-cpp_compact_framed-ip", - "py-cpp_compact_framed-ip-ssl", "py-cpp_compact_http-domain", "py-cpp_compact_http-ip", "py-cpp_compact_http-ip-ssl", - "py-cpp_compact_zlib-domain", - "py-cpp_compact_zlib-ip", - "py-cpp_compact_zlib-ip-ssl", - "py-cpp_header_buffered-domain", - "py-cpp_header_buffered-ip", - "py-cpp_header_buffered-ip-ssl", - "py-cpp_header_framed-domain", - "py-cpp_header_framed-ip", - "py-cpp_header_framed-ip-ssl", "py-cpp_header_http-domain", "py-cpp_header_http-ip", "py-cpp_header_http-ip-ssl", - "py-cpp_header_zlib-domain", - "py-cpp_header_zlib-ip", - "py-cpp_header_zlib-ip-ssl", - "py-cpp_json_buffered-domain", - "py-cpp_json_buffered-ip", - "py-cpp_json_buffered-ip-ssl", - "py-cpp_json_framed-domain", - "py-cpp_json_framed-ip", - "py-cpp_json_framed-ip-ssl", "py-cpp_json_http-domain", "py-cpp_json_http-ip", "py-cpp_json_http-ip-ssl", - "py-cpp_json_zlib-domain", - "py-cpp_json_zlib-ip", - "py-cpp_json_zlib-ip-ssl", - "py-cpp_multi-binary_buffered-domain", - "py-cpp_multi-binary_buffered-ip", - "py-cpp_multi-binary_buffered-ip-ssl", - "py-cpp_multi-binary_framed-domain", - "py-cpp_multi-binary_framed-ip", - "py-cpp_multi-binary_framed-ip-ssl", "py-cpp_multi-binary_http-domain", "py-cpp_multi-binary_http-ip", "py-cpp_multi-binary_http-ip-ssl", - "py-cpp_multi-binary_zlib-domain", - "py-cpp_multi-binary_zlib-ip", - "py-cpp_multi-binary_zlib-ip-ssl", - "py-cpp_multi_buffered-domain", - "py-cpp_multi_buffered-ip", - "py-cpp_multi_buffered-ip-ssl", - "py-cpp_multi_framed-domain", - "py-cpp_multi_framed-ip", - "py-cpp_multi_framed-ip-ssl", "py-cpp_multi_http-domain", "py-cpp_multi_http-ip", "py-cpp_multi_http-ip-ssl", - "py-cpp_multi_zlib-domain", - "py-cpp_multi_zlib-ip", - "py-cpp_multi_zlib-ip-ssl", - "py-cpp_multia-binary_buffered-domain", - "py-cpp_multia-binary_buffered-ip", - "py-cpp_multia-binary_buffered-ip-ssl", - "py-cpp_multia-binary_framed-domain", - "py-cpp_multia-binary_framed-ip", - "py-cpp_multia-binary_framed-ip-ssl", "py-cpp_multia-binary_http-domain", "py-cpp_multia-binary_http-ip", "py-cpp_multia-binary_http-ip-ssl", "py-cpp_multia-binary_zlib-domain", "py-cpp_multia-binary_zlib-ip", "py-cpp_multia-binary_zlib-ip-ssl", - "py-cpp_multia-multi_buffered-domain", - "py-cpp_multia-multi_buffered-ip", - "py-cpp_multia-multi_buffered-ip-ssl", - "py-cpp_multia-multi_framed-domain", - "py-cpp_multia-multi_framed-ip", - "py-cpp_multia-multi_framed-ip-ssl", "py-cpp_multia-multi_http-domain", "py-cpp_multia-multi_http-ip", "py-cpp_multia-multi_http-ip-ssl", "py-cpp_multia-multi_zlib-domain", "py-cpp_multia-multi_zlib-ip", "py-cpp_multia-multi_zlib-ip-ssl", - "py-cpp_multiac-compact_buffered-domain", - "py-cpp_multiac-compact_buffered-ip", - "py-cpp_multiac-compact_buffered-ip-ssl", - "py-cpp_multiac-compact_framed-domain", - "py-cpp_multiac-compact_framed-ip", - "py-cpp_multiac-compact_framed-ip-ssl", "py-cpp_multiac-compact_http-domain", "py-cpp_multiac-compact_http-ip", "py-cpp_multiac-compact_http-ip-ssl", "py-cpp_multiac-compact_zlib-domain", "py-cpp_multiac-compact_zlib-ip", "py-cpp_multiac-compact_zlib-ip-ssl", - "py-cpp_multiac-multic_buffered-domain", - "py-cpp_multiac-multic_buffered-ip", - "py-cpp_multiac-multic_buffered-ip-ssl", - "py-cpp_multiac-multic_framed-domain", - "py-cpp_multiac-multic_framed-ip", - "py-cpp_multiac-multic_framed-ip-ssl", "py-cpp_multiac-multic_http-domain", "py-cpp_multiac-multic_http-ip", "py-cpp_multiac-multic_http-ip-ssl", "py-cpp_multiac-multic_zlib-domain", "py-cpp_multiac-multic_zlib-ip", "py-cpp_multiac-multic_zlib-ip-ssl", - "py-cpp_multic-compact_buffered-domain", - "py-cpp_multic-compact_buffered-ip", - "py-cpp_multic-compact_buffered-ip-ssl", - "py-cpp_multic-compact_framed-domain", - "py-cpp_multic-compact_framed-ip", - "py-cpp_multic-compact_framed-ip-ssl", "py-cpp_multic-compact_http-domain", "py-cpp_multic-compact_http-ip", "py-cpp_multic-compact_http-ip-ssl", - "py-cpp_multic-compact_zlib-domain", - "py-cpp_multic-compact_zlib-ip", - "py-cpp_multic-compact_zlib-ip-ssl", - "py-cpp_multic_buffered-domain", - "py-cpp_multic_buffered-ip", - "py-cpp_multic_buffered-ip-ssl", - "py-cpp_multic_framed-domain", - "py-cpp_multic_framed-ip", - "py-cpp_multic_framed-ip-ssl", "py-cpp_multic_http-domain", "py-cpp_multic_http-ip", "py-cpp_multic_http-ip-ssl", - "py-cpp_multic_zlib-domain", - "py-cpp_multic_zlib-ip", - "py-cpp_multic_zlib-ip-ssl", - "py-cpp_multih-header_buffered-domain", - "py-cpp_multih-header_buffered-ip", - "py-cpp_multih-header_buffered-ip-ssl", - "py-cpp_multih-header_framed-domain", - "py-cpp_multih-header_framed-ip", - "py-cpp_multih-header_framed-ip-ssl", "py-cpp_multih-header_http-domain", "py-cpp_multih-header_http-ip", "py-cpp_multih-header_http-ip-ssl", - "py-cpp_multih-header_zlib-domain", - "py-cpp_multih-header_zlib-ip", - "py-cpp_multih-header_zlib-ip-ssl", - "py-cpp_multih_buffered-domain", - "py-cpp_multih_buffered-ip", - "py-cpp_multih_buffered-ip-ssl", - "py-cpp_multih_framed-domain", - "py-cpp_multih_framed-ip", - "py-cpp_multih_framed-ip-ssl", "py-cpp_multih_http-domain", "py-cpp_multih_http-ip", "py-cpp_multih_http-ip-ssl", - "py-cpp_multih_zlib-domain", - "py-cpp_multih_zlib-ip", - "py-cpp_multih_zlib-ip-ssl", - "py-cpp_multij-json_buffered-domain", - "py-cpp_multij-json_buffered-ip", - "py-cpp_multij-json_buffered-ip-ssl", - "py-cpp_multij-json_framed-domain", - "py-cpp_multij-json_framed-ip", - "py-cpp_multij-json_framed-ip-ssl", "py-cpp_multij-json_http-domain", "py-cpp_multij-json_http-ip", "py-cpp_multij-json_http-ip-ssl", - "py-cpp_multij-json_zlib-domain", - "py-cpp_multij-json_zlib-ip", - "py-cpp_multij-json_zlib-ip-ssl", - "py-cpp_multij_buffered-domain", - "py-cpp_multij_buffered-ip", - "py-cpp_multij_buffered-ip-ssl", - "py-cpp_multij_framed-domain", - "py-cpp_multij_framed-ip", - "py-cpp_multij_framed-ip-ssl", "py-cpp_multij_http-domain", "py-cpp_multij_http-ip", "py-cpp_multij_http-ip-ssl", - "py-cpp_multij_zlib-domain", - "py-cpp_multij_zlib-ip", - "py-cpp_multij_zlib-ip-ssl", "py-d_accel-binary_http-ip", "py-d_accel-binary_http-ip-ssl", "py-d_accelc-compact_http-ip", @@ -1301,12 +1078,12 @@ "py-rb_compact_framed-domain", "py-rb_compact_framed-ip", "py-rb_compact_framed-ip-ssl", - "py-rb_header_buffered-ip-ssl", - "py-rb_header_framed-ip-ssl", - "py-rb_header_framed-ip", "py-rb_header_buffered-domain", "py-rb_header_buffered-ip", + "py-rb_header_buffered-ip-ssl", "py-rb_header_framed-domain", + "py-rb_header_framed-ip", + "py-rb_header_framed-ip-ssl", "py-rb_json_buffered-domain", "py-rb_json_buffered-ip", "py-rb_json_buffered-ip-ssl", @@ -1383,8 +1160,8 @@ "rb-netstd_json_buffered-ip-ssl", "rb-netstd_json_framed-ip", "rb-netstd_json_framed-ip-ssl", - "rb-py_header_framed-ip-ssl", "rb-py_header_buffered-ip-ssl", + "rb-py_header_framed-ip-ssl", "rs-netstd_binary_buffered-ip", "rs-netstd_binary_framed-ip", "rs-netstd_compact_buffered-ip", @@ -1393,4 +1170,4 @@ "rs-netstd_multi-binary_framed-ip", "rs-netstd_multic-compact_buffered-ip", "rs-netstd_multic-compact_framed-ip" -] +] \ No newline at end of file diff --git a/test/py/Makefile.am b/test/py/Makefile.am index df3a647fb08..c471b92e2bc 100644 --- a/test/py/Makefile.am +++ b/test/py/Makefile.am @@ -125,8 +125,8 @@ gen-py-enum/%/__init__.py: ../%.thrift $(THRIFT) gen-py-type_hints/%/__init__.py: ../%.thrift $(THRIFT) test -d gen-py-type_hints || $(MKDIR_P) gen-py-type_hints - test ../v0.16/$(notdir $<) \ - && $(THRIFT) --gen py:type_hints,enum -out gen-py-type_hints ../v0.16/$(notdir $<) \ + test ../$(notdir $<) \ + && $(THRIFT) --gen py:type_hints,enum -out gen-py-type_hints ../$(notdir $<) \ || $(THRIFT) --gen py:type_hints,enum -out gen-py-type_hints $< clean-local: diff --git a/test/py/TestTypes.py b/test/py/TestTypes.py index bb1bc35e874..66bee69d6df 100644 --- a/test/py/TestTypes.py +++ b/test/py/TestTypes.py @@ -20,6 +20,7 @@ from ThriftTest import ThriftTest from ThriftTest.ThriftTest import Client from ThriftTest.ttypes import Xtruct +from uuid import UUID import unittest @@ -66,3 +67,6 @@ def test_list(self): def test_set(self): self.assertEqual(Client.testSet.__annotations__, {'return': set[int], 'thing': set[int]}) + + def test_uuid(self): + self.assertEqual(Client.testUuid.__annotations__, {'return': uuid, 'thing': uuid}) diff --git a/test/tests.json b/test/tests.json index bcc87d3217b..e8ea3c943f6 100644 --- a/test/tests.json +++ b/test/tests.json @@ -393,7 +393,7 @@ ] }, "client": { - "timeout": 8, + "timeout": 10, "command": [ "TestClient" ], From 122bce4fafe3886c1c606385eb0c82682cd2cf1a Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Tue, 3 Mar 2026 11:43:35 +0000 Subject: [PATCH 06/13] Sneak in skip UUID for C++ --- lib/cpp/src/thrift/protocol/TProtocol.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/cpp/src/thrift/protocol/TProtocol.h b/lib/cpp/src/thrift/protocol/TProtocol.h index 63b959163d4..565a8ecba85 100644 --- a/lib/cpp/src/thrift/protocol/TProtocol.h +++ b/lib/cpp/src/thrift/protocol/TProtocol.h @@ -784,6 +784,10 @@ uint32_t skip(Protocol_& prot, TType type) { result += prot.readListEnd(); return result; } + case T_UUID: { + TUuid uuid; + return prot.readUUID(uuid); + } default: break; } From 625107686ba2ed6e080ff02f91cd7964da47c8f1 Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Tue, 3 Mar 2026 12:16:54 +0000 Subject: [PATCH 07/13] Clean up and optimise the UUID reading and writing - RAII wrappers to prevent leaks - Optimisze with INTERN_STRING --- lib/py/src/ext/module.cpp | 4 ++++ lib/py/src/ext/protocol.tcc | 42 ++++++++++++++++--------------------- lib/py/src/ext/types.cpp | 1 + lib/py/src/ext/types.h | 3 +++ 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/lib/py/src/ext/module.cpp b/lib/py/src/ext/module.cpp index a1b0e5633e6..e2b540e6f6a 100644 --- a/lib/py/src/ext/module.cpp +++ b/lib/py/src/ext/module.cpp @@ -38,6 +38,8 @@ PyObject* INTERN_STRING(TFrozenDict); PyObject* INTERN_STRING(cstringio_buf); PyObject* INTERN_STRING(cstringio_refill); +PyObject* INTERN_STRING(UUID); +PyObject* INTERN_STRING(bytes); static PyObject* INTERN_STRING(string_length_limit); static PyObject* INTERN_STRING(container_length_limit); static PyObject* INTERN_STRING(trans); @@ -186,6 +188,8 @@ void initfastbinary() { INIT_INTERN_STRING(string_length_limit); INIT_INTERN_STRING(container_length_limit); INIT_INTERN_STRING(trans); + INIT_INTERN_STRING(UUID); + INIT_INTERN_STRING(bytes); #undef INIT_INTERN_STRING PyObject* module = diff --git a/lib/py/src/ext/protocol.tcc b/lib/py/src/ext/protocol.tcc index e92580eacdb..b517c381eeb 100644 --- a/lib/py/src/ext/protocol.tcc +++ b/lib/py/src/ext/protocol.tcc @@ -543,25 +543,20 @@ bool ProtocolBase::encodeValue(PyObject* value, TType type, PyObject* type } case T_UUID: { - ScopedPyObject instval(PyObject_GetAttrString(value, "bytes")); + ScopedPyObject instval(PyObject_GetAttr(value, INTERN_STRING(bytes))); if (!instval) { return false; } Py_ssize_t size; char* buffer; - if (PyBytes_AsStringAndSize(instval.get(), &buffer, &size) < 0) { - // Python exception is already set (e.g. TypeError, MemoryError) - // Thrift accelerator functions usually just return NULL here - return false; // or whatever your writeXXX method returns on error + return false; } - // Optional but strongly recommended for safety: if (size != 16) { PyErr_SetString(PyExc_TypeError, "uuid.bytes must be exactly 16 bytes long"); return false; } - impl()->writeUuid(buffer); return true; } @@ -845,27 +840,26 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { return nullptr; } - PyObject* uuid_mod = PyImport_ImportModule("uuid"); - if (!uuid_mod) return NULL; - - PyObject* UUID_type = PyObject_GetAttrString(uuid_mod, "UUID"); - Py_DECREF(uuid_mod); - if (!UUID_type) return NULL; - - PyObject* py_bytes = PyBytes_FromStringAndSize(buf, 16); - if (!py_bytes) { - Py_DECREF(UUID_type); - return NULL; + if(!UuidModule) { + UuidModule = PyImport_ImportModule("uuid"); + if (!UuidModule) + return nullptr; } - PyObject* kwargs = Py_BuildValue("{s:O}", "bytes", py_bytes); - Py_DECREF(py_bytes); + ScopedPyObject cls(PyObject_GetAttr(UuidModule, INTERN_STRING(UUID))); + if (!cls) { + return nullptr; + } - PyObject* uuid_obj = PyObject_Call(UUID_type, PyTuple_New(0), kwargs); - Py_DECREF(kwargs); - Py_DECREF(UUID_type); + ScopedPyObject pyBytes(PyBytes_FromStringAndSize(buf, 16)); + if (!pyBytes) { + return nullptr; + } - return uuid_obj; + ScopedPyObject args(PyTuple_New(0)); + ScopedPyObject kwargs(Py_BuildValue("{O:O}", INTERN_STRING(bytes), pyBytes.get())); + ScopedPyObject ret(PyObject_Call(cls.get(), args.get(), kwargs.get())); + return ret.release(); } case T_STOP: diff --git a/lib/py/src/ext/types.cpp b/lib/py/src/ext/types.cpp index 0c20e56224e..a7ccd0a8c67 100644 --- a/lib/py/src/ext/types.cpp +++ b/lib/py/src/ext/types.cpp @@ -26,6 +26,7 @@ namespace thrift { namespace py { PyObject* ThriftModule = nullptr; +PyObject* UuidModule = nullptr; #if PY_MAJOR_VERSION < 3 char refill_signature[] = {'s', '#', 'i'}; diff --git a/lib/py/src/ext/types.h b/lib/py/src/ext/types.h index ba75a29a683..929a77d5fee 100644 --- a/lib/py/src/ext/types.h +++ b/lib/py/src/ext/types.h @@ -48,6 +48,8 @@ extern "C" { extern PyObject* INTERN_STRING(TFrozenDict); extern PyObject* INTERN_STRING(cstringio_buf); extern PyObject* INTERN_STRING(cstringio_refill); +extern PyObject* INTERN_STRING(UUID); +extern PyObject* INTERN_STRING(bytes); } namespace apache { @@ -55,6 +57,7 @@ namespace thrift { namespace py { extern PyObject* ThriftModule; +extern PyObject* UuidModule; // Stolen out of TProtocol.h. // It would be a huge pain to have both get this from one place. From 1b65f4c280e2a40600ee053db3cd08fbefc1694b Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Tue, 3 Mar 2026 12:36:07 +0000 Subject: [PATCH 08/13] Fix options in test --- test/py/TestServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/py/TestServer.py b/test/py/TestServer.py index 4327daa8713..59a06ad2ada 100755 --- a/test/py/TestServer.py +++ b/test/py/TestServer.py @@ -83,7 +83,7 @@ def testBinary(self, thing): return thing def testUuid(self, thing): - if options.verbose > 1: + if self.options.verbose > 1: logging.info('testUuid(%s)' % thing) return thing From bc9a51f8d45090869a5a413d1c030c9672a0f2fd Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Tue, 3 Mar 2026 14:21:12 +0000 Subject: [PATCH 09/13] Combine go,cpp,py tests - Clean out known failures between go and py - Appears there is some ssl issue for some of the tests --- .github/workflows/build.yml | 2 +- test/known_failures_Linux.json | 30 ------------------------------ test/tests.json | 8 ++++---- 3 files changed, 5 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2a7dde1285..72213e9a52d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -836,7 +836,7 @@ jobs: # kotlin cross test are failing -> see THRIFT-5879 server_lang: ['java', 'go', 'cpp', 'py', 'rb'] # we always use comma join as many client langs as possible, to reduce the number of jobs - client_lang: ['java,kotlin', 'go,cpp', 'py', 'rb'] + client_lang: ['java,kotlin', 'go,cpp,py', 'rb'] fail-fast: false steps: - uses: actions/checkout@v6 diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json index aa3dcb1b4d6..41b29c97681 100644 --- a/test/known_failures_Linux.json +++ b/test/known_failures_Linux.json @@ -801,53 +801,23 @@ "py-dart_binary_http-ip", "py-dart_compact_http-ip", "py-dart_json_http-ip", - "py-go_accel-binary_buffered-ip", "py-go_accel-binary_buffered-ip-ssl", - "py-go_accel-binary_framed-ip", "py-go_accel-binary_framed-ip-ssl", - "py-go_accel-binary_http-ip", - "py-go_accel-binary_http-ip-ssl", - "py-go_accel-binary_zlib-ip", "py-go_accel-binary_zlib-ip-ssl", - "py-go_accelc-compact_buffered-ip", "py-go_accelc-compact_buffered-ip-ssl", - "py-go_accelc-compact_framed-ip", "py-go_accelc-compact_framed-ip-ssl", - "py-go_accelc-compact_http-ip", - "py-go_accelc-compact_http-ip-ssl", - "py-go_accelc-compact_zlib-ip", "py-go_accelc-compact_zlib-ip-ssl", - "py-go_binary_buffered-ip", "py-go_binary_buffered-ip-ssl", - "py-go_binary_framed-ip", "py-go_binary_framed-ip-ssl", - "py-go_binary_http-ip", - "py-go_binary_http-ip-ssl", - "py-go_binary_zlib-ip", "py-go_binary_zlib-ip-ssl", - "py-go_compact_buffered-ip", "py-go_compact_buffered-ip-ssl", - "py-go_compact_framed-ip", "py-go_compact_framed-ip-ssl", - "py-go_compact_http-ip", - "py-go_compact_http-ip-ssl", - "py-go_compact_zlib-ip", "py-go_compact_zlib-ip-ssl", - "py-go_header_buffered-ip", "py-go_header_buffered-ip-ssl", - "py-go_header_framed-ip", "py-go_header_framed-ip-ssl", - "py-go_header_http-ip", - "py-go_header_http-ip-ssl", - "py-go_header_zlib-ip", "py-go_header_zlib-ip-ssl", - "py-go_json_buffered-ip", "py-go_json_buffered-ip-ssl", - "py-go_json_framed-ip", "py-go_json_framed-ip-ssl", - "py-go_json_http-ip", - "py-go_json_http-ip-ssl", - "py-go_json_zlib-ip", "py-go_json_zlib-ip-ssl", "py-hs_accel-binary_http-ip", "py-hs_accelc-compact_http-ip", diff --git a/test/tests.json b/test/tests.json index e8ea3c943f6..0f7dbe75e70 100644 --- a/test/tests.json +++ b/test/tests.json @@ -99,7 +99,7 @@ ] }, "client": { - "timeout": 6, + "timeout": 15, "command": [ "testclient" ] @@ -292,7 +292,7 @@ ] }, "client": { - "timeout": 10, + "timeout": 15, "command": [ "TestClient.py", "--verbose", @@ -342,7 +342,7 @@ ] }, "client": { - "timeout": 10, + "timeout": 15, "command": [ "python3", "TestClient.py", @@ -393,7 +393,7 @@ ] }, "client": { - "timeout": 10, + "timeout": 15, "command": [ "TestClient" ], From 30646eee63dd91ece7e720e502fe367b10a72269 Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Tue, 3 Mar 2026 14:30:29 +0000 Subject: [PATCH 10/13] Mark UUID as supported in the LANGAUGES page --- LANGUAGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LANGUAGES.md b/LANGUAGES.md index 8bf9a333d51..a92d53043b5 100644 --- a/LANGUAGES.md +++ b/LANGUAGES.md @@ -301,7 +301,7 @@ Thrift's core protocol is TBinary, supported by all languages except for JavaScr 0.2.0 YesYes 2.7.12, 3.5.22.7.15, 3.6.8 - +Yes YesYesYes YesYesYes YesYesYesYes From 6a3d889875890c9a4a6b1681396723777450b834 Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Mon, 9 Mar 2026 06:16:20 +0000 Subject: [PATCH 11/13] Remove UTF8 and UTF16 as discussed in PR - It is not in the spec and string can be used --- lib/py/src/Thrift.py | 4 ---- lib/py/src/ext/types.h | 2 -- lib/py/src/protocol/TProtocol.py | 4 +--- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/py/src/Thrift.py b/lib/py/src/Thrift.py index 9c051d6d34d..7b28c908c7e 100644 --- a/lib/py/src/Thrift.py +++ b/lib/py/src/Thrift.py @@ -34,8 +34,6 @@ class TType(object): MAP = 13 SET = 14 LIST = 15 - UTF8 = 11 - UTF16 = 11 UUID = 16 _VALUES_TO_NAMES = ( @@ -55,8 +53,6 @@ class TType(object): 'MAP', 'SET', 'LIST', - 'UTF8', - 'UTF16', 'UUID', ) diff --git a/lib/py/src/ext/types.h b/lib/py/src/ext/types.h index 929a77d5fee..2848b28f0ba 100644 --- a/lib/py/src/ext/types.h +++ b/lib/py/src/ext/types.h @@ -79,8 +79,6 @@ enum TType { T_MAP = 13, T_SET = 14, T_LIST = 15, - T_UTF8 = 11, - T_UTF16 = 11, T_UUID = 16, }; diff --git a/lib/py/src/protocol/TProtocol.py b/lib/py/src/protocol/TProtocol.py index 8a53d8c55ab..975cbf5915e 100644 --- a/lib/py/src/protocol/TProtocol.py +++ b/lib/py/src/protocol/TProtocol.py @@ -246,14 +246,12 @@ def skip(self, ttype): ('readI32', 'writeI32', False), # 8 TType.I32 (None, None, False), # 9 undefined ('readI64', 'writeI64', False), # 10 TType.I64 - ('readString', 'writeString', False), # 11 TType.STRING and UTF8 + ('readString', 'writeString', False), # 11 TType.STRING and UTF7 ('readContainerStruct', 'writeContainerStruct', True), # 12 *.STRUCT ('readContainerMap', 'writeContainerMap', True), # 13 TType.MAP ('readContainerSet', 'writeContainerSet', True), # 14 TType.SET ('readContainerList', 'writeContainerList', True), # 15 TType.LIST ('readUuid', 'writeUuid', False), # 16 TType.UUID - (None, None, False), # 16 TType.UTF8 # TODO: handle utf8 types? - (None, None, False) # 17 TType.UTF16 # TODO: handle utf16 types? ) def _ttype_handlers(self, ttype, spec): From 4bafaee1b78db481c9f0af71040ec6bd007d4273 Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Mon, 9 Mar 2026 06:39:51 +0000 Subject: [PATCH 12/13] Flake8 findings - Probably found a buf in the JSON protocol --- lib/py/src/protocol/TBinaryProtocol.py | 1 + lib/py/src/protocol/TCompactProtocol.py | 1 + lib/py/src/protocol/TJSONProtocol.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/py/src/protocol/TBinaryProtocol.py b/lib/py/src/protocol/TBinaryProtocol.py index 788a6640236..b73e3c93944 100644 --- a/lib/py/src/protocol/TBinaryProtocol.py +++ b/lib/py/src/protocol/TBinaryProtocol.py @@ -244,6 +244,7 @@ def readUuid(self): val = uuid.UUID(bytes=buff) return val + class TBinaryProtocolFactory(TProtocolFactory): def __init__(self, strictRead=False, strictWrite=True, **kwargs): self.strictRead = strictRead diff --git a/lib/py/src/protocol/TCompactProtocol.py b/lib/py/src/protocol/TCompactProtocol.py index 13e1904018e..b58095a0007 100644 --- a/lib/py/src/protocol/TCompactProtocol.py +++ b/lib/py/src/protocol/TCompactProtocol.py @@ -80,6 +80,7 @@ def readVarint(trans): return result shift += 7 + # As per TCompactProtocol.tcc class CompactType(object): STOP = 0x00 diff --git a/lib/py/src/protocol/TJSONProtocol.py b/lib/py/src/protocol/TJSONProtocol.py index de590651749..6782eeb26fb 100644 --- a/lib/py/src/protocol/TJSONProtocol.py +++ b/lib/py/src/protocol/TJSONProtocol.py @@ -671,7 +671,7 @@ def writeBinary(self, binary): self.writeJSONBase64(binary) def writeUuid(self, uuid): - self.writeJSONString(str(binary)) + self.writeJSONString(str(uuid)) class TSimpleJSONProtocolFactory(TProtocolFactory): From aa4ba8b7fc1649c5a7698bb5fe73e1d0cacd8403 Mon Sep 17 00:00:00 2001 From: CJCombrink Date: Mon, 9 Mar 2026 06:44:56 +0000 Subject: [PATCH 13/13] More flake8 fixes --- lib/py/setup.py | 2 +- test/py/TestTypes.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/py/setup.py b/lib/py/setup.py index 3cf3f74e35e..456fd6d3425 100644 --- a/lib/py/setup.py +++ b/lib/py/setup.py @@ -161,7 +161,7 @@ def run_setup(with_binary): run_setup(False) sys.exit(0) -except : +except BuildFailed: print() print('*' * 80) print("An error occurred while trying to compile without the C extension enabled") diff --git a/test/py/TestTypes.py b/test/py/TestTypes.py index 66bee69d6df..0df87aa6fd1 100644 --- a/test/py/TestTypes.py +++ b/test/py/TestTypes.py @@ -69,4 +69,4 @@ def test_set(self): self.assertEqual(Client.testSet.__annotations__, {'return': set[int], 'thing': set[int]}) def test_uuid(self): - self.assertEqual(Client.testUuid.__annotations__, {'return': uuid, 'thing': uuid}) + self.assertEqual(Client.testUuid.__annotations__, {'return': UUID, 'thing': UUID})