2121#include " absl/strings/numbers.h"
2222#include " absl/strings/str_replace.h"
2323#include " absl/strings/str_split.h"
24+ #include " absl/strings/string_view.h"
2425#include " absl/types/optional.h"
2526#include " pybind11_protobuf/check_unknown_fields.h"
2627
@@ -534,10 +535,8 @@ class PythonDescriptorPoolWrapper {
534535 }
535536 }
536537
537- py::object wire = py_file_descriptor.attr (" serialized_pb" );
538- const char * bytes = PYBIND11_BYTES_AS_STRING (wire.ptr ());
539- return output->ParsePartialFromArray (bytes,
540- PYBIND11_BYTES_SIZE (wire.ptr ()));
538+ return output->ParsePartialFromString (
539+ PyBytesAsStringView (py_file_descriptor.attr (" serialized_pb" )));
541540 }
542541
543542 py::object pool_; // never dereferenced.
@@ -549,6 +548,11 @@ class PythonDescriptorPoolWrapper {
549548
550549} // namespace
551550
551+ absl::string_view PyBytesAsStringView (py::bytes py_bytes) {
552+ return absl::string_view (PyBytes_AsString (py_bytes.ptr ()),
553+ PyBytes_Size (py_bytes.ptr ()));
554+ }
555+
552556void InitializePybindProtoCastUtil () {
553557 assert (PyGILState_Check ());
554558 GlobalState::instance ();
@@ -593,7 +597,7 @@ const Message* PyProtoGetCppMessagePointer(py::handle src) {
593597#endif
594598}
595599
596- absl::optional<std::string> PyProtoDescriptorName (py::handle py_proto) {
600+ absl::optional<std::string> PyProtoDescriptorFullName (py::handle py_proto) {
597601 assert (PyGILState_Check ());
598602 auto py_full_name = ResolveAttrs (py_proto, {" DESCRIPTOR" , " full_name" });
599603 if (py_full_name) {
@@ -602,66 +606,42 @@ absl::optional<std::string> PyProtoDescriptorName(py::handle py_proto) {
602606 return absl::nullopt ;
603607}
604608
605- bool PyProtoIsCompatible (py::handle py_proto, const Descriptor* descriptor) {
606- assert (PyGILState_Check ());
607- if (descriptor->file ()->pool () != DescriptorPool::generated_pool ()) {
608- // / This indicates that the C++ descriptor does not come from the C++
609- // / DescriptorPool. This may happen if the C++ code has the same proto
610- // / in different descriptor pools, perhaps from different shared objects,
611- // / and could be result in undefined behavior.
612- return false ;
613- }
614-
615- auto py_descriptor = ResolveAttrs (py_proto, {" DESCRIPTOR" });
616- if (!py_descriptor) {
617- // Not a valid protobuf -- missing DESCRIPTOR.
618- return false ;
619- }
620-
621- // Test full_name equivalence.
622- {
623- auto py_full_name = ResolveAttrs (*py_descriptor, {" full_name" });
624- if (!py_full_name) {
625- // Not a valid protobuf -- missing DESCRIPTOR.full_name
626- return false ;
627- }
628- auto full_name = CastToOptionalString (*py_full_name);
629- if (!full_name || *full_name != descriptor->full_name ()) {
630- // Name mismatch.
631- return false ;
632- }
633- }
634-
635- // The C++ descriptor is compiled in (see above assert), so the py_proto
636- // is expected to be from the global pool, i.e. the DESCRIPTOR.file.pool
637- // instance is the global python pool, and not a custom pool.
638- auto py_pool = ResolveAttrs (*py_descriptor, {" file" , " pool" });
639- if (py_pool) {
640- return py_pool->is (GlobalState::instance ()->global_pool ());
641- }
642-
643- // The py_proto is missing a DESCRIPTOR.file.pool, but the name matches.
644- // This will not happen with a native python implementation, but does
645- // occur with the deprecated :proto_casters, and could happen with other
646- // mocks. Returning true allows the caster to call PyProtoCopyToCProto.
647- return true ;
609+ bool PyProtoHasMatchingFullName (py::handle py_proto,
610+ const Descriptor* descriptor) {
611+ auto full_name = PyProtoDescriptorFullName (py_proto);
612+ return full_name && *full_name == descriptor->full_name ();
648613}
649614
650- bool PyProtoCopyToCProto (py::handle py_proto, Message* message) {
651- assert (PyGILState_Check ());
652- auto serialize_fn = ResolveAttrMRO (py_proto, " SerializePartialToString" );
615+ py::bytes PyProtoSerializePartialToString (py::handle py_proto,
616+ bool raise_if_error) {
617+ static const char * serialize_fn_name = " SerializePartialToString" ;
618+ auto serialize_fn = ResolveAttrMRO (py_proto, serialize_fn_name);
653619 if (!serialize_fn) {
654- throw py::type_error (
655- " SerializePartialToString method not found; is this a " +
656- message->GetDescriptor ()->full_name ());
657- }
658- auto wire = (*serialize_fn)();
659- const char * bytes = PYBIND11_BYTES_AS_STRING (wire.ptr ());
660- if (!bytes) {
661- throw py::type_error (" SerializePartialToString failed; is this a " +
662- message->GetDescriptor ()->full_name ());
620+ return py::object ();
621+ }
622+ auto serialized_bytes = py::reinterpret_steal<py::object>(
623+ PyObject_CallObject (serialize_fn->ptr (), nullptr ));
624+ if (!serialized_bytes) {
625+ if (raise_if_error) {
626+ std::string msg = py::repr (py_proto).cast <std::string>() + " ." +
627+ serialize_fn_name + " () function call FAILED" ;
628+ py::raise_from (PyExc_TypeError, msg.c_str ());
629+ throw py::error_already_set ();
630+ }
631+ return py::object ();
632+ }
633+ if (!PyBytes_Check (serialized_bytes.ptr ())) {
634+ if (raise_if_error) {
635+ std::string msg = py::repr (py_proto).cast <std::string>() + " ." +
636+ serialize_fn_name +
637+ " () function call is expected to return bytes, but the "
638+ " returned value is " +
639+ py::repr (serialized_bytes).cast <std::string>();
640+ throw py::type_error (msg);
641+ }
642+ return py::object ();
663643 }
664- return message-> ParsePartialFromArray (bytes, PYBIND11_BYTES_SIZE (wire. ptr ())) ;
644+ return serialized_bytes ;
665645}
666646
667647void CProtoCopyToPyProto (Message* message, py::handle py_proto) {
@@ -686,7 +666,8 @@ std::unique_ptr<Message> AllocateCProtoFromPythonSymbolDatabase(
686666 assert (PyGILState_Check ());
687667 auto pool = ResolveAttrs (src, {" DESCRIPTOR" , " file" , " pool" });
688668 if (!pool) {
689- throw py::type_error (" Object is not a valid protobuf" );
669+ throw py::type_error (py::repr (src).cast <std::string>() +
670+ " object is not a valid protobuf" );
690671 }
691672
692673 auto pool_data =
0 commit comments