Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hides the assertions that checks the data types of the message fields. #194

Merged
merged 14 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions rosidl_generator_py/resource/_idl.py.em
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# generated from rosidl_generator_py/resource/_idl.py.em
# with input from @(package_name):@(interface_path)
# generated code does not contain a copyright notice

# This is being done at the module level and not on the instance level to avoid looking
# for the same variable multiple times on each instance. This variable is not supposed to
# change during runtime so it makes sense to only look for it once.
from os import getenv

ros_python_check_fields = getenv('ROS_PYTHON_CHECK_FIELDS', default='')
@
@#######################################################################
@# EmPy template for generating _<idl>.py files
Expand Down
51 changes: 30 additions & 21 deletions rosidl_generator_py/resource/_msg.py.em
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class @(message.structure.namespaced_type.name)(metaclass=Metaclass_@(message.st
@[ end if]@
'_@(member.name)',
@[end for]@
'_check_fields',
]

_fields_and_field_types = {
Expand Down Expand Up @@ -254,6 +255,8 @@ string@
@[end for]@
}

# This attribute is used to store an rosidl_parser.definition variable
# related to the data type of each of the components the message.
SLOT_TYPES = (
@[for member in message.structure.members]@
@[ if len(message.structure.members) == 1 and member.name == EMPTY_STRUCTURE_REQUIRED_MEMBER_NAME]@
Expand Down Expand Up @@ -291,9 +294,14 @@ if isinstance(type_, AbstractNestedType):
)

def __init__(self, **kwargs):
assert all('_' + key in self.__slots__ for key in kwargs.keys()), \
'Invalid arguments passed to constructor: %s' % \
', '.join(sorted(k for k in kwargs.keys() if '_' + k not in self.__slots__))
if 'check_fields' in kwargs:
self._check_fields = kwargs['check_fields']
else:
self._check_fields = ros_python_check_fields == '1'
if self._check_fields:
assert all('_' + key in self.__slots__ for key in kwargs.keys()), \
'Invalid arguments passed to constructor: %s' % \
', '.join(sorted(k for k in kwargs.keys() if '_' + k not in self.__slots__))
@[for member in message.structure.members]@
@[ if len(message.structure.members) == 1 and member.name == EMPTY_STRUCTURE_REQUIRED_MEMBER_NAME]@
@[ continue]@
Expand Down Expand Up @@ -364,7 +372,7 @@ if isinstance(type_, AbstractNestedType):
typename.pop()
typename.append(self.__class__.__name__)
args = []
for s, t in zip(self.__slots__, self.SLOT_TYPES):
for s, t in zip(self.get_fields_and_field_types().keys(), self.SLOT_TYPES):
field = getattr(self, s)
fieldstr = repr(field)
# We use Python array type for fields that can be directly stored
Expand All @@ -378,11 +386,12 @@ if isinstance(type_, AbstractNestedType):
if len(field) == 0:
fieldstr = '[]'
else:
assert fieldstr.startswith('array(')
if self._check_fields:
assert fieldstr.startswith('array(')
prefix = "array('X', "
suffix = ')'
fieldstr = fieldstr[len(prefix):-len(suffix)]
args.append(s[1:] + '=' + fieldstr)
args.append(s + '=' + fieldstr)
return '%s(%s)' % ('.'.join(typename), ', '.join(args))

def __eq__(self, other):
Expand Down Expand Up @@ -428,28 +437,28 @@ if member.name in dict(inspect.getmembers(builtins)).keys():

@@@(member.name).setter@(noqa_string)
def @(member.name)(self, value):@(noqa_string)
if self._check_fields:
@[ if isinstance(member.type, AbstractNestedType) and isinstance(member.type.value_type, BasicType) and member.type.value_type.typename in SPECIAL_NESTED_BASIC_TYPES]@
@[ if isinstance(member.type, Array)]@
if isinstance(value, numpy.ndarray):
assert value.dtype == @(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['dtype']), \
"The '@(member.name)' numpy.ndarray() must have the dtype of '@(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['dtype'])'"
assert value.size == @(member.type.size), \
"The '@(member.name)' numpy.ndarray() must have a size of @(member.type.size)"
self._@(member.name) = value
return
if isinstance(value, numpy.ndarray):
assert value.dtype == @(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['dtype']), \
"The '@(member.name)' numpy.ndarray() must have the dtype of '@(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['dtype'])'"
assert value.size == @(member.type.size), \
"The '@(member.name)' numpy.ndarray() must have a size of @(member.type.size)"
self._@(member.name) = value
return
@[ elif isinstance(member.type, AbstractSequence)]@
if isinstance(value, array.array):
assert value.typecode == '@(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['type_code'])', \
"The '@(member.name)' array.array() must have the type code of '@(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['type_code'])'"
if isinstance(value, array.array):
assert value.typecode == '@(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['type_code'])', \
"The '@(member.name)' array.array() must have the type code of '@(SPECIAL_NESTED_BASIC_TYPES[member.type.value_type.typename]['type_code'])'"
@[ if isinstance(member.type, BoundedSequence)]@
assert len(value) <= @(member.type.maximum_size), \
"The '@(member.name)' array.array() must have a size <= @(member.type.maximum_size)"
assert len(value) <= @(member.type.maximum_size), \
"The '@(member.name)' array.array() must have a size <= @(member.type.maximum_size)"
@[ end if]@
self._@(member.name) = value
return
self._@(member.name) = value
return
@[ end if]@
@[ end if]@
if __debug__:
@[ if isinstance(type_, NamespacedType)]@
@[ if (
type_.name.endswith(ACTION_GOAL_SUFFIX) or
Expand Down
47 changes: 21 additions & 26 deletions rosidl_generator_py/test/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@


def test_basic_types():
msg = BasicTypes()
msg = BasicTypes(check_fields=True)

# types
assert isinstance(msg.bool_value, bool)
Expand Down Expand Up @@ -149,7 +149,7 @@ def test_basic_types():


def test_strings():
msg = Strings()
msg = Strings(check_fields=True)

# types
assert isinstance(msg.string_value, str)
Expand Down Expand Up @@ -203,7 +203,7 @@ def test_strings():


def test_wstrings():
msg = WStrings()
msg = WStrings(check_fields=True)

# types
assert isinstance(msg.wstring_value, str)
Expand All @@ -217,7 +217,7 @@ def test_wstrings():


def test_arrays_of_bounded_strings():
msg = StringArrays()
msg = StringArrays(check_fields=True)
array_valid_string_length = ['a' * 2, 'b' * 3, 'c' * 4]
array_too_long_strings = ['a' * 2, 'b' * 3, 'c' * 6]
assert ['', '', ''] == msg.ub_string_static_array_value
Expand Down Expand Up @@ -260,12 +260,12 @@ def test_arrays_of_bounded_strings():


def test_constructor():
msg = Strings(string_value='foo')
msg = Strings(string_value='foo', check_fields=True)

assert'foo' == msg.string_value

with pytest.raises(AssertionError):
Strings(unknown_field='test')
Strings(unknown_field='test', check_fields=True)


def test_constants():
Expand All @@ -289,7 +289,7 @@ def test_constants():


def test_default_values():
msg = Defaults()
msg = Defaults(check_fields=True)

assert msg.bool_value is True
assert bytes([50]) == msg.byte_value
Expand All @@ -316,7 +316,7 @@ def test_default_values():


def test_arrays():
msg = Arrays()
msg = Arrays(check_fields=True)

# types
assert isinstance(msg.bool_values, list)
Expand Down Expand Up @@ -525,7 +525,7 @@ def test_arrays():


def test_bounded_sequences():
msg = BoundedSequences()
msg = BoundedSequences(check_fields=True)

# types
assert isinstance(msg.bool_values, list)
Expand Down Expand Up @@ -749,7 +749,7 @@ def test_bounded_sequences():


def test_unbounded_sequences():
msg = UnboundedSequences()
msg = UnboundedSequences(check_fields=True)

# types
assert isinstance(msg.byte_values, list)
Expand Down Expand Up @@ -898,12 +898,10 @@ def test_unbounded_sequences():


def test_slot_attributes():
msg = Nested()
msg = Nested(check_fields=True)
assert hasattr(msg, 'get_fields_and_field_types')
assert hasattr(msg, '__slots__')
nested_slot_types_dict = getattr(msg, 'get_fields_and_field_types')()
nested_slots = getattr(msg, '__slots__')
assert len(nested_slot_types_dict) == len(nested_slots)
expected_nested_slot_types_dict = {
'basic_types_value': 'rosidl_generator_py/BasicTypes',
}
Expand All @@ -915,12 +913,10 @@ def test_slot_attributes():


def test_string_slot_attributes():
msg = StringArrays()
msg = StringArrays(check_fields=True)
assert hasattr(msg, 'get_fields_and_field_types')
assert hasattr(msg, '__slots__')
string_slot_types_dict = getattr(msg, 'get_fields_and_field_types')()
string_slots = getattr(msg, '__slots__')
assert len(string_slot_types_dict) == len(string_slots)
expected_string_slot_types_dict = {
'ub_string_static_array_value': 'string<5>[3]',
'ub_string_ub_array_value': 'sequence<string<5>, 10>',
Expand All @@ -943,7 +939,7 @@ def test_string_slot_attributes():


def test_modifying_slot_fields_and_types():
msg = StringArrays()
msg = StringArrays(check_fields=True)
assert hasattr(msg, 'get_fields_and_field_types')
string_slot_types_dict = getattr(msg, 'get_fields_and_field_types')()
string_slot_types_dict_len = len(string_slot_types_dict)
Expand All @@ -952,24 +948,20 @@ def test_modifying_slot_fields_and_types():


def test_slot_types():
msg = Nested()
msg = Nested(check_fields=True)
assert hasattr(msg, 'SLOT_TYPES')
assert hasattr(msg, '__slots__')
nested_slot_types = Nested.SLOT_TYPES
nested_slots = getattr(msg, '__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 == 'BasicTypes'


def test_string_slot_types():
msg = StringArrays()
msg = StringArrays(check_fields=True)
assert hasattr(msg, 'SLOT_TYPES')
assert hasattr(msg, '__slots__')
string_slot_types = StringArrays.SLOT_TYPES
string_slots = getattr(msg, '__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)
Expand All @@ -994,9 +986,12 @@ def test_string_slot_types():


def test_builtin_sequence_slot_attributes():
msg = BuiltinTypeSequencesIdl()
msg = BuiltinTypeSequencesIdl(check_fields=True)
assert hasattr(msg, 'get_fields_and_field_types')
assert hasattr(msg, '__slots__')
builtin_sequence_slot_types_dict = getattr(msg, 'get_fields_and_field_types')()
builtin_sequence_slots = getattr(msg, '__slots__')
assert len(builtin_sequence_slot_types_dict) == len(builtin_sequence_slots)
expected_builtin_sequence_slot_types_dict = {
'char_sequence_unbounded': 'sequence<char>',
}

assert len(builtin_sequence_slot_types_dict) == len(expected_builtin_sequence_slot_types_dict)