There is a special case where message factories can be confused: if a module written in C++ with pybind11 links against a self-recursive message, and that message is part of another message loaded from Python, then the confusion will happen. Example: # This one is also linked into the C++ module. message SelfRecursive { optional SelfRecursive self_recursive = 1; } # This one is used only in Python and not linked. message OnlyUsedInPython { optional SelfRecursive self_recursive = 2; } The caching through message_factory::RegisterMessageClass then happens on one instance of the factory, but traversal with the lookup in another. This occurs in the pure Python and upb implementations that have their own default descriptor pools (and thus message factory). Fix this by using the already passed message factory to registering the message class to cache. A test accounts for this case to avoid regressions. PiperOrigin-RevId: 642551744pull/17034/head
parent
bbf52275db
commit
3784ba1959
7 changed files with 216 additions and 95 deletions
@ -0,0 +1,38 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
// Author: timdn@google.com (Tim Niemueller)
|
||||
|
||||
#include <pybind11/pybind11.h> |
||||
|
||||
#include <memory> |
||||
|
||||
#include "google/protobuf/message.h" |
||||
#include "google/protobuf/internal/self_recursive.pb.h" |
||||
#include "third_party/pybind11_protobuf/native_proto_caster.h" |
||||
|
||||
namespace google::protobuf::python { |
||||
|
||||
namespace py = pybind11; |
||||
|
||||
void invoke_callback_on_message(py::object callback, |
||||
const google::protobuf::Message& exemplar) { |
||||
std::shared_ptr<google::protobuf::Message> new_msg(exemplar.New()); |
||||
callback(new_msg); |
||||
} |
||||
|
||||
PYBIND11_MODULE(pybind11_test_module, m) { |
||||
pybind11_protobuf::ImportNativeProtoCasters(); |
||||
google::protobuf::LinkMessageReflection< |
||||
google::protobuf::python::internal::SelfRecursive>(); |
||||
|
||||
m.def("invoke_callback_on_message", &invoke_callback_on_message, |
||||
py::arg("callback"), py::arg("message")); |
||||
} |
||||
|
||||
} // namespace protobuf
|
||||
} // namespace google::python
|
@ -0,0 +1,44 @@ |
||||
"""Regression test for self or indirect recursive messages with pybind11.""" |
||||
|
||||
from google.protobuf.internal import pybind11_test_module |
||||
from google.protobuf.internal import self_recursive_from_py_pb2 |
||||
from google3.testing.pybase import unittest |
||||
|
||||
|
||||
class RecursiveMessagePybind11Test(unittest.TestCase): |
||||
|
||||
def test_self_recursive_message_callback(self): |
||||
called = False |
||||
|
||||
def callback( |
||||
msg: self_recursive_from_py_pb2.ContainsSelfRecursive, |
||||
) -> None: |
||||
nonlocal called |
||||
called = True |
||||
|
||||
# Without proper handling of message factories (in pyext/message.cc New) |
||||
# this will stack overflow |
||||
pybind11_test_module.invoke_callback_on_message( |
||||
callback, self_recursive_from_py_pb2.ContainsSelfRecursive() |
||||
) |
||||
self.assertTrue(called) |
||||
|
||||
def test_indirect_recursive_message_callback(self): |
||||
called = False |
||||
|
||||
def callback( |
||||
msg: self_recursive_from_py_pb2.ContainsIndirectRecursive, |
||||
) -> None: |
||||
nonlocal called |
||||
called = True |
||||
|
||||
# Without proper handling of message factories (in pyext/message.cc New) |
||||
# this will stack overflow |
||||
pybind11_test_module.invoke_callback_on_message( |
||||
callback, self_recursive_from_py_pb2.ContainsIndirectRecursive() |
||||
) |
||||
self.assertTrue(called) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
unittest.main() |
@ -0,0 +1,22 @@ |
||||
// Protocol Buffers - Google's data interchange format |
||||
// Copyright 2024 Google Inc. All rights reserved. |
||||
// |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file or at |
||||
// https://developers.google.com/open-source/licenses/bsd |
||||
|
||||
syntax = "proto2"; |
||||
|
||||
package google.protobuf.python.internal; |
||||
|
||||
message SelfRecursive { |
||||
optional SelfRecursive sub = 1; |
||||
} |
||||
|
||||
message IndirectRecursive { |
||||
optional IntermediateRecursive intermediate = 1; |
||||
} |
||||
|
||||
message IntermediateRecursive { |
||||
optional IndirectRecursive indirect = 1; |
||||
} |
@ -0,0 +1,20 @@ |
||||
// Protocol Buffers - Google's data interchange format |
||||
// Copyright 2024 Google Inc. All rights reserved. |
||||
// |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file or at |
||||
// https://developers.google.com/open-source/licenses/bsd |
||||
|
||||
syntax = "proto2"; |
||||
|
||||
package google.protobuf.python.internal; |
||||
|
||||
import "google/protobuf/internal/self_recursive.proto"; |
||||
|
||||
message ContainsSelfRecursive { |
||||
optional SelfRecursive recursive = 1; |
||||
} |
||||
|
||||
message ContainsIndirectRecursive { |
||||
optional IndirectRecursive recursive = 1; |
||||
} |
Loading…
Reference in new issue