Exemple #1
0
def test_unallowed_yaml_types_in_substitutions():
    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': TextSubstitution(text="{'asd': 3}")}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Allowed value types' in str(exc.value)
    assert 'dict' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': TextSubstitution(text='[1, 2.0, 3]')}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty sequence' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': TextSubstitution(text='[[2, 3], [2, 3], [2, 3]]')}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty sequence' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': TextSubstitution(text='[]')}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty sequence' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{
            'foo': 1,
            'fiz': [
                [TextSubstitution(text="['asd', 'bsd']")],
                [TextSubstitution(text="['asd', 'csd']")]
            ]
        }]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty sequence' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': TextSubstitution(text='Text That : Cannot Be Parsed As : Yaml')}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Unable to parse' in str(exc.value)
Exemple #2
0
def test_nested_dictionaries():
    orig = [{'foo': {'bar': 'baz'}, 'fiz': {'buz': 3}}]
    norm = normalize_parameters(orig)
    expected = ({'foo.bar': 'baz', 'fiz.buz': 3},)
    assert evaluate_parameters(LaunchContext(), norm) == expected
Exemple #3
0
def test_mixed_path_dicts():
    orig = ['/foo/bar', {'fiz': {'buz': 3}}, pathlib.Path('/tmp/baz')]
    norm = normalize_parameters(orig)
    expected = (pathlib.Path('/foo/bar'), {'fiz.buz': 3}, pathlib.Path('/tmp/baz'))
    assert evaluate_parameters(LaunchContext(), norm) == expected
Exemple #4
0
def test_dictionary_with_bytes():
    orig = [{'foo': 1, 'fiz': bytes([0xff, 0x5c, 0xaa])}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': bytes([0xff, 0x5c, 0xaa])},)
    assert evaluate_parameters(LaunchContext(), norm) == expected
Exemple #5
0
def test_dictionary_with_dissimilar_array():
    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': [True, 2.0, 3]}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': [True, 1, TextSubstitution(text='foo')]}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': [TextSubstitution(text='foo'), True, 1]}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': [True, 1, [TextSubstitution(text='foo')]]}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': 1, 'fiz': [[TextSubstitution(text='foo')], True, 1]}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty' in str(exc.value)

    with pytest.raises(TypeError) as exc:
        orig = [{'foo': [
            [TextSubstitution(text='True')],
            [TextSubstitution(text='2.0')],
            [TextSubstitution(text='3')],
        ]}]
        norm = normalize_parameters(orig)
        evaluate_parameters(LaunchContext(), norm)
    assert 'Expected a non-empty' in str(exc.value)
Exemple #6
0
def test_dictionary_with_float():
    orig = [{'foo': 1.2, 'fiz': [2.3, 3.4, 4.5]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1.2, 'fiz': (2.3, 3.4, 4.5)},)
    assert evaluate_parameters(LaunchContext(), norm) == expected
