Skip to content

Commit 78b765d

Browse files
mikaelarguedasdirk-thomas
authored andcommitted
store types as tuple of abstract types (#33)
* store types as constant and return ordered dict Signed-off-by: Mikael Arguedas <[email protected]> update tests Signed-off-by: Mikael Arguedas <[email protected]> simplify logic by printing member type directly Signed-off-by: Mikael Arguedas <[email protected]> move dict construction to rosidl_runtime_py Signed-off-by: Mikael Arguedas <[email protected]> add utility to import complex message and add support for nested array in set_message Signed-off-by: Mikael Arguedas <[email protected]> update tests and remove coverage for dict to avoid circular dependency Signed-off-by: Mikael Arguedas <[email protected]> more descriptive variable name Signed-off-by: Mikael Arguedas <[email protected]> refactor import logic according to #35 Signed-off-by: Mikael Arguedas <[email protected]> move imports to top of file Signed-off-by: Mikael Arguedas <[email protected]> string maximum size doesn't need quoting or str conversion Signed-off-by: Mikael Arguedas <[email protected]> update docblock Signed-off-by: Mikael Arguedas <[email protected]> slot_types_dict_from_message -> get_message_slot_types Signed-off-by: Mikael Arguedas <[email protected]> check Abstrct type instead of 'list' Signed-off-by: Mikael Arguedas <[email protected]> use NestedType Signed-off-by: Mikael Arguedas <[email protected]> add conditional is namespaces is an empty list Signed-off-by: Mikael Arguedas <[email protected]> one liner Signed-off-by: Mikael Arguedas <[email protected]> extend test suite to cover NesteTypes of NamespacedTypes Signed-off-by: Mikael Arguedas <[email protected]> single message fixture to use Signed-off-by: Mikael Arguedas <[email protected]> use NestedType Signed-off-by: Mikael Arguedas <[email protected]> add coverage for static array of nested messages Signed-off-by: Mikael Arguedas <[email protected]> restore old API Signed-off-by: Mikael Arguedas <[email protected]> SLOT_TYPES improvement from dirk-thomas Signed-off-by: Mikael Arguedas <[email protected]> resolve conflicts Signed-off-by: Mikael Arguedas <[email protected]> * add back I100 ignore Signed-off-by: Mikael Arguedas <[email protected]> * use new rosidl_parser.definition types and new test interfaces Signed-off-by: Mikael Arguedas <[email protected]> * restore original test, add test for SLOT_TYPES Signed-off-by: Mikael Arguedas <[email protected]>
1 parent 9dc2ed0 commit 78b765d

File tree

8 files changed

+196
-2
lines changed

8 files changed

+196
-2
lines changed

rosidl_generator_py/resource/_msg.py.em

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ from rosidl_parser.definition import FLOATING_POINT_TYPES
2222
from rosidl_parser.definition import INTEGER_TYPES
2323
from rosidl_parser.definition import NamespacedType
2424
from rosidl_parser.definition import SIGNED_INTEGER_TYPES
25+
from rosidl_parser.definition import UnboundedSequence
2526
from rosidl_parser.definition import UNSIGNED_INTEGER_TYPES
2627
}@
2728
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@@ -30,6 +31,9 @@ from rosidl_parser.definition import UNSIGNED_INTEGER_TYPES
3031
from collections import OrderedDict
3132
import numpy
3233
imports = OrderedDict()
34+
if message.structure.members:
35+
imports.setdefault(
36+
'import rosidl_parser.definition', []) # used for SLOT_TYPES
3337
for member in message.structure.members:
3438
if (
3539
isinstance(member.type, AbstractNestedType) and
@@ -65,7 +69,7 @@ for member in message.structure.members:
6569
@(import_statement)@
6670
@[ if import_statement not in import_statements]@
6771
@{import_statements.add(import_statement)}@
68-
# noqa: E402@
72+
# noqa: E402, I100@
6973
@[ end if]
7074
@[ end for]@
7175
@[end if]@
@@ -227,6 +231,39 @@ string@
227231
@[end for]@
228232
}
229233

