dep-protobuf/python/google/protobuf/internal/recursive_message_pybind11_test.py
Protobuf Team Bot 3784ba1959 Fix stack overflow on self-recursive messages with pybind11
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: 642551744
2024-06-12 02:35:05 -07:00

44 lines
1.3 KiB
Python

"""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()