Exemple #7
0
def test_dictionary_with_int():
    orig = [{'foo': 1, 'fiz': [2, 3, 4]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': (2, 3, 4)},)
    assert evaluate_parameters(LaunchContext(), norm) == expected
Exemple #8
0
def test_dictionary_with_bool():
    orig = [{'foo': False, 'fiz': [True, False, True]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': False, 'fiz': (True, False, True)},)
    assert evaluate_parameters(LaunchContext(), norm) == expected
Exemple #9
0
 def _perform_substitutions(self, context: LaunchContext) -> None:
     try:
         if self.__substitutions_performed:
             # This function may have already been called by a subclass' `execute`, for example.
             return
         self.__substitutions_performed = True
         if self.__node_name is not None:
             self.__expanded_node_name = perform_substitutions(
                 context,
                 normalize_to_list_of_substitutions(self.__node_name))
             validate_node_name(self.__expanded_node_name)
         self.__expanded_node_name.lstrip('/')
         expanded_node_namespace = None
         if self.__node_namespace is not None:
             expanded_node_namespace = perform_substitutions(
                 context,
                 normalize_to_list_of_substitutions(self.__node_namespace))
         base_ns = context.launch_configurations.get('ros_namespace', None)
         if base_ns is not None or expanded_node_namespace is not None:
             if expanded_node_namespace is None:
                 expanded_node_namespace = ''
             if base_ns is None:
                 base_ns = ''
             if not expanded_node_namespace.startswith('/'):
                 expanded_node_namespace = (
                     base_ns + '/' + expanded_node_namespace).rstrip('/')
             if not expanded_node_namespace.startswith('/'):
                 expanded_node_namespace = '/' + expanded_node_namespace
             self.__expanded_node_namespace = expanded_node_namespace
         if expanded_node_namespace is not None:
             cmd_extension = [
                 '-r',
                 LocalSubstitution("ros_specific_arguments['ns']")
             ]
             self.cmd.extend([
                 normalize_to_list_of_substitutions(x)
                 for x in cmd_extension
             ])
             validate_namespace(self.__expanded_node_namespace)
     except Exception:
         self.__logger.error(
             "Error while expanding or validating node name or namespace for '{}':"
             .format(
                 'package={}, executable={}, name={}, namespace={}'.format(
                     self.__package,
                     self.__node_executable,
                     self.__node_name,
                     self.__node_namespace,
                 )))
         raise
     self.__final_node_name = ''
     if self.__expanded_node_namespace != '/':
         self.__final_node_name += self.__expanded_node_namespace
     self.__final_node_name += '/' + self.__expanded_node_name
     # expand global parameters first,
     # so they can be overriden with specific parameters of this Node
     global_params = context.launch_configurations.get('ros_params', None)
     if global_params is not None or self.__parameters is not None:
         self.__expanded_parameter_files = []
     if global_params is not None:
         param_file_path = self._create_params_file_from_dict(global_params)
         self.__expanded_parameter_files.append(param_file_path)
         cmd_extension = ['--params-file', f'{param_file_path}']
         self.cmd.extend(
             [normalize_to_list_of_substitutions(x) for x in cmd_extension])
         assert os.path.isfile(param_file_path)
     # expand parameters too
     if self.__parameters is not None:
         evaluated_parameters = evaluate_parameters(context,
                                                    self.__parameters)
         for i, params in enumerate(evaluated_parameters):
             if isinstance(params, dict):
                 param_file_path = self._create_params_file_from_dict(
                     params)
                 assert os.path.isfile(param_file_path)
             elif isinstance(params, pathlib.Path):
                 param_file_path = str(params)
             else:
                 raise RuntimeError(
                     'invalid normalized parameters {}'.format(
                         repr(params)))
             if not os.path.isfile(param_file_path):
                 self.__logger.warning(
                     'Parameter file path is not a file: {}'.format(
                         param_file_path), )
                 continue
             self.__expanded_parameter_files.append(param_file_path)
             cmd_extension = ['--params-file', f'{param_file_path}']
             self.cmd.extend([
                 normalize_to_list_of_substitutions(x)
                 for x in cmd_extension
             ])
     # expand remappings too
     if self.__remappings is not None:
         self.__expanded_remappings = []
         for k, v in self.__remappings:
             key = perform_substitutions(
                 context, normalize_to_list_of_substitutions(k))
             value = perform_substitutions(
                 context, normalize_to_list_of_substitutions(v))
             self.__expanded_remappings.append((key, value))
Exemple #10
0
    def _perform_substitutions(self, context: LaunchContext) -> None:
        # Here to avoid cyclic import
        from ..descriptions import Parameter
        try:
            if self.__substitutions_performed:
                # This function may have already been called by a subclass' `execute`, for example.
                return
            self.__substitutions_performed = True
            if self.__node_name is not None:
                self.__expanded_node_name = perform_substitutions(
                    context, normalize_to_list_of_substitutions(self.__node_name))
                validate_node_name(self.__expanded_node_name)
            self.__expanded_node_name.lstrip('/')
            expanded_node_namespace: Optional[Text] = None
            if self.__node_namespace is not None:
                expanded_node_namespace = perform_substitutions(
                    context, normalize_to_list_of_substitutions(self.__node_namespace))
            base_ns = context.launch_configurations.get('ros_namespace', None)
            expanded_node_namespace = make_namespace_absolute(
                prefix_namespace(base_ns, expanded_node_namespace))
            if expanded_node_namespace is not None:
                self.__expanded_node_namespace = expanded_node_namespace
                cmd_extension = ['-r', LocalSubstitution("ros_specific_arguments['ns']")]
                self.cmd.extend([normalize_to_list_of_substitutions(x) for x in cmd_extension])
                validate_namespace(self.__expanded_node_namespace)
        except Exception:
            self.__logger.error(
                "Error while expanding or validating node name or namespace for '{}':"
                .format('package={}, executable={}, name={}, namespace={}'.format(
                    self.__package,
                    self.__node_executable,
                    self.__node_name,
                    self.__node_namespace,
                ))
            )
            raise
        self.__final_node_name = prefix_namespace(
            self.__expanded_node_namespace, self.__expanded_node_name)

        # Expand global parameters first,
        # so they can be overridden with specific parameters of this Node
        # The params_container list is expected to contain name-value pairs (tuples)
        # and/or strings representing paths to parameter files.
        params_container = context.launch_configurations.get('global_params', None)

        if any(x is not None for x in (params_container, self.__parameters)):
            self.__expanded_parameter_arguments = []
        if params_container is not None:
            for param in params_container:
                if isinstance(param, tuple):
                    name, value = param
                    cmd_extension = ['-p', f'{name}:={value}']
                    self.cmd.extend([normalize_to_list_of_substitutions(x) for x in cmd_extension])
                else:
                    param_file_path = os.path.abspath(param)
                    self.__expanded_parameter_arguments.append((param_file_path, True))
                    cmd_extension = ['--params-file', f'{param_file_path}']
                    assert os.path.isfile(param_file_path)
                    self.cmd.extend([normalize_to_list_of_substitutions(x) for x in cmd_extension])

        # expand parameters too
        if self.__parameters is not None:
            evaluated_parameters = evaluate_parameters(context, self.__parameters)
            for params in evaluated_parameters:
                is_file = False
                if isinstance(params, dict):
                    param_argument = self._create_params_file_from_dict(params)
                    is_file = True
                    assert os.path.isfile(param_argument)
                elif isinstance(params, pathlib.Path):
                    param_argument = str(params)
                    is_file = True
                elif isinstance(params, Parameter):
                    param_argument = self._get_parameter_rule(params, context)
                else:
                    raise RuntimeError('invalid normalized parameters {}'.format(repr(params)))
                if is_file and not os.path.isfile(param_argument):
                    self.__logger.warning(
                        'Parameter file path is not a file: {}'.format(param_argument),
                    )
                    continue
                self.__expanded_parameter_arguments.append((param_argument, is_file))
                cmd_extension = ['--params-file' if is_file else '-p', f'{param_argument}']
                self.cmd.extend([normalize_to_list_of_substitutions(x) for x in cmd_extension])
        # expand remappings too
        global_remaps = context.launch_configurations.get('ros_remaps', None)
        if global_remaps or self.__remappings:
            self.__expanded_remappings = []
        if global_remaps:
            self.__expanded_remappings.extend(global_remaps)
        if self.__remappings:
            self.__expanded_remappings.extend([
                (perform_substitutions(context, src), perform_substitutions(context, dst))
                for src, dst in self.__remappings
            ])
        if self.__expanded_remappings:
            cmd_extension = []
            for src, dst in self.__expanded_remappings:
                cmd_extension.extend(['-r', f'{src}:={dst}'])
            self.cmd.extend([normalize_to_list_of_substitutions(x) for x in cmd_extension])
Exemple #11
0
 def _perform_substitutions(self, context: LaunchContext) -> None:
     try:
         if self.__substitutions_performed:
             # This function may have already been called by a subclass' `execute`, for example.
             return
         self.__substitutions_performed = True
         if self.__node_name is not None:
             self.__expanded_node_name = perform_substitutions(
                 context,
                 normalize_to_list_of_substitutions(self.__node_name))
             validate_node_name(self.__expanded_node_name)
         self.__expanded_node_name.lstrip('/')
         self.__expanded_node_namespace = perform_substitutions(
             context,
             normalize_to_list_of_substitutions(self.__node_namespace))
         if not self.__expanded_node_namespace.startswith('/'):
             base_ns = context.launch_configurations.get(
                 'ros_namespace', '')
             self.__expanded_node_namespace = (
                 base_ns + '/' + self.__expanded_node_namespace).rstrip('/')
             if (self.__expanded_node_namespace != '' and
                     not self.__expanded_node_namespace.startswith('/')):
                 self.__expanded_node_namespace = '/' + self.__expanded_node_namespace
         if self.__expanded_node_namespace != '':
             cmd_extension = [
                 '-r',
                 LocalSubstitution("ros_specific_arguments['ns']")
             ]
             self.cmd.extend([
                 normalize_to_list_of_substitutions(x)
                 for x in cmd_extension
             ])
             validate_namespace(self.__expanded_node_namespace)
     except Exception:
         self.__logger.error(
             "Error while expanding or validating node name or namespace for '{}':"
             .format(
                 'package={}, node_executable={}, name={}, namespace={}'.
                 format(
                     self.__package,
                     self.__node_executable,
                     self.__node_name,
                     self.__node_namespace,
                 )))
         raise
     self.__final_node_name = ''
     if self.__expanded_node_namespace not in ['', '/']:
         self.__final_node_name += self.__expanded_node_namespace
     self.__final_node_name += '/' + self.__expanded_node_name
     # expand parameters too
     if self.__parameters is not None:
         self.__expanded_parameter_files = []
         evaluated_parameters = evaluate_parameters(context,
                                                    self.__parameters)
         for params in evaluated_parameters:
             if isinstance(params, dict):
                 param_file_path = self._create_params_file_from_dict(
                     params)
             elif isinstance(params, pathlib.Path):
                 param_file_path = str(params)
             else:
                 raise RuntimeError(
                     'invalid normalized parameters {}'.format(
                         repr(params)))
             if not os.path.isfile(param_file_path):
                 self.__logger.warning(
                     'Parameter file path is not a file: {}'.format(
                         param_file_path), )
                 # Don't skip adding the file to the parameter list since space has been
                 # reserved for it in the ros_specific_arguments.
             self.__expanded_parameter_files.append(param_file_path)
     # expand remappings too
     if self.__remappings is not None:
         self.__expanded_remappings = []
         for k, v in self.__remappings:
             key = perform_substitutions(
                 context, normalize_to_list_of_substitutions(k))
             value = perform_substitutions(
                 context, normalize_to_list_of_substitutions(v))
             self.__expanded_remappings.append((key, value))
Exemple #12
0
def check_launch_node(file):
    root_entity, parser = Parser.load(file)
    ld = parser.parse_description(root_entity)
    ls = LaunchService()
    ls.include_launch_description(ld)
    assert (0 == ls.run())
    evaluated_parameters = evaluate_parameters(
        ls.context,
        ld.describe_sub_entities()[3]._Node__parameters)
    assert len(evaluated_parameters) == 3
    assert isinstance(evaluated_parameters[0], dict)
    assert isinstance(evaluated_parameters[1], dict)
    assert isinstance(evaluated_parameters[2], pathlib.Path)

    assert 'param1' in evaluated_parameters[0]
    assert evaluated_parameters[0]['param1'] == 'ads'

    param_dict = evaluated_parameters[1]
    assert 'param_group1.param_group2.param2' in param_dict
    assert 'param_group1.param3' in param_dict
    assert 'param_group1.param4' in param_dict
    assert 'param_group1.param5' in param_dict
    assert 'param_group1.param6' in param_dict
    assert 'param_group1.param7' in param_dict
    assert 'param_group1.param8' in param_dict
    assert 'param_group1.param9' in param_dict
    assert 'param_group1.param10' in param_dict
    assert 'param_group1.param11' in param_dict
    assert 'param_group1.param12' in param_dict
    assert 'param_group1.param13' in param_dict
    assert 'param_group1.param14' in param_dict
    assert 'param_group1.param15' in param_dict
    assert param_dict['param_group1.param_group2.param2'] == 2
    assert param_dict['param_group1.param3'] == [2, 5, 8]
    assert param_dict['param_group1.param4'] == [2, 5, 8]
    assert param_dict['param_group1.param5'] == '[2, 5, 8]'
    assert param_dict['param_group1.param6'] == [2., 5., 8.]
    assert param_dict['param_group1.param7'] == ['2', '5', '8']
    assert param_dict['param_group1.param8'] == ["'2'", "'5'", "'8'"]
    assert param_dict['param_group1.param9'] == ["'2'", "'5'", "'8'"]
    assert param_dict['param_group1.param10'] == ["'asd'", "'bsd'", "'csd'"]
    assert param_dict['param_group1.param11'] == ['asd', 'bsd', 'csd']
    assert param_dict['param_group1.param12'] == ''
    assert param_dict['param_group1.param13'] == '100'
    assert param_dict['param_group1.param14'] == ["'2'", "'5'", "'8'"]
    assert param_dict['param_group1.param15'] == ['2', '5', '8']

    # Check remappings exist
    remappings = ld.describe_sub_entities()[3]._Node__remappings
    assert remappings is not None
    assert len(remappings) == 2

    talker_node_action = ld.describe_sub_entities()[3]
    talker_node_cmd_string = ' '.join(
        talker_node_action.process_details['cmd'])
    assert '--ros-args --log-level info' in talker_node_cmd_string

    listener_node_action = ld.describe_sub_entities()[4]
    listener_node_cmd = listener_node_action.process_details['cmd']
    assert [sys.executable, '-c',
            'import sys; print(sys.argv[1:])'] == listener_node_cmd[:3]