diff --git a/rosidl_generator_py/CMakeLists.txt b/rosidl_generator_py/CMakeLists.txt index acffa894..c6cc067e 100644 --- a/rosidl_generator_py/CMakeLists.txt +++ b/rosidl_generator_py/CMakeLists.txt @@ -56,6 +56,7 @@ if(BUILD_TESTING) ${test_interface_files_MSG_FILES} # Cases not covered by test_interface_files msg/BuiltinTypeSequencesIdl.idl + msg/ByteArray.msg msg/StringArrays.msg msg/Property.msg ADD_LINTER_TESTS @@ -88,6 +89,13 @@ if(BUILD_TESTING) APPEND_LIBRARY_DIRS "${_append_library_dirs}" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_py" ) + + ament_add_pytest_test(test_byte_array_py test/test_byte_array.py + PYTHON_EXECUTABLE "${BUILDTYPE_PYTHON_EXECUTABLE}" + APPEND_ENV "PYTHONPATH=${pythonpath}" + APPEND_LIBRARY_DIRS "${_append_library_dirs}" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_py" + ) endif() endif() diff --git a/rosidl_generator_py/msg/ByteArray.msg b/rosidl_generator_py/msg/ByteArray.msg new file mode 100644 index 00000000..fb7239a1 --- /dev/null +++ b/rosidl_generator_py/msg/ByteArray.msg @@ -0,0 +1 @@ +byte[] data diff --git a/rosidl_generator_py/resource/_msg.py.em b/rosidl_generator_py/resource/_msg.py.em index 8c21e552..745bf14c 100644 --- a/rosidl_generator_py/resource/_msg.py.em +++ b/rosidl_generator_py/resource/_msg.py.em @@ -427,6 +427,7 @@ if isinstance(type_, AbstractNestedType): import inspect import builtins noqa_string = '' +byte_array_detected = False if member.name in dict(inspect.getmembers(builtins)).keys(): noqa_string = ' # noqa: A003' }@ @@ -503,8 +504,16 @@ if member.name in dict(inspect.getmembers(builtins)).keys(): @{assert_msg_suffixes.insert(1, 'with length %d' % member.type.size)}@ @[ end if]@ @[ end if]@ - all(isinstance(v, @(get_python_type(type_))) for v in value) and @{assert_msg_suffixes.append("and each value of type '%s'" % get_python_type(type_))}@ +@[ if get_python_type(type_) == 'bytes']@ +@{byte_array_detected = True}@ +@{assert_msg_suffixes.append("or type 'int' in range(0, 255)")}@ + (isinstance(value, @(get_python_type(type_))) or + all(isinstance(v, @(get_python_type(type_))) for v in value) or + all(isinstance(v, int) for v in value)) and +@[ else]@ + all(isinstance(v, @(get_python_type(type_))) for v in value) and +@[ end if]@ @[ if isinstance(type_, BasicType) and type_.typename in SIGNED_INTEGER_TYPES]@ @{ nbits = int(type_.typename[3:]) @@ -604,6 +613,13 @@ bound = 1.7976931348623157e+308 self._@(member.name) = array.array('@(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['type_code'])', value) @[ end if]@ @[ else]@ +@[ if byte_array_detected]@ + if any(isinstance(v, int) for v in value): + self._@(member.name) = bytes(value) + else: + self._@(member.name) = value +@[ else]@ self._@(member.name) = value +@[ end if]@ @[ end if]@ @[end for]@ diff --git a/rosidl_generator_py/test/test_byte_array.py b/rosidl_generator_py/test/test_byte_array.py new file mode 100644 index 00000000..fffedffa --- /dev/null +++ b/rosidl_generator_py/test/test_byte_array.py @@ -0,0 +1,75 @@ +# Copyright 2022 Sony Group Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +from rosidl_generator_py.msg import ByteArray + + +def test_msg_bytearray(): + msg = ByteArray() + + # types + assert isinstance(msg.data, list) + + # default values + assert [] == msg.data + + # set values + l1 = [1, 2, 3] + msg.data = l1 + # get values + assert bytes(l1) == msg.data + + # set values + l2 = {1, 2, 3} + msg.data = l2 + # get values + assert bytes(l2) == msg.data + + from collections import UserList + # set values + l3 = UserList(l1) + msg.data = l3 + # get values + assert bytes(l3) == msg.data + + # set values + l4 = bytes('123', 'utf-8') + msg.data = l4 + # get values + assert l4 == msg.data + + # set values + l5 = [b'1', b'2', b'3'] + msg.data = l5 + # get values + assert l5 == msg.data + + # set values + l6 = ['a', 'b'] + msg.data = l6 + # get values + assert l6 == msg.data + + +def test_msg_bytearray_exception(): + msg = ByteArray() + + with pytest.raises(ValueError): + l1 = [1, 2, 256] + msg.data = l1 + + with pytest.raises(TypeError): + l2 = [1, b'2', b'3'] + msg.data = l2