22 #ifndef PROTO_PYTHON_BINDINGS_H
23 #define PROTO_PYTHON_BINDINGS_H
25 #include "google/protobuf/message.h"
26 #include <boost/python.hpp>
34 namespace bpy = boost::python;
35 namespace gpb = google::protobuf;
54 template<
class MSG_TYPE,
class ...FIELD_MSG_TYPES>
60 static_assert(std::is_base_of<google::protobuf::Message, MSG_TYPE>(),
"Parameter MSG_TYPE must derive from protobuf::Message");
61 static_assert((std::is_base_of_v<google::protobuf::Message, FIELD_MSG_TYPES...>),
"Parameter FIELD_MSG_TYPES must derive from protobuf::Message");
65 PyErr_SetString(error, msg.c_str());
66 boost::python::throw_error_already_set();
74 const gpb::FieldDescriptor *field = m.GetDescriptor()->FindFieldByName(name);
77 s << m.GetDescriptor()->name() <<
"\" object has no attribute \"" << name <<
"\"";
78 throw_python_error(PyExc_AttributeError, s.str());
84 else if(field->is_repeated())
86 if(field->cpp_type() == gpb::FieldDescriptor::CPPTYPE_MESSAGE)
91 else if(field->cpp_type() == gpb::FieldDescriptor::CPPTYPE_MESSAGE)
93 if constexpr (
sizeof...(FIELD_MSG_TYPES) > 0)
105 static void SetAttribute(MSG_TYPE& m,
char const* name,
const bpy::object& value)
107 const gpb::FieldDescriptor *field = m.GetDescriptor()->FindFieldByName(name);
110 s << m.GetDescriptor()->name() <<
"\" object has no attribute \"" << name <<
"\"";
111 throw_python_error(PyExc_AttributeError, s.str());
115 if(field->is_repeated() || field->is_map())
116 throw_python_error(PyExc_AttributeError,
117 "Assignment not allowed to repeated field \"" + field->name() +
"\" in protocol message object.");
118 else if(field->cpp_type() == gpb::FieldDescriptor::CPPTYPE_MESSAGE)
119 throw_python_error(PyExc_AttributeError,
120 "Assignment not allowed to field \"" + field->name() +
"\" in protocol message object.");
130 const gpb::OneofDescriptor *fieldOne = m.GetDescriptor()->FindOneofByName(name);
133 s <<
"Protocol message has no oneof \"" << name <<
"\" field";
134 PyErr_SetString(PyExc_ValueError, s.str().c_str());
135 boost::python::throw_error_already_set();
136 return bpy::object();
139 const gpb::FieldDescriptor *field = m.GetReflection()->GetOneofFieldDescriptor(m,fieldOne);
141 return bpy::object(field->name());
143 return bpy::object();
151 const gpb::FieldDescriptor *field = m.GetDescriptor()->FindFieldByName(name);
153 m.GetReflection()->ClearField(&m, field);
157 const gpb::OneofDescriptor *fieldOne = m.GetDescriptor()->FindOneofByName(name);
159 m.GetReflection()->ClearOneof(&m, fieldOne);
164 s <<
"Protocol message has no \"" << name <<
"\" field.";
165 PyErr_SetString(PyExc_ValueError, s.str().c_str());
166 boost::python::throw_error_already_set();
172 static bool HasField(MSG_TYPE& m,
char const* name)
174 const gpb::FieldDescriptor *field = m.GetDescriptor()->FindFieldByName(name);
176 return m.GetReflection()->HasField(m, field);
179 s <<
"Unknown field " << name;
180 PyErr_SetString(PyExc_ValueError, s.str().c_str());
181 boost::python::throw_error_already_set();
190 bpy::list fieldNames;
191 for(
auto i = 0; i < m.descriptor()->field_count(); ++i)
192 fieldNames.template append(
bpy::str(m.descriptor()->field(i)->name()));
201 const gpb::FieldDescriptor *field = m.GetDescriptor()->FindFieldByName(name);
203 return bpy::str(field->type_name());
206 s <<
"Unknown field " << name;
207 PyErr_SetString(PyExc_ValueError, s.str().c_str());
208 boost::python::throw_error_already_set();
216 std::shared_ptr<gpb::Message> m(
new MSG_TYPE());
217 const gpb::Descriptor *desc = m->GetDescriptor();
219 auto py_name = desc->full_name();
220 py_name.erase(std::remove(py_name.begin(), py_name.end(),
'.'), py_name.end());
222 bpy::class_<MSG_TYPE> binder(py_name.c_str());
223 binder.def(bpy::init<const MSG_TYPE &>(py_name.c_str()));
224 binder.def(
"__str__", &MSG_TYPE::DebugString);
225 binder.def(
"__getattr__", GetAttribute);
226 binder.def(
"__setattr__", SetAttribute);
227 binder.def(
"ClearField",ClearField);
228 binder.def(
"HasField", HasField);
229 binder.def(
"GetFieldNames", GetFieldNames);
230 binder.def(
"GetFieldTypeName", GetFieldTypeName);
231 binder.def(
"WhichOneof",WhichOneof);
232 binder.def(
"IsInitialized", &MSG_TYPE::IsInitialized);
233 binder.def(
"Clear", &MSG_TYPE::Clear);
239 #endif // PROTO_PYTHON_BINDINGS_H