diff --git a/rosidl_generator_py/resource/_msg.py.em b/rosidl_generator_py/resource/_msg.py.em
index 976aee32..f10ae370 100644
--- a/rosidl_generator_py/resource/_msg.py.em
+++ b/rosidl_generator_py/resource/_msg.py.em
@@ -22,6 +22,7 @@ from rosidl_parser.definition import FLOATING_POINT_TYPES
from rosidl_parser.definition import INTEGER_TYPES
from rosidl_parser.definition import NamespacedType
from rosidl_parser.definition import SIGNED_INTEGER_TYPES
+from rosidl_parser.definition import UnboundedSequence
from rosidl_parser.definition import UNSIGNED_INTEGER_TYPES
}@
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@@ -30,6 +31,9 @@ from rosidl_parser.definition import UNSIGNED_INTEGER_TYPES
from collections import OrderedDict
import numpy
imports = OrderedDict()
+if message.structure.members:
+ imports.setdefault(
+ 'import rosidl_parser.definition', []) # used for SLOT_TYPES
for member in message.structure.members:
if (
isinstance(member.type, AbstractNestedType) and
@@ -65,7 +69,7 @@ for member in message.structure.members:
@(import_statement)@
@[ if import_statement not in import_statements]@
@{import_statements.add(import_statement)}@
- # noqa: E402@
+ # noqa: E402, I100@
@[ end if]
@[ end for]@
@[end if]@
@@ -227,6 +231,39 @@ string@
@[end for]@
}
+ SLOT_TYPES = (
+@[for member in message.structure.members]@
+@{
+type_ = member.type
+if isinstance(type_, AbstractNestedType):
+ type_ = type_.value_type
+}@
+ @
+@[ if isinstance(member.type, AbstractNestedType)]@
+@(member.type.__class__.__module__).@(member.type.__class__.__name__)(@
+@[ end if]@
+@# the typename of the non-nested type or the nested value_type
+@(type_.__class__.__module__).@(type_.__class__.__name__)(@
+@[ if isinstance(type_, BasicType)]@
+'@(type_.typename)'@
+@[ elif isinstance(type_, AbstractGenericString) and type_.has_maximum_size()]@
+@(type_.maximum_size)@
+@[ elif isinstance(type_, NamespacedType)]@
+[@(', '.join("'%s'" % n for n in type_.namespaces))], '@(type_.name)'@
+@[ end if]@
+)@
+@[ if isinstance(member.type, Array)]@
+, @(member.type.size)@
+@[ elif isinstance(member.type, BoundedSequence)]@
+, @(member.type.maximum_size)@
+@[ end if]@
+@[ if isinstance(member.type, AbstractNestedType)]@
+)@
+@[ end if]@
+, # noqa: E501
+@[end for]@
+ )
+
def __init__(self, **kwargs):
assert all('_' + key in self.__slots__ for key in kwargs.keys()), \
'Invalid arguments passed to constructor: %s' % \
diff --git a/rosidl_generator_py/test/test_interfaces.py b/rosidl_generator_py/test/test_interfaces.py
index e4a7cf59..6c4f23e0 100644
--- a/rosidl_generator_py/test/test_interfaces.py
+++ b/rosidl_generator_py/test/test_interfaces.py
@@ -16,6 +16,7 @@
import numpy
import pytest
+
from rosidl_generator_py.msg import Constants
from rosidl_generator_py.msg import Nested
from rosidl_generator_py.msg import Primitives
@@ -24,6 +25,13 @@
from rosidl_generator_py.msg import Various
from rosidl_generator_py.msg import WStrings
+from rosidl_parser.definition import Array
+from rosidl_parser.definition import BoundedSequence
+from rosidl_parser.definition import BoundedString
+from rosidl_parser.definition import NamespacedType
+from rosidl_parser.definition import UnboundedSequence
+from rosidl_parser.definition import UnboundedString
+
def test_strings():
a = Strings()
@@ -314,7 +322,7 @@ def test_slot_attributes():
assert expected_slot_type == nested_slot_types_dict[expected_field]
-def test_primative_slot_attributes():
+def test_string_slot_attributes():
b = StringArrays()
assert hasattr(b, 'get_fields_and_field_types')
assert hasattr(b, '__slots__')
@@ -349,3 +357,63 @@ def test_modifying_slot_fields_and_types():
string_slot_types_dict_len = len(string_slot_types_dict)
string_slot_types_dict[1] = 2
assert len(getattr(b, 'get_fields_and_field_types')()) == string_slot_types_dict_len
+
+
+def test_slot_types():
+ a = Nested()
+ assert hasattr(a, 'SLOT_TYPES')
+ assert hasattr(a, '__slots__')
+ nested_slot_types = Nested.SLOT_TYPES
+ nested_slots = getattr(a, '__slots__')
+ assert len(nested_slot_types) == len(nested_slots)
+ assert isinstance(nested_slot_types[0], NamespacedType)
+ assert nested_slot_types[0].namespaces == ['rosidl_generator_py', 'msg']
+ assert nested_slot_types[0].name == 'Primitives'
+
+ assert isinstance(nested_slot_types[1], Array)
+ assert isinstance(nested_slot_types[1].value_type, NamespacedType)
+ assert nested_slot_types[1].value_type.namespaces == \
+ ['rosidl_generator_py', 'msg']
+ assert nested_slot_types[1].value_type.name == 'Primitives'
+
+ assert isinstance(nested_slot_types[2], BoundedSequence)
+ assert isinstance(nested_slot_types[2].value_type, NamespacedType)
+ assert nested_slot_types[2].value_type.namespaces == \
+ ['rosidl_generator_py', 'msg']
+ assert nested_slot_types[2].value_type.name == 'Primitives'
+
+ assert isinstance(nested_slot_types[3], UnboundedSequence)
+ assert isinstance(nested_slot_types[3].value_type, NamespacedType)
+ assert nested_slot_types[3].value_type.namespaces == \
+ ['rosidl_generator_py', 'msg']
+ assert nested_slot_types[3].value_type.name == 'Primitives'
+
+
+def test_string_slot_types():
+ b = StringArrays()
+ assert hasattr(b, 'SLOT_TYPES')
+ assert hasattr(b, '__slots__')
+ string_slot_types = StringArrays.SLOT_TYPES
+ string_slots = getattr(b, '__slots__')
+ assert len(string_slot_types) == len(string_slots)
+
+ assert isinstance(string_slot_types[0], Array)
+ assert isinstance(string_slot_types[0].value_type, BoundedString)
+ assert string_slot_types[0].size == 3
+ assert string_slot_types[0].value_type.maximum_size == 5
+
+ assert isinstance(string_slot_types[1], BoundedSequence)
+ assert isinstance(string_slot_types[1].value_type, BoundedString)
+ assert string_slot_types[1].maximum_size == 10
+ assert string_slot_types[1].value_type.maximum_size == 5
+
+ assert isinstance(string_slot_types[2], UnboundedSequence)
+ assert isinstance(string_slot_types[2].value_type, BoundedString)
+ assert string_slot_types[2].value_type.maximum_size == 5
+
+ assert isinstance(string_slot_types[3], UnboundedSequence)
+ assert isinstance(string_slot_types[3].value_type, UnboundedString)
+
+ assert isinstance(string_slot_types[4], Array)
+ assert isinstance(string_slot_types[4].value_type, UnboundedString)
+ assert string_slot_types[4].size == 3
diff --git a/rosidl_runtime_py/package.xml b/rosidl_runtime_py/package.xml
index 9139c9a7..2de8eb66 100644
--- a/rosidl_runtime_py/package.xml
+++ b/rosidl_runtime_py/package.xml
@@ -11,6 +11,7 @@
python3-numpy
python3-yaml
+ rosidl_parser
ament_copyright
ament_flake8
diff --git a/rosidl_runtime_py/rosidl_runtime_py/__init__.py b/rosidl_runtime_py/rosidl_runtime_py/__init__.py
index 5f40b2ed..7a71aa92 100644
--- a/rosidl_runtime_py/rosidl_runtime_py/__init__.py
+++ b/rosidl_runtime_py/rosidl_runtime_py/__init__.py
@@ -12,13 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from .convert import get_message_slot_types
from .convert import message_to_csv
from .convert import message_to_ordereddict
from .convert import message_to_yaml
+from .import_message import import_message_from_namespaced_type
from .set_message import set_message_fields
__all__ = [
+ 'get_message_slot_types',
+ 'import_message_from_namespaced_type',
'message_to_csv',
'message_to_ordereddict',
'message_to_yaml',
diff --git a/rosidl_runtime_py/rosidl_runtime_py/convert.py b/rosidl_runtime_py/rosidl_runtime_py/convert.py
index fc783f13..1babe73c 100644
--- a/rosidl_runtime_py/rosidl_runtime_py/convert.py
+++ b/rosidl_runtime_py/rosidl_runtime_py/convert.py
@@ -154,3 +154,13 @@ def _convert_value(value, truncate_length=None):
# Assuming value is a message since it is neither a collection nor a primitive type
value = message_to_ordereddict(value, truncate_length=truncate_length)
return value
+
+
+def get_message_slot_types(msg: Any) -> OrderedDict:
+ """
+ Return an OrderedDict of the slot types of a message.
+
+ :param msg: The ROS message to get members types from.
+ :returns: An OrderedDict with message member names as keys and slot types as values.
+ """
+ return OrderedDict(zip([s[1:] for s in msg.__slots__], msg.SLOT_TYPES))
diff --git a/rosidl_runtime_py/rosidl_runtime_py/import_message.py b/rosidl_runtime_py/rosidl_runtime_py/import_message.py
new file mode 100644
index 00000000..ceb11ad8
--- /dev/null
+++ b/rosidl_runtime_py/rosidl_runtime_py/import_message.py
@@ -0,0 +1,24 @@
+# Copyright 2019 Mikael Arguedas.
+#
+# 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 importlib
+from typing import Any
+
+from rosidl_parser.definition import NamespacedType
+
+
+def import_message_from_namespaced_type(message_type: NamespacedType) -> Any:
+ module = importlib.import_module(
+ '.'.join(message_type.value_type.namespaces))
+ return getattr(module, message_type.value_type.name)
diff --git a/rosidl_runtime_py/rosidl_runtime_py/set_message.py b/rosidl_runtime_py/rosidl_runtime_py/set_message.py
index cfb36563..d84a7a4d 100644
--- a/rosidl_runtime_py/rosidl_runtime_py/set_message.py
+++ b/rosidl_runtime_py/rosidl_runtime_py/set_message.py
@@ -15,6 +15,11 @@
from typing import Any
from typing import Dict
+from rosidl_parser.definition import AbstractNestedType
+from rosidl_parser.definition import NamespacedType
+from rosidl_runtime_py.convert import get_message_slot_types
+from rosidl_runtime_py.import_message import import_message_from_namespaced_type
+
def set_message_fields(msg: Any, values: Dict[str, str]) -> None:
"""
@@ -33,4 +38,13 @@ def set_message_fields(msg: Any, values: Dict[str, str]) -> None:
except TypeError:
value = field_type()
set_message_fields(value, field_value)
+ rosidl_type = get_message_slot_types(msg)[field_name]
+ # Check if field is an array of ROS messages
+ if isinstance(rosidl_type, AbstractNestedType):
+ if isinstance(rosidl_type.value_type, NamespacedType):
+ field_elem_type = import_message_from_namespaced_type(rosidl_type)
+ for n in range(len(value)):
+ submsg = field_elem_type()
+ set_message_fields(submsg, value[n])
+ value[n] = submsg
setattr(msg, field_name, value)
diff --git a/rosidl_runtime_py/test/rosidl_runtime_py/test_set_message.py b/rosidl_runtime_py/test/rosidl_runtime_py/test_set_message.py
index a74a6d92..4f178d65 100644
--- a/rosidl_runtime_py/test/rosidl_runtime_py/test_set_message.py
+++ b/rosidl_runtime_py/test/rosidl_runtime_py/test_set_message.py
@@ -91,3 +91,39 @@ def test_set_message_fields_invalid():
invalid_type['int32_value'] = 'this is not an integer'
with pytest.raises(ValueError):
set_message_fields(msg, invalid_type)
+
+
+def test_set_nested_namespaced_fields():
+ unbounded_sequence_msg = message_fixtures.get_msg_unbounded_sequences()[1]
+ test_values = {
+ 'basic_types_values': [
+ {'float64_value': 42.42, 'int8_value': 42},
+ {'float64_value': 11.11, 'int8_value': 11}
+ ]
+ }
+ set_message_fields(unbounded_sequence_msg, test_values)
+ assert unbounded_sequence_msg.basic_types_values[0].float64_value == 42.42
+ assert unbounded_sequence_msg.basic_types_values[0].int8_value == 42
+ assert unbounded_sequence_msg.basic_types_values[0].uint8_value == 0
+ assert unbounded_sequence_msg.basic_types_values[1].float64_value == 11.11
+ assert unbounded_sequence_msg.basic_types_values[1].int8_value == 11
+ assert unbounded_sequence_msg.basic_types_values[1].uint8_value == 0
+
+ arrays_msg = message_fixtures.get_msg_arrays()[0]
+ test_values = {
+ 'basic_types_values': [
+ {'float64_value': 42.42, 'int8_value': 42},
+ {'float64_value': 11.11, 'int8_value': 11},
+ {'float64_value': 22.22, 'int8_value': 22},
+ ]
+ }
+ set_message_fields(arrays_msg, test_values)
+ assert arrays_msg.basic_types_values[0].float64_value == 42.42
+ assert arrays_msg.basic_types_values[0].int8_value == 42
+ assert arrays_msg.basic_types_values[0].uint8_value == 0
+ assert arrays_msg.basic_types_values[1].float64_value == 11.11
+ assert arrays_msg.basic_types_values[1].int8_value == 11
+ assert arrays_msg.basic_types_values[1].uint8_value == 0
+ assert arrays_msg.basic_types_values[2].float64_value == 22.22
+ assert arrays_msg.basic_types_values[2].int8_value == 22
+ assert arrays_msg.basic_types_values[2].uint8_value == 0