in thrift/compiler/generate/t_py_generator.cc [1641:1847]
void t_py_generator::generate_py_struct_definition(
ofstream& out,
const t_struct* tstruct,
bool is_exception,
bool /*is_result*/) {
const vector<t_field*>& members = tstruct->get_members();
const vector<t_field*>& sorted_members = tstruct->get_sorted_members();
vector<t_field*>::const_iterator m_iter;
out << "class " << rename_reserved_keywords(tstruct->get_name());
if (is_exception) {
out << "(TException)";
} else if (gen_newstyle_) {
out << "(object)";
}
out << ":" << endl;
indent_up();
generate_python_docstring(out, tstruct);
out << endl;
/*
Here we generate the structure specification for the fastproto codec.
These specifications have the following structure:
thrift_spec -> tuple of item_spec
item_spec -> None | (tag, type_enum, name, spec_args, default)
tag -> integer
type_enum -> TType.I32 | TType.STRING | TType.STRUCT | ...
name -> string_literal
default -> None # Handled by __init__
spec_args -> None # For simple types
| True/False for Text/Binary Strings
| (type_enum, spec_args) # Value type for list/set
| (type_enum, spec_args, type_enum, spec_args)
# Key and value for map
| [class_name, spec_args_ptr, is_union] # For struct/exception
| class_name for Enums
class_name -> identifier # Basically a pointer to the class
spec_args_ptr -> expression # just class_name.spec_args
*/
if (gen_slots_) {
indent(out) << "__slots__ = [ " << endl;
indent_up();
for (m_iter = sorted_members.begin(); m_iter != sorted_members.end();
++m_iter) {
indent(out) << "'" << rename_reserved_keywords((*m_iter)->get_name())
<< "'," << endl;
}
indent_down();
indent(out) << " ]" << endl << endl;
}
// TODO(dreiss): Test encoding of structs where some inner structs
// don't have thrift_spec.
indent(out) << "thrift_spec = None" << endl;
indent(out) << "thrift_field_annotations = None" << endl;
indent(out) << "thrift_struct_annotations = None" << endl;
if (members.size() != 0) {
indent(out) << "__init__ = None" << endl;
}
// Generate `isUnion` method to distinguish union
indent(out) << "@staticmethod" << endl;
indent(out) << "def isUnion():" << endl;
indent(out) << " return False" << endl << endl;
generate_py_struct_reader(out, tstruct);
generate_py_struct_writer(out, tstruct);
generate_json_reader(out, tstruct);
// For exceptions only, generate a __str__ method. Use the message annotation
// if available, otherwise default to __repr__ explicitly. See python bug
// #5882
if (is_exception) {
out << indent() << "def __str__(self):" << endl;
if (const auto* msg = tstruct->find_annotation_or_null("message")) {
out << indent() << " if self." << *msg << ":" << endl
<< indent() << " return self." << *msg << endl
<< indent() << " else:" << endl
<< indent() << " return repr(self)" << endl;
} else {
out << indent() << " return repr(self)" << endl;
}
out << endl;
}
if (!gen_slots_) {
// According to Python doc, __repr__() "should" return a valid expression
// such that `object == eval(repr(object))` is true.
out << indent() << "def __repr__(self):" << endl
<< indent() << " L = []" << endl
<< indent() << " padding = ' ' * 4" << endl;
for (auto const& member : members) {
auto key = rename_reserved_keywords(member->get_name());
auto has_double_underscore = key.find("__") == 0;
if (has_double_underscore) {
out << indent() << " if getattr(self, \"" << key
<< "\", None) is not None:" << endl;
} else {
out << indent() << " if self." << key << " is not None:" << endl;
}
indent_up();
if (has_double_underscore) {
out << indent() << " value = pprint.pformat(getattr(self, \"" << key
<< "\", None), indent=0)" << endl;
} else {
out << indent() << " value = pprint.pformat(self." << key
<< ", indent=0)" << endl;
}
out << indent() << " value = padding.join(value.splitlines(True))"
<< endl
<< indent() << " L.append(' " << key << "=%s' % (value))" << endl;
indent_down();
}
// For exceptions only, force message attribute to be included in
// __repr__(). This is because BaseException.message has been deprecated as
// of Python 2.6 so python refuses to include the message attribute in
// __dict__ of an Exception object which is used for generating return
// value of __repr__.
if (is_exception) {
out << indent() << " if 'message' not in self.__dict__:" << endl
<< indent() << " message = getattr(self, 'message', None)" << endl
<< indent() << " if message:" << endl
<< indent() << " L.append('message=%r' % message)" << endl;
}
out << indent() << " return \"%s(%s)\" % (self.__class__.__name__, "
<< "\"\\n\" + \",\\n\".join(L) if L else '')" << endl
<< endl;
// Equality and inequality methods that compare by value
out << indent() << "def __eq__(self, other):" << endl;
indent_up();
out << indent() << "if not isinstance(other, self.__class__):" << endl;
indent_up();
out << indent() << "return False" << endl;
indent_down();
out << endl;
if (compare_t_fields_only_) {
out << indent() << "spec_t_fields = parse_struct_spec(self)" << endl;
out << indent() << "return "
<< "all(getattr(self, field.name, field.default) "
<< "== getattr(other, field.name, field.default)"
<< " for field in spec_t_fields)" << endl;
} else {
out << indent() << "return "
<< "self.__dict__ == other.__dict__ " << endl;
}
indent_down();
out << endl;
out << indent() << "def __ne__(self, other):" << endl;
indent_up();
out << indent() << "return not (self == other)" << endl;
indent_down();
out << endl;
} else {
// Use __slots__ instead of __dict__ for implementing
// __eq__, __repr__, __ne__
out << indent() << "def __repr__(self):" << endl
<< indent() << " L = []" << endl
<< indent() << " padding = ' ' * 4" << endl
<< indent() << " for key in self.__slots__:" << endl
<< indent() << " value = getattr(self, key)" << endl
<< indent() << " if value is None:" << endl
<< indent() << " continue" << endl
<< indent() << " value = pprint.pformat(value)" << endl
<< indent() << " value = padding.join(value.splitlines(True))"
<< endl
<< indent() << " L.append(' %s=%s' % (key, value))" << endl
<< indent() << " return \"%s(\\n%s)\" % (self.__class__.__name__, "
<< "\"\\n\" + \",\\n\".join(L) if L else '')" << endl
<< endl;
// Equality method that compares each attribute by value and type,
// walking __slots__
out << indent() << "def __eq__(self, other):" << endl
<< indent() << " if not isinstance(other, self.__class__):" << endl
<< indent() << " return False" << endl
<< indent() << " for attr in self.__slots__:" << endl
<< indent() << " my_val = getattr(self, attr)" << endl
<< indent() << " other_val = getattr(other, attr)" << endl
<< indent() << " if my_val != other_val:" << endl
<< indent() << " return False" << endl
<< indent() << " return True" << endl
<< endl;
out << indent() << "def __ne__(self, other):" << endl
<< indent() << " return not (self == other)" << endl
<< endl;
}
indent_down();
// Hash override for Python3 (t10434117)
indent_up();
out << indent() << "# Override the __hash__ function for Python3 - t10434117"
<< endl;
out << indent() << "__hash__ = object.__hash__" << endl;
out << endl;
indent_down();
}