234+
SLOT_TYPES = (
235+
@[for member in message.structure.members]@
236+
@{
237+
type_ = member.type
238+
if isinstance(type_, AbstractNestedType):
239+
type_ = type_.value_type
240+
}@
241+
@
242+
@[ if isinstance(member.type, AbstractNestedType)]@
243+
@(member.type.__class__.__module__).@(member.type.__class__.__name__)(@
244+
@[ end if]@
245+
@# the typename of the non-nested type or the nested value_type
246+
@(type_.__class__.__module__).@(type_.__class__.__name__)(@
247+
@[ if isinstance(type_, BasicType)]@
248+
'@(type_.typename)'@
249+
@[ elif isinstance(type_, AbstractGenericString) and type_.has_maximum_size()]@
250+
@(type_.maximum_size)@
251+
@[ elif isinstance(type_, NamespacedType)]@
252+
[@(', '.join("'%s'" % n for n in type_.namespaces))], '@(type_.name)'@
253+
@[ end if]@
254+
)@
255+
@[ if isinstance(member.type, Array)]@
256+
, @(member.type.size)@
257+
@[ elif isinstance(member.type, BoundedSequence)]@
258+
, @(member.type.maximum_size)@
259+
@[ end if]@
260+
@[ if isinstance(member.type, AbstractNestedType)]@
261+
)@
262+
@[ end if]@
263+
, # noqa: E501
264+
@[end for]@
265+
)
266+
230267
def __init__(self, **kwargs):
231268
assert all('_' + key in self.__slots__ for key in kwargs.keys()), \
232269
'Invalid arguments passed to constructor: %s' % \

rosidl_generator_py/test/test_interfaces.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import numpy
1818
import pytest
19+
1920
from rosidl_generator_py.msg import Constants
2021
from rosidl_generator_py.msg import Nested
2122
from rosidl_generator_py.msg import Primitives
@@ -24,6 +25,13 @@
2425
from rosidl_generator_py.msg import Various
2526
from rosidl_generator_py.msg import WStrings
2627

28+
from rosidl_parser.definition import Array
29+
from rosidl_parser.definition import BoundedSequence
30+
from rosidl_parser.definition import BoundedString
31+
from rosidl_parser.definition import NamespacedType
32+
from rosidl_parser.definition import UnboundedSequence
33+
from rosidl_parser.definition import UnboundedString
34+
2735

2836
def test_strings():
2937
a = Strings()
@@ -314,7 +322,7 @@ def test_slot_attributes():
314322
assert expected_slot_type == nested_slot_types_dict[expected_field]
315323

316324

317-
def test_primative_slot_attributes():
325+
def test_string_slot_attributes():
318326
b = StringArrays()
319327
assert hasattr(b, 'get_fields_and_field_types')
320328
assert hasattr(b, '__slots__')
@@ -349,3 +357,63 @@ def test_modifying_slot_fields_and_types():
349357
string_slot_types_dict_len = len(string_slot_types_dict)
350358
string_slot_types_dict[1] = 2
351359
assert len(getattr(b, 'get_fields_and_field_types')()) == string_slot_types_dict_len
360+
361+
362+
def test_slot_types():
363+
a = Nested()
364+
assert hasattr(a, 'SLOT_TYPES')
365+
assert hasattr(a, '__slots__')
366+
nested_slot_types = Nested.SLOT_TYPES
367+
nested_slots = getattr(a, '__slots__')
368+
assert len(nested_slot_types) == len(nested_slots)
369+
assert isinstance(nested_slot_types[0], NamespacedType)
370+
assert nested_slot_types[0].namespaces == ['rosidl_generator_py', 'msg']
371+
assert nested_slot_types[0].name == 'Primitives'
372+
373+
assert isinstance(nested_slot_types[1], Array)
374+
assert isinstance(nested_slot_types[1].value_type, NamespacedType)
375+
assert nested_slot_types[1].value_type.namespaces == \
376+
['rosidl_generator_py', 'msg']
377+
assert nested_slot_types[1].value_type.name == 'Primitives'
378+
379+
assert isinstance(nested_slot_types[2], BoundedSequence)
380+
assert isinstance(nested_slot_types[2].value_type, NamespacedType)
381+
assert nested_slot_types[2].value_type.namespaces == \
382+
['rosidl_generator_py', 'msg']
383+
assert nested_slot_types[2].value_type.name == 'Primitives'
384+
385+
assert isinstance(nested_slot_types[3], UnboundedSequence)
386+
assert isinstance(nested_slot_types[3].value_type, NamespacedType)
387+
assert nested_slot_types[3].value_type.namespaces == \
388+
['rosidl_generator_py', 'msg']
389+
assert nested_slot_types[3].value_type.name == 'Primitives'
390+
391+
392+
def test_string_slot_types():
393+
b = StringArrays()
394+
assert hasattr(b, 'SLOT_TYPES')
395+
assert hasattr(b, '__slots__')
396+
string_slot_types = StringArrays.SLOT_TYPES
397+
string_slots = getattr(b, '__slots__')
398+
assert len(string_slot_types) == len(string_slots)
399+
400+
assert isinstance(string_slot_types[0], Array)
401+
assert isinstance(string_slot_types[0].value_type, BoundedString)
402+
assert string_slot_types[0].size == 3
403+
assert string_slot_types[0].value_type.maximum_size == 5
404+
405+
assert isinstance(string_slot_types[1], BoundedSequence)
406+
assert isinstance(string_slot_types[1].value_type, BoundedString)
407+
assert string_slot_types[1].maximum_size == 10
408+
assert string_slot_types[1].value_type.maximum_size == 5
409+
410+
assert isinstance(string_slot_types[2], UnboundedSequence)
411+
assert isinstance(string_slot_types[2].value_type, BoundedString)
412+
assert string_slot_types[2].value_type.maximum_size == 5
413+
414+
assert isinstance(string_slot_types[3], UnboundedSequence)
415+
assert isinstance(string_slot_types[3].value_type, UnboundedString)
416+
417+
assert isinstance(string_slot_types[4], Array)
418+
assert isinstance(string_slot_types[4].value_type, UnboundedString)
419+
assert string_slot_types[4].size == 3

rosidl_runtime_py/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
<exec_depend>python3-numpy</exec_depend>
1313
<exec_depend>python3-yaml</exec_depend>
14+
<exec_depend>rosidl_parser</exec_depend>
1415

1516
<test_depend>ament_copyright</test_depend>
1617
<test_depend>ament_flake8</test_depend>

rosidl_runtime_py/rosidl_runtime_py/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from .convert import get_message_slot_types
1516
from .convert import message_to_csv
1617
from .convert import message_to_ordereddict
1718
from .convert import message_to_yaml
19+
from .import_message import import_message_from_namespaced_type
1820
from .set_message import set_message_fields
1921

2022

