THRIFT-2642 Recursive structs don't work in python
Client: Python
Patch: Eric Conner <eric@pinterest.com>
This closes #1293
diff --git a/lib/py/src/TRecursive.py b/lib/py/src/TRecursive.py
new file mode 100644
index 0000000..d5a5686
--- /dev/null
+++ b/lib/py/src/TRecursive.py
@@ -0,0 +1,83 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+from thrift.Thrift import TType
+
+TYPE_IDX = 1
+SPEC_ARGS_IDX = 3
+SPEC_ARGS_CLASS_REF_IDX = 0
+SPEC_ARGS_THRIFT_SPEC_IDX = 1
+
+
+def fix_spec(all_structs):
+ """Wire up recursive references for all TStruct definitions inside of each thrift_spec."""
+ for struc in all_structs:
+ spec = struc.thrift_spec
+ for thrift_spec in spec:
+ if thrift_spec is None:
+ continue
+ elif thrift_spec[TYPE_IDX] == TType.STRUCT:
+ other = thrift_spec[SPEC_ARGS_IDX][SPEC_ARGS_CLASS_REF_IDX].thrift_spec
+ thrift_spec[SPEC_ARGS_IDX][SPEC_ARGS_THRIFT_SPEC_IDX] = other
+ elif thrift_spec[TYPE_IDX] in (TType.LIST, TType.SET):
+ _fix_list_or_set(thrift_spec[SPEC_ARGS_IDX])
+ elif thrift_spec[TYPE_IDX] == TType.MAP:
+ _fix_map(thrift_spec[SPEC_ARGS_IDX])
+
+
+def _fix_list_or_set(element_type):
+ # For a list or set, the thrift_spec entry looks like,
+ # (1, TType.LIST, 'lister', (TType.STRUCT, [RecList, None], False), None, ), # 1
+ # so ``element_type`` will be,
+ # (TType.STRUCT, [RecList, None], False)
+ if element_type[0] == TType.STRUCT:
+ element_type[1][1] = element_type[1][0].thrift_spec
+ elif element_type[0] in (TType.LIST, TType.SET):
+ _fix_list_or_set(element_type[1])
+ elif element_type[0] == TType.MAP:
+ _fix_map(element_type[1])
+
+
+def _fix_map(element_type):
+ # For a map of key -> value type, ``element_type`` will be,
+ # (TType.I16, None, TType.STRUCT, [RecMapBasic, None], False), None, )
+ # which is just a normal struct definition.
+ #
+ # For a map of key -> list / set, ``element_type`` will be,
+ # (TType.I16, None, TType.LIST, (TType.STRUCT, [RecMapList, None], False), False)
+ # and we need to process the 3rd element as a list.
+ #
+ # For a map of key -> map, ``element_type`` will be,
+ # (TType.I16, None, TType.MAP, (TType.I16, None, TType.STRUCT,
+ # [RecMapMap, None], False), False)
+ # and need to process 3rd element as a map.
+
+ # Is the map key a struct?
+ if element_type[0] == TType.STRUCT:
+ element_type[1][1] = element_type[1][0].thrift_spec
+ elif element_type[0] in (TType.LIST, TType.SET):
+ _fix_list_or_set(element_type[1])
+ elif element_type[0] == TType.MAP:
+ _fix_map(element_type[1])
+
+ # Is the map value a struct?
+ if element_type[2] == TType.STRUCT:
+ element_type[3][1] = element_type[3][0].thrift_spec
+ elif element_type[2] in (TType.LIST, TType.SET):
+ _fix_list_or_set(element_type[3])
+ elif element_type[2] == TType.MAP:
+ _fix_map(element_type[3])
diff --git a/lib/py/src/ext/types.cpp b/lib/py/src/ext/types.cpp
index 849ab2f..68443fb 100644
--- a/lib/py/src/ext/types.cpp
+++ b/lib/py/src/ext/types.cpp
@@ -98,13 +98,13 @@
}
bool parse_struct_args(StructTypeArgs* dest, PyObject* typeargs) {
- if (PyTuple_Size(typeargs) != 2) {
- PyErr_SetString(PyExc_TypeError, "expecting tuple of size 2 for struct args");
+ if (PyList_Size(typeargs) != 2) {
+ PyErr_SetString(PyExc_TypeError, "expecting list of size 2 for struct args");
return false;
}
- dest->klass = PyTuple_GET_ITEM(typeargs, 0);
- dest->spec = PyTuple_GET_ITEM(typeargs, 1);
+ dest->klass = PyList_GET_ITEM(typeargs, 0);
+ dest->spec = PyList_GET_ITEM(typeargs, 1);
return true;
}
diff --git a/lib/py/src/protocol/TBase.py b/lib/py/src/protocol/TBase.py
index 55da19e..9ae1b11 100644
--- a/lib/py/src/protocol/TBase.py
+++ b/lib/py/src/protocol/TBase.py
@@ -44,14 +44,14 @@
if (iprot._fast_decode is not None and
isinstance(iprot.trans, TTransport.CReadableTransport) and
self.thrift_spec is not None):
- iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
+ iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
else:
iprot.readStruct(self, self.thrift_spec)
def write(self, oprot):
if (oprot._fast_encode is not None and self.thrift_spec is not None):
oprot.trans.write(
- oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
+ oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
else:
oprot.writeStruct(self, self.thrift_spec)
@@ -77,6 +77,6 @@
cls.thrift_spec is not None):
self = cls()
return iprot._fast_decode(None, iprot,
- (self.__class__, self.thrift_spec))
+ [self.__class__, self.thrift_spec])
else:
return iprot.readStruct(cls, cls.thrift_spec, True)