From f45dc8f66c521a855295ebfd49eb0b8a2da44149 Mon Sep 17 00:00:00 2001 From: CY Chen Date: Fri, 5 Jun 2026 17:43:42 +0000 Subject: [PATCH 1/3] Show rosidl buffer backend metadata in topic endpoint info Signed-off-by: CY Chen --- rclpy/rclpy/endpoint_info.py | 26 ++++++++++++++++++++++++-- rclpy/rclpy/impl/_rclpy_pybind11.pyi | 1 + rclpy/src/rclpy/utils.cpp | 11 +++++++++++ rclpy/test/test_topic_endpoint_info.py | 14 ++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/rclpy/rclpy/endpoint_info.py b/rclpy/rclpy/endpoint_info.py index 90ec30aeb..e34b2e0b5 100644 --- a/rclpy/rclpy/endpoint_info.py +++ b/rclpy/rclpy/endpoint_info.py @@ -48,7 +48,8 @@ class TopicEndpointInfo: '_topic_type_hash', '_endpoint_type', '_endpoint_gid', - '_qos_profile' + '_qos_profile', + '_buffer_backend_metadata' ] def __init__( @@ -60,7 +61,8 @@ def __init__( endpoint_type: Union[EndpointTypeEnum, int] = EndpointTypeEnum.INVALID, endpoint_gid: list[int] = [], qos_profile: Union[QoSProfile, '_rclpy._rmw_qos_profile_dict'] = - QoSPresetProfiles.UNKNOWN.value + QoSPresetProfiles.UNKNOWN.value, + buffer_backend_metadata: dict[str, str] = {} ): self.node_name = node_name self.node_namespace = node_namespace @@ -69,6 +71,7 @@ def __init__( self.endpoint_type = endpoint_type self.endpoint_gid = endpoint_gid self.qos_profile = qos_profile + self.buffer_backend_metadata = buffer_backend_metadata @property def node_name(self) -> str: @@ -183,6 +186,21 @@ def qos_profile(self, value: Union[QoSProfile, '_rclpy._rmw_qos_profile_dict']) else: assert False + @property + def buffer_backend_metadata(self) -> dict[str, str]: + """ + Get field 'buffer_backend_metadata'. + + :returns: mapping from buffer backend name to backend metadata + """ + return self._buffer_backend_metadata + + @buffer_backend_metadata.setter + def buffer_backend_metadata(self, value: dict[str, str]) -> None: + assert isinstance(value, dict) + assert all(isinstance(k, str) and isinstance(v, str) for k, v in value.items()) + self._buffer_backend_metadata = value + def __eq__(self, other: object) -> bool: if not isinstance(other, TopicEndpointInfo): return False @@ -196,6 +214,9 @@ def __str__(self) -> str: history_depth_str = self.qos_profile.history.name else: history_depth_str = f'{self.qos_profile.history.name} ({self.qos_profile.depth})' + buffer_backend_str = ', '.join(sorted(self.buffer_backend_metadata.keys())) + if not buffer_backend_str: + buffer_backend_str = '' return '\n'.join([ f'Node name: {self.node_name}', f'Node namespace: {self.node_namespace}', @@ -203,6 +224,7 @@ def __str__(self) -> str: f'Topic type hash: {self.topic_type_hash}', f'Endpoint type: {self.endpoint_type.name}', f'GID: {gid}', + f'Buffer backends: {buffer_backend_str}', 'QoS profile:', f' Reliability: {self.qos_profile.reliability.name}', f' History (Depth): {history_depth_str}', diff --git a/rclpy/rclpy/impl/_rclpy_pybind11.pyi b/rclpy/rclpy/impl/_rclpy_pybind11.pyi index 6ea0a9a43..6d46eec27 100644 --- a/rclpy/rclpy/impl/_rclpy_pybind11.pyi +++ b/rclpy/rclpy/impl/_rclpy_pybind11.pyi @@ -801,6 +801,7 @@ class _TopicEndpointInfoDict(TypedDict): endpoint_type: int endpoint_gid: list[int] qos_profile: _rmw_qos_profile_dict + buffer_backend_metadata: dict[str, str] class _ServiceEndpointInfoDict(TypedDict): diff --git a/rclpy/src/rclpy/utils.cpp b/rclpy/src/rclpy/utils.cpp index 7d603123e..46d131a60 100644 --- a/rclpy/src/rclpy/utils.cpp +++ b/rclpy/src/rclpy/utils.cpp @@ -313,6 +313,17 @@ _convert_to_py_topic_endpoint_info(const rmw_topic_endpoint_info_t * topic_endpo py_endpoint_info_dict["endpoint_gid"] = py_endpoint_gid; py_endpoint_info_dict["qos_profile"] = convert_to_qos_dict(&topic_endpoint_info->qos_profile); + py::dict py_buffer_backend_metadata; + const char * current_key = rcutils_string_map_get_next_key( + &topic_endpoint_info->buffer_backend_metadata, nullptr); + while (current_key) { + const char * current_value = rcutils_string_map_get( + &topic_endpoint_info->buffer_backend_metadata, current_key); + py_buffer_backend_metadata[py::str(current_key)] = py::str(current_value ? current_value : ""); + current_key = rcutils_string_map_get_next_key( + &topic_endpoint_info->buffer_backend_metadata, current_key); + } + py_endpoint_info_dict["buffer_backend_metadata"] = py_buffer_backend_metadata; return py_endpoint_info_dict; } diff --git a/rclpy/test/test_topic_endpoint_info.py b/rclpy/test/test_topic_endpoint_info.py index af4dc352c..e9500275d 100644 --- a/rclpy/test/test_topic_endpoint_info.py +++ b/rclpy/test/test_topic_endpoint_info.py @@ -88,6 +88,19 @@ def test_qos_profile_only_constructor(self) -> None: self.assertEqual(info_for_ref, info_from_ctor) self.assertEqual(test_qos_profile, info_from_ctor.qos_profile) + def test_buffer_backend_metadata_only_constructor(self) -> None: + test_buffer_backend_metadata = {'cuda': 'version=1.0'} + + info_for_ref = TopicEndpointInfo() + info_for_ref.buffer_backend_metadata = test_buffer_backend_metadata + + info_from_ctor = TopicEndpointInfo( + buffer_backend_metadata=test_buffer_backend_metadata) + + self.assertEqual(info_for_ref, info_from_ctor) + self.assertEqual( + test_buffer_backend_metadata, info_from_ctor.buffer_backend_metadata) + def test_print(self) -> None: actual_info_str = str(TopicEndpointInfo()) expected_info_str = 'Node name: \n' \ @@ -96,6 +109,7 @@ def test_print(self) -> None: 'Topic type hash: INVALID\n' \ 'Endpoint type: INVALID\n' \ 'GID: \n' \ + 'Buffer backends: \n' \ 'QoS profile:\n' \ ' Reliability: UNKNOWN\n' \ ' History (Depth): UNKNOWN\n' \ From de244bcba4ee6d48b8b4fae43d198434af00a18f Mon Sep 17 00:00:00 2001 From: CY Chen Date: Fri, 5 Jun 2026 23:41:14 +0000 Subject: [PATCH 2/3] Include backend metadata in the buffer backends string Signed-off-by: CY Chen --- rclpy/rclpy/endpoint_info.py | 4 +++- rclpy/test/test_topic_endpoint_info.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rclpy/rclpy/endpoint_info.py b/rclpy/rclpy/endpoint_info.py index e34b2e0b5..a29afd5cc 100644 --- a/rclpy/rclpy/endpoint_info.py +++ b/rclpy/rclpy/endpoint_info.py @@ -214,7 +214,9 @@ def __str__(self) -> str: history_depth_str = self.qos_profile.history.name else: history_depth_str = f'{self.qos_profile.history.name} ({self.qos_profile.depth})' - buffer_backend_str = ', '.join(sorted(self.buffer_backend_metadata.keys())) + buffer_backend_str = ', '.join( + f'{backend}({metadata})' if metadata else backend + for backend, metadata in sorted(self.buffer_backend_metadata.items())) if not buffer_backend_str: buffer_backend_str = '' return '\n'.join([ diff --git a/rclpy/test/test_topic_endpoint_info.py b/rclpy/test/test_topic_endpoint_info.py index e9500275d..b99036249 100644 --- a/rclpy/test/test_topic_endpoint_info.py +++ b/rclpy/test/test_topic_endpoint_info.py @@ -89,7 +89,7 @@ def test_qos_profile_only_constructor(self) -> None: self.assertEqual(test_qos_profile, info_from_ctor.qos_profile) def test_buffer_backend_metadata_only_constructor(self) -> None: - test_buffer_backend_metadata = {'cuda': 'version=1.0'} + test_buffer_backend_metadata = {'cpu': ''} info_for_ref = TopicEndpointInfo() info_for_ref.buffer_backend_metadata = test_buffer_backend_metadata From 3a3a951c71cbdc97b3481c303e05282fe7886f44 Mon Sep 17 00:00:00 2001 From: CY Chen Date: Wed, 1 Jul 2026 13:58:45 +0000 Subject: [PATCH 3/3] Parse serialized buffer metadata for Python endpoint info Signed-off-by: CY Chen --- rclpy/rclpy/endpoint_info.py | 5 +++-- rclpy/src/rclpy/utils.cpp | 13 +++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/rclpy/rclpy/endpoint_info.py b/rclpy/rclpy/endpoint_info.py index a29afd5cc..936eb6880 100644 --- a/rclpy/rclpy/endpoint_info.py +++ b/rclpy/rclpy/endpoint_info.py @@ -60,8 +60,9 @@ def __init__( topic_type_hash: Union[TypeHash, TypeHashDictionary] = TypeHash(), endpoint_type: Union[EndpointTypeEnum, int] = EndpointTypeEnum.INVALID, endpoint_gid: list[int] = [], - qos_profile: Union[QoSProfile, '_rclpy._rmw_qos_profile_dict'] = - QoSPresetProfiles.UNKNOWN.value, + qos_profile: Union[QoSProfile, '_rclpy._rmw_qos_profile_dict'] = ( + QoSPresetProfiles.UNKNOWN.value + ), buffer_backend_metadata: dict[str, str] = {} ): self.node_name = node_name diff --git a/rclpy/src/rclpy/utils.cpp b/rclpy/src/rclpy/utils.cpp index 46d131a60..3cd5d7535 100644 --- a/rclpy/src/rclpy/utils.cpp +++ b/rclpy/src/rclpy/utils.cpp @@ -32,6 +32,7 @@ #include #include +#include #include "exceptions.hpp" #include "utils.hpp" @@ -314,14 +315,10 @@ _convert_to_py_topic_endpoint_info(const rmw_topic_endpoint_info_t * topic_endpo py_endpoint_info_dict["qos_profile"] = convert_to_qos_dict(&topic_endpoint_info->qos_profile); py::dict py_buffer_backend_metadata; - const char * current_key = rcutils_string_map_get_next_key( - &topic_endpoint_info->buffer_backend_metadata, nullptr); - while (current_key) { - const char * current_value = rcutils_string_map_get( - &topic_endpoint_info->buffer_backend_metadata, current_key); - py_buffer_backend_metadata[py::str(current_key)] = py::str(current_value ? current_value : ""); - current_key = rcutils_string_map_get_next_key( - &topic_endpoint_info->buffer_backend_metadata, current_key); + for (const auto & backend_pair : + rmw::impl::cpp::parse_buffer_backend_metadata(topic_endpoint_info->buffer_backend_metadata)) + { + py_buffer_backend_metadata[py::str(backend_pair.first)] = py::str(backend_pair.second); } py_endpoint_info_dict["buffer_backend_metadata"] = py_buffer_backend_metadata;