2123
__all__ = [
24+
'get_message_slot_types',
25+
'import_message_from_namespaced_type',
2226
'message_to_csv',
2327
'message_to_ordereddict',
2428
'message_to_yaml',

rosidl_runtime_py/rosidl_runtime_py/convert.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,13 @@ def _convert_value(value, truncate_length=None):
154154
# Assuming value is a message since it is neither a collection nor a primitive type
155155
value = message_to_ordereddict(value, truncate_length=truncate_length)
156156
return value
157+
158+
159+
def get_message_slot_types(msg: Any) -> OrderedDict:
160+
"""
161+
Return an OrderedDict of the slot types of a message.
162+
163+
:param msg: The ROS message to get members types from.
164+
:returns: An OrderedDict with message member names as keys and slot types as values.
165+
"""
166+
return OrderedDict(zip([s[1:] for s in msg.__slots__], msg.SLOT_TYPES))
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2019 Mikael Arguedas.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import importlib
16+
from typing import Any
17+
18+
from rosidl_parser.definition import NamespacedType
19+
20+
21+
def import_message_from_namespaced_type(message_type: NamespacedType) -> Any:
22+
module = importlib.import_module(
23+
'.'.join(message_type.value_type.namespaces))
24+
return getattr(module, message_type.value_type.name)

rosidl_runtime_py/rosidl_runtime_py/set_message.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
from typing import Any
1616
from typing import Dict
1717

18+
from rosidl_parser.definition import AbstractNestedType
19+
from rosidl_parser.definition import NamespacedType
20+
from rosidl_runtime_py.convert import get_message_slot_types
21+
from rosidl_runtime_py.import_message import import_message_from_namespaced_type
22+
1823

1924
def set_message_fields(msg: Any, values: Dict[str, str]) -> None:
2025
"""
@@ -33,4 +38,13 @@ def set_message_fields(msg: Any, values: Dict[str, str]) -> None:
3338
except TypeError:
3439
value = field_type()
3540
set_message_fields(value, field_value)
41+
rosidl_type = get_message_slot_types(msg)[field_name]
42+
# Check if field is an array of ROS messages
43+
if isinstance(rosidl_type, AbstractNestedType):
44+
if isinstance(rosidl_type.value_type, NamespacedType):
45+
field_elem_type = import_message_from_namespaced_type(rosidl_type)
46+
for n in range(len(value)):
47+
submsg = field_elem_type()
48+
set_message_fields(submsg, value[n])
49+
value[n] = submsg
3650
setattr(msg, field_name, value)

rosidl_runtime_py/test/rosidl_runtime_py/test_set_message.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,39 @@ def test_set_message_fields_invalid():
9191
invalid_type['int32_value'] = 'this is not an integer'
9292
with pytest.raises(ValueError):
9393
set_message_fields(msg, invalid_type)
94+
95+
96+
def test_set_nested_namespaced_fields():
97+
unbounded_sequence_msg = message_fixtures.get_msg_unbounded_sequences()[1]
98+
test_values = {
99+
'basic_types_values': [
100+
{'float64_value': 42.42, 'int8_value': 42},
101+
{'float64_value': 11.11, 'int8_value': 11}
102+
]
103+
}
104+
set_message_fields(unbounded_sequence_msg, test_values)
105+
assert unbounded_sequence_msg.basic_types_values[0].float64_value == 42.42
106+
assert unbounded_sequence_msg.basic_types_values[0].int8_value == 42
107+
assert unbounded_sequence_msg.basic_types_values[0].uint8_value == 0
108+
assert unbounded_sequence_msg.basic_types_values[1].float64_value == 11.11
109+
assert unbounded_sequence_msg.basic_types_values[1].int8_value == 11
110+
assert unbounded_sequence_msg.basic_types_values[1].uint8_value == 0
111+
112+
arrays_msg = message_fixtures.get_msg_arrays()[0]
113+
test_values = {
114+
'basic_types_values': [
115+
{'float64_value': 42.42, 'int8_value': 42},
116+
{'float64_value': 11.11, 'int8_value': 11},
117+
{'float64_value': 22.22, 'int8_value': 22},
118+
]
119+
}
120+
set_message_fields(arrays_msg, test_values)
121+
assert arrays_msg.basic_types_values[0].float64_value == 42.42
122+
assert arrays_msg.basic_types_values[0].int8_value == 42
123+
assert arrays_msg.basic_types_values[0].uint8_value == 0
124+
assert arrays_msg.basic_types_values[1].float64_value == 11.11
125+
assert arrays_msg.basic_types_values[1].int8_value == 11
126+
assert arrays_msg.basic_types_values[1].uint8_value == 0
127+
assert arrays_msg.basic_types_values[2].float64_value == 22.22
128+
assert arrays_msg.basic_types_values[2].int8_value == 22
129+
assert arrays_msg.basic_types_values[2].uint8_value == 0

0 commit comments

Comments
 (0)