in thrift/lib/py/protocol/fastproto.cpp [626:781]
static bool decode_struct(
Reader* reader, PyObject* value, StructTypeArgs* args, int utf8strings) {
int speclen = PyTuple_Size(args->spec);
if (speclen == -1) {
return false;
}
std::string sname;
reader->readStructBegin(sname);
std::string fname;
TType ftype;
int16_t fid;
PyObject* itemspec;
StructItemSpec parsedspec;
int first_tag = 0;
bool first_tag_read = false;
PyObject* first_item_spec;
StructItemSpec first_parsed_spec;
while (true) {
PyObject* fieldval = nullptr;
reader->readFieldBegin(fname, ftype, fid);
if (ftype == TType::T_STOP) {
break;
}
if (!first_tag_read) {
first_tag_read = true;
if (speclen == 0) { // Empty struct and all fields will be skipped
first_item_spec = Py_None;
} else {
first_item_spec = PyTuple_GET_ITEM(args->spec, 0);
if (first_item_spec != Py_None) {
if (!parse_struct_item_spec(&first_parsed_spec, first_item_spec)) {
return false;
}
first_tag = first_parsed_spec.tag;
}
}
}
fid -= first_tag;
if (fid > 0 && fid < speclen) {
itemspec = PyTuple_GET_ITEM(args->spec, fid);
} else if (fid == 0) {
itemspec = first_item_spec;
} else {
itemspec = Py_None;
}
if (itemspec == Py_None) {
reader->skip(ftype);
continue;
}
if (fid == 0) {
parsedspec = first_parsed_spec;
} else if (!parse_struct_item_spec(&parsedspec, itemspec)) {
return false;
}
if (parsedspec.type != ftype) {
reader->skip(ftype);
continue;
}
fieldval = decode_val(
reader, parsedspec.type, parsedspec.typeargs, utf8strings, args);
if (!fieldval) {
PyObject *pType, *pValue, *pTraceback;
PyErr_Fetch(&pType, &pValue, &pTraceback);
if (!PyErr_GivenExceptionMatches(pType, PyExc_UnicodeDecodeError)) {
PyErr_Restore(pType, pValue, pTraceback);
return false;
}
#if PY_MAJOR_VERSION >= 3
const char* fieldName = PyUnicode_AsUTF8(parsedspec.attrname);
#else
const char* fieldName = PyString_AsString(parsedspec.attrname);
#endif
PyObject* create_func_method_name = Py_BuildValue(
"s", "create_ThriftUnicodeDecodeError_from_UnicodeDecodeError");
PyObject* pFieldName = Py_BuildValue("s", fieldName);
PyObject* decode_error = PyObject_CallMethodObjArgs(
ExceptionsModule, create_func_method_name, pValue, pFieldName, NULL);
PyObject* thrift_decode_error_type = PyObject_Type(decode_error);
PyErr_SetObject(thrift_decode_error_type, decode_error);
Py_DECREF(pType);
Py_DECREF(pValue);
if (pTraceback != nullptr) {
Py_DECREF(pTraceback);
}
return false;
}
if (args->isunion) {
PyObject* fieldobj = PyObject_GetAttrString(value, "field");
if (!fieldobj) {
Py_DECREF(fieldval);
return false;
}
int field = (int)AS_LONG(fieldobj);
if (field == -1 && PyErr_Occurred()) {
Py_DECREF(fieldval);
Py_DECREF(fieldobj);
return false;
}
if (field != 0) {
PyErr_SetString(PyExc_AssertionError, "field already set in union");
Py_DECREF(fieldval);
Py_DECREF(fieldobj);
return false;
}
Py_DECREF(fieldobj);
PyObject* valueobj = PyObject_GetAttrString(value, "value");
if (!valueobj) {
Py_DECREF(fieldobj);
return false;
}
if (valueobj != Py_None) {
PyErr_SetString(PyExc_AssertionError, "value already set in union");
Py_DECREF(fieldval);
Py_DECREF(valueobj);
return false;
}
Py_DECREF(valueobj);
PyObject* tagobj = FROM_LONG(parsedspec.tag);
if (!tagobj) {
return false;
}
if (PyObject_SetAttrString(value, "value", fieldval) == -1 ||
PyObject_SetAttrString(value, "field", tagobj) == -1) {
Py_DECREF(fieldval);
Py_DECREF(tagobj);
return false;
}
Py_DECREF(tagobj);
} else {
if (PyObject_SetAttr(value, parsedspec.attrname, fieldval) == -1) {
Py_DECREF(fieldval);
return false;
}
}
Py_DECREF(fieldval);
reader->readFieldEnd();
}
reader->readStructEnd();
return true;
}