diff --git a/launch_ros/launch_ros/utilities/to_parameters_list.py b/launch_ros/launch_ros/utilities/to_parameters_list.py index 6aad2032..909d1c6a 100644 --- a/launch_ros/launch_ros/utilities/to_parameters_list.py +++ b/launch_ros/launch_ros/utilities/to_parameters_list.py @@ -15,6 +15,7 @@ """Module with utility to transform evaluated parameters into parameter lists.""" import pathlib +import re from typing import List import warnings @@ -74,10 +75,13 @@ def to_parameters_list( :returns: a list of parameters """ parameters = [] # type: List[rclpy.parameter.Parameter] - node_name = node_name.lstrip('/') + + if node_name[0] != '/': + node_name = '/' + node_name + if namespace and namespace != '/': namespace = namespace.strip('/') - node_name = f'{namespace}/{node_name}' + node_name = f'/{namespace}{node_name}' params_set = {} warned_once = False @@ -89,10 +93,18 @@ def to_parameters_list( if normalized_param_dict: param_dict.clear() - if '**' in normalized_param_dict: - param_dict = normalized_param_dict['**'] - if node_name in normalized_param_dict: - param_dict.update(normalized_param_dict[node_name]) + + for key, value in normalized_param_dict.items(): + pattern = key if key[0] == '/' else '/' + key + pattern = pattern.replace('/*', '(/\\w+)') + try: + match = re.fullmatch(pattern, node_name) + if match: + param_dict.update(value) + except re.error as e: + raise RuntimeError( + 'invalid yaml file {}, error: {}'.format(str(params_set_or_path), e)) + if not warned_once and not node_name: warnings.warn( 'node name not provided to launch; parameter files will not apply', diff --git a/test_launch_ros/test/test_launch_ros/actions/example_parameters_bad_wildcard.yaml b/test_launch_ros/test/test_launch_ros/actions/example_parameters_bad_wildcard.yaml new file mode 100644 index 00000000..d1e5b6c9 --- /dev/null +++ b/test_launch_ros/test/test_launch_ros/actions/example_parameters_bad_wildcard.yaml @@ -0,0 +1,3 @@ +/***: + ros__parameters: + param: "wildcard" diff --git a/test_launch_ros/test/test_launch_ros/actions/example_parameters_wildcard_mixed.yaml b/test_launch_ros/test/test_launch_ros/actions/example_parameters_wildcard_mixed.yaml new file mode 100644 index 00000000..dc7f6d17 --- /dev/null +++ b/test_launch_ros/test/test_launch_ros/actions/example_parameters_wildcard_mixed.yaml @@ -0,0 +1,3 @@ +/*/aa/**/my_node: + ros__parameters: + param: "wildcard" diff --git a/test_launch_ros/test/test_launch_ros/actions/test_load_composable_nodes.py b/test_launch_ros/test/test_launch_ros/actions/test_load_composable_nodes.py index c7b7f4df..c5ebbffa 100644 --- a/test_launch_ros/test/test_launch_ros/actions/test_load_composable_nodes.py +++ b/test_launch_ros/test/test_launch_ros/actions/test_load_composable_nodes.py @@ -396,12 +396,12 @@ def test_load_node_with_param_file(mock_component_container): assert request.node_name == 'node_1' assert request.node_namespace == '/ns_1' assert len(request.parameters) == 3 - assert request.parameters[0].name == 'param_2' - assert request.parameters[0].value.integer_value == 2 - assert request.parameters[1].name == 'param_3' - assert request.parameters[1].value.integer_value == 33 - assert request.parameters[2].name == 'param_1' - assert request.parameters[2].value.integer_value == 1 + assert request.parameters[0].name == 'param_1' + assert request.parameters[0].value.integer_value == 1 + assert request.parameters[1].name == 'param_2' + assert request.parameters[1].value.integer_value == 22 + assert request.parameters[2].name == 'param_3' + assert request.parameters[2].value.integer_value == 33 request = mock_component_container.requests[-1] assert get_node_name_count(context, '/ns_2/node_2') == 1 @@ -477,6 +477,26 @@ def test_load_node_with_param_file(mock_component_container): assert request.node_namespace == '/ns' assert len(request.parameters) == 0 + # Case 9: wildcard mixed + context = _assert_launch_no_errors([ + _load_composable_node( + package='foo_package', + plugin='bar_plugin', + name='my_node', + namespace='/wildcard_ns/aa/extra1/extra2', + parameters=[ + parameters_file_dir / 'example_parameters_wildcard_mixed.yaml' + ], + ) + ]) + request = mock_component_container.requests[-1] + assert get_node_name_count(context, '/wildcard_ns/aa/extra1/extra2/my_node') == 1 + assert request.node_name == 'my_node' + assert request.node_namespace == '/wildcard_ns/aa/extra1/extra2' + assert len(request.parameters) == 1 + assert request.parameters[0].name == 'param' + assert request.parameters[0].value.string_value == 'wildcard' + # Namespace not found context = _assert_launch_no_errors([ _load_composable_node( diff --git a/test_launch_ros/test/test_launch_ros/actions/test_set_parameters_from_file.py b/test_launch_ros/test/test_launch_ros/actions/test_set_parameters_from_file.py index acb3a5f2..577459d4 100644 --- a/test_launch_ros/test/test_launch_ros/actions/test_set_parameters_from_file.py +++ b/test_launch_ros/test/test_launch_ros/actions/test_set_parameters_from_file.py @@ -27,6 +27,7 @@ from launch_ros.actions.load_composable_nodes import get_composable_node_load_request from launch_ros.descriptions import ComposableNode +import pytest import yaml @@ -176,3 +177,19 @@ def test_set_param_with_composable_node(): assert parameters[0].value.integer_value == 10 assert parameters[1].name == 'asd' assert parameters[1].value.string_value == 'bsd' + + +def test_set_bad_wildcard_param_with_composable_node(): + lc = MockContext() + node_description = ComposableNode( + package='asd', + plugin='my_plugin', + name='my_node', + namespace='my_ns' + ) + param_file_path = \ + os.path.dirname(os.path.abspath(__file__)) + '/example_parameters_bad_wildcard.yaml' + set_param_1 = SetParametersFromFile(param_file_path) + set_param_1.execute(lc) + with pytest.raises(RuntimeError): + get_composable_node_load_request(node_description, lc)