Skip to content

Add support for rosidl::Buffer in rosidl Python path for rclpy#250

Open
nvcyc wants to merge 1 commit intorollingfrom
native_buffer/5-rosidl_generator_py
Open

Add support for rosidl::Buffer in rosidl Python path for rclpy#250
nvcyc wants to merge 1 commit intorollingfrom
native_buffer/5-rosidl_generator_py

Conversation

@nvcyc
Copy link

@nvcyc nvcyc commented Mar 16, 2026

Description

This pull request adds rosidl::Buffer support for uint8[] fields in the Python code generation path.

This pull request consists of the following key changes:

  • Python message setters (_msg.py.em): For uint8[] fields, the generated setters now allow assigning from rosidl_buffer.Buffer or plain array.array. Existing code that sets msg.data = array.array('B', ...) continues to work unchanged.
  • Python/C conversion (_msg_support.c.em): Messages with uint8[] fields use the sequence's is_rosidl_buffer flag to determine the conversion path:
    • Python to C: For non-CPU rosidl_buffer.Buffer objects, calls _get_buffer_ptr() and sets the C++ Buffer pointer plus is_rosidl_buffer = true so the RMW path uses the existing buffer without copying. For array.array objects, uses the normal byte-copy path.
    • C to Python: When the RMW deserializes into a vendor-backed buffer (is_rosidl_buffer == true), calls _take_buffer_from_ptr() to wrap the pointer in a Python Buffer (taking ownership) and set it on the message. CPU-based data is converted to array.array as before.

Is this user-facing behavior change?

This pull request does not change existing rclpy behavior. Existing code using array.array for uint8[] fields continues to work. The rosidl_buffer.Buffer type is only encountered when a subscriber receives data from a non-CPU buffer backend (and has opted in via acceptable_buffer_backends).

Did you use Generative AI?

Yes. Claude (claude-4.6-opus) via Cursor was used to assist with creating an initial prototype version of the changes contained in this PR.

Additional Information

This PR is part of the broader ROS 2 native buffer feature introduced in this post.

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass. Ownership is bit fuzzy.

Py_DECREF(backend_attr);
Py_DECREF(field);
// Sentinel is set, skip normal conversion for this field
goto @(member.name)__done;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nvcyc why not simply return true? Is this missing some cleanup?

@(bi) if (length > 0) {
@(bi) PyObject * pop = PyObject_GetAttrString(field, "pop");
@(bi) assert(pop != NULL);
@(bi) for (Py_ssize_t i = 0; i < length; ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nvcyc meta: I know this code predates your implementation but wow. I can see looping over pop instead of clear having a massive impact in runtime performance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants