def test_dictionary_with_str(): orig = [{'foo': 'bar', 'fiz': ['b', 'u', 'z']}] norm = normalize_parameters(orig) expected = ({'foo': 'bar', 'fiz': ('b', 'u', 'z')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 'False', 'fiz': ['True', 'False', 'True']}] norm = normalize_parameters(orig) expected = ({'foo': 'False', 'fiz': ('True', 'False', 'True')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected 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 orig = [{'foo': '1', 'fiz': ['2', '3', '4']}] norm = normalize_parameters(orig) expected = ({'foo': '1', 'fiz': ('2', '3', '4')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'strs': ['True', '2.0', '3']}] norm = normalize_parameters(orig) expected = ({'strs': ('True', '2.0', '3')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
def test_dictionary_with_dissimilar_array(): orig = [{'foo': 1, 'fiz': [True, 2.0, 3]}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'fiz': ('True', '2.0', '3')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 1, 'fiz': [True, 1, TextSubstitution(text='foo')]}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'fiz': ('True', '1', 'foo')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 1, 'fiz': [TextSubstitution(text='foo'), True, 1]}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'fiz': ('foo', 'True', '1')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 1, 'fiz': [True, 1, [TextSubstitution(text='foo')]]}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'fiz': ('True', '1', 'foo')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 1, 'fiz': [[TextSubstitution(text='foo')], True, 1]}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'fiz': ('foo', 'True', '1')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
def test_dictionary_with_substitution(): orig = [{TextSubstitution(text='bar'): TextSubstitution(text='baz')}] norm = normalize_parameters(orig) expected = ({'bar': 'baz'}, ) assert evaluate_parameters(LaunchContext(), norm) == expected # substitutions get yamlfied orig = [{TextSubstitution(text='false'): TextSubstitution(text='off')}] norm = normalize_parameters(orig) expected = ({'false': False}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
def test_dictionary_with_substitution_list_value(): orig = [{ 'foo': [TextSubstitution(text='fiz'), TextSubstitution(text='buz')] }] norm = normalize_parameters(orig) expected = ({'foo': 'fizbuz'}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{ 'foo': [[TextSubstitution(text='fiz')], [TextSubstitution(text='buz')]] }] norm = normalize_parameters(orig) expected = ({'foo': ('fiz', 'buz')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
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
def test_dictionary_with_int_and_float(): orig = [{'foo': 1, 'fiz': [2, 3.1, 4]}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'fiz': (2.0, 3.1, 4.0)}, ) evaluated = evaluate_parameters(LaunchContext(), norm) assert evaluated == expected # pytest doesn't check int vs float type assert tuple(map(type, evaluated[0]['fiz'])) == (float, float, float) orig = [{'foo': 1, 'fiz': [2.0, 3, 4]}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'fiz': (2.0, 3.0, 4.0)}, ) evaluated = evaluate_parameters(LaunchContext(), norm) assert evaluated == expected # pytest doesn't check int vs float type assert tuple(map(type, evaluated[0]['fiz'])) == (float, float, float)
def __init__( self, *, package: SomeSubstitutionsType, node_plugin: SomeSubstitutionsType, node_name: Optional[SomeSubstitutionsType] = None, node_namespace: Optional[SomeSubstitutionsType] = None, parameters: Optional[SomeParameters] = None, remappings: Optional[SomeRemapRules] = None, extra_arguments: Optional[SomeParameters] = None, ) -> None: """ Initialize a ComposableNode description. :param package: name of the ROS package the node plugin lives in :param node_plugin: name of the plugin to be loaded :param node_name: name the node should have :param node_namespace: namespace the node should create topics/services/etc in :param parameters: list of either paths to yaml files or dictionaries of parameters :param remappings: list of from/to pairs for remapping names :param extra_arguments: container specific arguments to be passed to the loaded node """ self.__package = normalize_to_list_of_substitutions(package) self.__node_plugin = normalize_to_list_of_substitutions(node_plugin) self.__node_name = None # type: Optional[List[Substitution]] if node_name is not None: self.__node_name = normalize_to_list_of_substitutions(node_name) self.__node_namespace = None # type: Optional[List[Substitution]] if node_namespace is not None: self.__node_namespace = normalize_to_list_of_substitutions( node_namespace) self.__parameters = None # type: Optional[Parameters] if parameters is not None: self.__parameters = normalize_parameters(parameters) self.__remappings = None # type: Optional[RemapRules] if remappings: self.__remappings = normalize_remap_rules(remappings) self.__extra_arguments = None # type: Optional[Parameters] if extra_arguments: self.__extra_arguments = normalize_parameters(extra_arguments)
def test_dictionary_with_mixed_substitutions_and_strings(): orig = [{'foo': [TextSubstitution(text='fiz'), 'bar']}] norm = normalize_parameters(orig) expected = ({'foo': 'fizbar'}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': [[TextSubstitution(text='fiz')], 'bar']}] norm = normalize_parameters(orig) expected = ({'foo': ('fiz', 'bar')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': ['bar', TextSubstitution(text='fiz')]}] norm = normalize_parameters(orig) expected = ({'foo': 'barfiz'}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': ['bar', [TextSubstitution(text='fiz')]]}] norm = normalize_parameters(orig) expected = ({'foo': ('bar', 'fiz')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
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)
def __init__( self, *, package: SomeSubstitutionsType, node_executable: SomeSubstitutionsType, node_name: Optional[SomeSubstitutionsType] = None, node_namespace: SomeSubstitutionsType = '', parameters: Optional[SomeParameters] = None, remappings: Optional[SomeRemapRules] = None, arguments: Optional[Iterable[SomeSubstitutionsType]] = None, ) -> None: """ Construct a SandboxedNode description. The actual node execution is delegated to the sandboxing environment defined by the policy. :param: package is the name of the node's package and is required for resolving the node. :param: node_executable is the name of the node's executable and is required for resolving the node. :param: node_name is an optional name attached to the node when it is launched. Defaults to NONE. :param: node_namespace is an optional namespace attached to the node when it is launched. Defaults to empty string. :param: parameters are the optional runtime configurations for the node, read from a YAML file. Defaults to NONE. :param: remappings are the ordered list of 'to' and 'from' string pairs to be passed to a node as ROS remapping rules. """ self.__package = \ normalize_to_list_of_substitutions(package) self.__node_executable = \ normalize_to_list_of_substitutions(node_executable) self.__node_name = None if node_name is not None: self.__node_name = normalize_to_list_of_substitutions(node_name) self.__node_namespace = None if node_namespace is not None: self.__node_namespace = \ normalize_to_list_of_substitutions(node_namespace) self.__parameters = None if parameters is not None: self.__parameters = normalize_parameters(parameters) self.__remappings = None if remappings is not None: self.__remappings = normalize_remap_rules(remappings)
def test_unallowed_yaml_types_as_strings(): # All the tests from test_unallowed_yaml_types_in_substitutions # but coerced to the proper type with ParameterValue orig = [{'foo': 1, 'fiz': ParameterValue(TextSubstitution(text="{'asd': 3}"), value_type=str)}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'fiz': "{'asd': 3}"},) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 1, 'fiz': ParameterValue(TextSubstitution(text='[1, 2.0, 3]'), value_type=str)}] norm = normalize_parameters(orig) evaluate_parameters(LaunchContext(), norm) expected = ({'foo': 1, 'fiz': '[1, 2.0, 3]'},) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 1, 'fiz': ParameterValue(TextSubstitution(text='[[2, 3], [2, 3], [2, 3]]'), value_type=str)}] norm = normalize_parameters(orig) evaluate_parameters(LaunchContext(), norm) expected = ({'foo': 1, 'fiz': '[[2, 3], [2, 3], [2, 3]]'},) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 1, 'fiz': ParameterValue(TextSubstitution(text='[]'), value_type=str)}] norm = normalize_parameters(orig) evaluate_parameters(LaunchContext(), norm) expected = ({'foo': 1, 'fiz': '[]'},) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{ 'foo': 1, 'fiz': ParameterValue([ [TextSubstitution(text="['asd', 'bsd']")], [TextSubstitution(text="['asd', 'csd']")] ], value_type=List[str]) }] norm = normalize_parameters(orig) evaluate_parameters(LaunchContext(), norm) expected = ({'foo': 1, 'fiz': ["['asd', 'bsd']", "['asd', 'csd']"]},) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'foo': 1, 'fiz': ParameterValue(TextSubstitution(text='Text That : Cannot Be Parsed As : Yaml'), value_type=str)}] norm = normalize_parameters(orig) evaluate_parameters(LaunchContext(), norm) expected = ({'foo': 1, 'fiz': 'Text That : Cannot Be Parsed As : Yaml'},) assert evaluate_parameters(LaunchContext(), norm) == expected
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)
def test_dictionary_with_substitution_list_value(): orig = [{ 'foo': [[TextSubstitution(text='fiz'), TextSubstitution(text='buz')], TextSubstitution(text='fiz')] }] norm = normalize_parameters(orig) expected = ({'foo': ('fizbuz', 'fiz')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{ 'foo': [TextSubstitution(text='fiz'), TextSubstitution(text='buz')] }] norm = normalize_parameters(orig) expected = ({'foo': 'fizbuz'}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{ 'foo': [[TextSubstitution(text='fiz')], [TextSubstitution(text='buz')]] }] norm = normalize_parameters(orig) expected = ({'foo': ('fiz', 'buz')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{ 'bools': [[TextSubstitution(text='True')], [TextSubstitution(text='False')]] }] norm = normalize_parameters(orig) expected = ({'bools': (True, False)}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{ 'ints': [[TextSubstitution(text='1')], [TextSubstitution(text='2')]] }] norm = normalize_parameters(orig) expected = ({'ints': (1, 2)}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{ 'floats': [[TextSubstitution(text='1.0')], [TextSubstitution(text='2.0')]] }] norm = normalize_parameters(orig) expected = ({'floats': (1.0, 2.0)}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'strings': ['True', '1', '1.0']}] norm = normalize_parameters(orig) expected = ({'strings': ('True', '1', '1.0')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'int_sequence': TextSubstitution(text='[2, 3, 4]')}] norm = normalize_parameters(orig) expected = ({'int_sequence': (2, 3, 4)}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'float_sequence': TextSubstitution(text='[2.0, 3.0, 4.0]')}] norm = normalize_parameters(orig) expected = ({'float_sequence': (2., 3., 4.)}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{'bool_sequence': TextSubstitution(text='[True, False, True]')}] norm = normalize_parameters(orig) expected = ({'bool_sequence': (True, False, True)}, ) assert evaluate_parameters(LaunchContext(), norm) == expected orig = [{ 'string_sequence': TextSubstitution(text="['True', '1', 'asd', '2.0']") }] norm = normalize_parameters(orig) expected = ({'string_sequence': ('True', '1', 'asd', '2.0')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
def test_dictionary_with_substitution_list_name(): orig = [{(TextSubstitution(text='bar'), TextSubstitution(text='foo')): 1}] norm = normalize_parameters(orig) expected = ({'barfoo': 1}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
def test_multiple_dictionaries(): orig = [{'foo': 1, 'bar': 2.0}, {'baz': 'asdf'}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'bar': 2.0}, {'baz': 'asdf'}) assert evaluate_parameters(LaunchContext(), norm) == expected
def test_single_dictionary(): orig = [{'foo': 1, 'bar': 2.0}] norm = normalize_parameters(orig) expected = ({'foo': 1, 'bar': 2.0}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
def test_path_with_substitutions(): orig = [(TextSubstitution(text='/foo'), TextSubstitution(text='/bar'))] norm = normalize_parameters(orig) expected = (pathlib.Path('/foo/bar'), ) assert evaluate_parameters(LaunchContext(), norm) == expected
def test_multiple_paths(): norm = normalize_parameters([pathlib.Path('/foo/bar'), '/bar/baz']) expected = (pathlib.Path('/foo/bar'), pathlib.Path('/bar/baz')) assert evaluate_parameters(LaunchContext(), norm) == expected
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
def test_not_a_list(): with pytest.raises(TypeError): normalize_parameters({'foo': 'bar'}) with pytest.raises(TypeError): normalize_parameters('foobar')
def __init__(self, *, executable: Optional[SomeSubstitutionsType] = None, node_executable: Optional[SomeSubstitutionsType] = None, package: Optional[SomeSubstitutionsType] = None, name: Optional[SomeSubstitutionsType] = None, namespace: Optional[SomeSubstitutionsType] = None, node_name: Optional[SomeSubstitutionsType] = None, node_namespace: SomeSubstitutionsType = None, exec_name: Optional[SomeSubstitutionsType] = None, parameters: Optional[SomeParameters] = None, remappings: Optional[SomeRemapRules] = None, arguments: Optional[Iterable[SomeSubstitutionsType]] = None, **kwargs) -> None: """ Construct an Node action. Many arguments are passed eventually to :class:`launch.actions.ExecuteProcess`, so see the documentation of that class for additional details. However, the `cmd` is not meant to be used, instead use the `executable` and `arguments` keyword arguments to this function. This action, once executed, delegates most work to the :class:`launch.actions.ExecuteProcess`, but it also converts some ROS specific arguments into generic command line arguments. The launch_ros.substitutions.ExecutableInPackage substitution is used to find the executable at runtime, so this Action also raise the exceptions that substituion can raise when the package or executable are not found. If the name is not given (or is None) then no name is passed to the node on creation and instead the default name specified within the code of the node is used instead. The namespace can either be absolute (i.e. starts with /) or relative. If absolute, then nothing else is considered and this is passed directly to the node to set the namespace. If relative, the namespace in the 'ros_namespace' LaunchConfiguration will be prepended to the given relative node namespace. If no namespace is given, then the default namespace `/` is assumed. The parameters are passed as a list, with each element either a yaml file that contains parameter rules (string or pathlib.Path to the full path of the file), or a dictionary that specifies parameter rules. Keys of the dictionary can be strings or an iterable of Substitutions that will be expanded to a string. Values in the dictionary can be strings, integers, floats, or tuples of Substitutions that will be expanded to a string. Additionally, values in the dictionary can be lists of the aforementioned types, or another dictionary with the same properties. A yaml file with the resulting parameters from the dictionary will be written to a temporary file, the path to which will be passed to the node. Multiple dictionaries/files can be passed: each file path will be passed in in order to the node (where the last definition of a parameter takes effect). .. deprecated:: Foxy Parameters `node_executable`, `node_name`, and `node_namespace` are deprecated. Use `executable`, `name`, and `namespace` instead. :param: executable the name of the executable to find if a package is provided or otherwise a path to the executable to run. :param: node_executable (DEPRECATED) the name of the executable to find if a package is provided or otherwise a path to the executable to run. :param: package the package in which the node executable can be found :param: name the name of the node :param: namespace the ROS namespace for this Node :param: exec_name the label used to represent the process. Defaults to the basename of node executable. :param: node_name (DEPRECATED) the name of the node :param: node_namespace (DEPRECATED) the ros namespace for this Node :param: parameters list of names of yaml files with parameter rules, or dictionaries of parameters. :param: remappings ordered list of 'to' and 'from' string pairs to be passed to the node as ROS remapping rules :param: arguments list of extra arguments for the node """ if node_executable is not None: warnings.warn( "The parameter 'node_executable' is deprecated, use 'executable' instead", stacklevel=2) if executable is not None: raise RuntimeError( "Passing both 'node_executable' and 'executable' parameters. " "Only use 'executable'") executable = node_executable if package is not None: cmd = [ExecutableInPackage(package=package, executable=executable)] else: cmd = [executable] cmd += [] if arguments is None else arguments # Reserve space for ros specific arguments. # The substitutions will get expanded when the action is executed. cmd += ['--ros-args' ] # Prepend ros specific arguments with --ros-args flag if node_name is not None: warnings.warn( "The parameter 'node_name' is deprecated, use 'name' instead", stacklevel=2) if name is not None: raise RuntimeError( "Passing both 'node_name' and 'name' parameters. Only use 'name'." ) cmd += [ '-r', LocalSubstitution("ros_specific_arguments['name']", description='node name') ] name = node_name if name is not None: cmd += [ '-r', LocalSubstitution("ros_specific_arguments['name']", description='node name') ] if node_namespace: warnings.warn( "The parameter 'node_namespace' is deprecated, use 'namespace' instead" ) if namespace: raise RuntimeError( "Passing both 'node_namespace' and 'namespace' parameters. " "Only use 'namespace'.") namespace = node_namespace if parameters is not None: ensure_argument_type(parameters, (list), 'parameters', 'Node') # All elements in the list are paths to files with parameters (or substitutions that # evaluate to paths), or dictionaries of parameters (fields can be substitutions). normalized_params = normalize_parameters(parameters) # Forward 'exec_name' as to ExecuteProcess constructor kwargs['name'] = exec_name super().__init__(cmd=cmd, **kwargs) self.__package = package self.__node_executable = executable self.__node_name = name self.__node_namespace = namespace self.__parameters = [] if parameters is None else normalized_params self.__remappings = [] if remappings is None else list( normalize_remap_rules(remappings)) self.__arguments = arguments self.__expanded_node_name = self.UNSPECIFIED_NODE_NAME self.__expanded_node_namespace = self.UNSPECIFIED_NODE_NAMESPACE self.__expanded_parameter_arguments = None # type: Optional[List[Tuple[Text, bool]]] self.__final_node_name = None # type: Optional[Text] self.__expanded_remappings = None # type: Optional[List[Tuple[Text, Text]]] self.__substitutions_performed = False self.__logger = launch.logging.get_logger(__name__)
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
def test_dictionary_with_str(): orig = [{'foo': 'bar', 'fiz': ['b', 'u', 'z']}] norm = normalize_parameters(orig) expected = ({'foo': 'bar', 'fiz': ('b', 'u', 'z')}, ) assert evaluate_parameters(LaunchContext(), norm) == expected
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
def __init__( self, *, package: SomeSubstitutionsType, plugin: Optional[SomeSubstitutionsType] = None, name: Optional[SomeSubstitutionsType] = None, namespace: Optional[SomeSubstitutionsType] = None, node_plugin: Optional[SomeSubstitutionsType] = None, node_name: Optional[SomeSubstitutionsType] = None, node_namespace: Optional[SomeSubstitutionsType] = None, parameters: Optional[SomeParameters] = None, remappings: Optional[SomeRemapRules] = None, extra_arguments: Optional[SomeParameters] = None, ) -> None: """ Initialize a ComposableNode description. .. deprecated:: Foxy Parameters `node_plugin`, `node_name`, and `node_namespace` are deprecated. Use `plugin`, `name`, and `namespace` instead. :param package: name of the ROS package the node plugin lives in :param plugin: name of the plugin to be loaded :param name: name to give to the ROS node :param namespace: namespace to give to the ROS node :param node_plugin: (DEPRECATED) name of the plugin to be loaded :param node_name: (DEPRECATED) name the node should have :param node_namespace: (DEPRECATED) namespace the node should create topics/services/etc in :param parameters: list of either paths to yaml files or dictionaries of parameters :param remappings: list of from/to pairs for remapping names :param extra_arguments: container specific arguments to be passed to the loaded node """ if node_plugin is not None: warnings.warn("The parameter 'node_plugin' is deprecated, use 'plugin' instead") if plugin is not None: raise RuntimeError( "Passing both 'node_plugin' and 'plugin' parameters. Only use 'plugin'." ) plugin = node_plugin if plugin is None: raise RuntimeError("The 'plugin' parameter is required") if node_name is not None: warnings.warn("The parameter 'node_name' is deprecated, use 'name' instead") if name is not None: raise RuntimeError( "Passing both 'node_name' and 'name' parameters. Only use 'name'." ) name = node_name if node_namespace is not None: warnings.warn("The parameter 'node_namespace' is deprecated, use 'namespace' instead") if namespace is not None: raise RuntimeError( "Passing both 'node_namespace' and 'namespace' parameters. " "Only use 'namespace'." ) namespace = node_namespace self.__package = normalize_to_list_of_substitutions(package) self.__node_plugin = normalize_to_list_of_substitutions(plugin) self.__node_name = None # type: Optional[List[Substitution]] if name is not None: self.__node_name = normalize_to_list_of_substitutions(name) self.__node_namespace = None # type: Optional[List[Substitution]] if namespace is not None: self.__node_namespace = normalize_to_list_of_substitutions(namespace) self.__parameters = None # type: Optional[Parameters] if parameters is not None: self.__parameters = normalize_parameters(parameters) self.__remappings = None # type: Optional[RemapRules] if remappings: self.__remappings = normalize_remap_rules(remappings) self.__extra_arguments = None # type: Optional[Parameters] if extra_arguments: self.__extra_arguments = normalize_parameters(extra_arguments)
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
def test_single_pathlib_path(): norm = normalize_parameters([pathlib.Path('/foo/bar')]) expected = (pathlib.Path('/foo/bar'), ) assert evaluate_parameters(LaunchContext(), norm) == expected
def __init__(self, *, package: SomeSubstitutionsType, node_executable: SomeSubstitutionsType, node_name: Optional[SomeSubstitutionsType] = None, node_namespace: SomeSubstitutionsType = '', parameters: Optional[SomeParameters] = None, remappings: Optional[SomeRemapRules] = None, arguments: Optional[Iterable[SomeSubstitutionsType]] = None, **kwargs) -> None: """ Construct an Node action. Many arguments are passed eventually to :class:`launch.actions.ExecuteProcess`, so see the documentation of that class for additional details. However, the `cmd` is not meant to be used, instead use the `node_executable` and `arguments` keyword arguments to this function. This action, once executed, delegates most work to the :class:`launch.actions.ExecuteProcess`, but it also converts some ROS specific arguments into generic command line arguments. The launch_ros.substitutions.ExecutableInPackage substitution is used to find the executable at runtime, so this Action also raise the exceptions that substituion can raise when the package or executable are not found. If the node_name is not given (or is None) then no name is passed to the node on creation and instead the default name specified within the code of the node is used instead. The node_namespace can either be absolute (i.e. starts with /) or relative. If absolute, then nothing else is considered and this is passed directly to the node to set the namespace. If relative, the namespace in the 'ros_namespace' LaunchConfiguration will be prepended to the given relative node namespace. If no node_namespace is given, then the default namespace `/` is assumed. The parameters are passed as a list, with each element either a yaml file that contains parameter rules (string or pathlib.Path to the full path of the file), or a dictionary that specifies parameter rules. Keys of the dictionary can be strings or an iterable of Substitutions that will be expanded to a string. Values in the dictionary can be strings, integers, floats, or tuples of Substitutions that will be expanded to a string. Additionally, values in the dictionary can be lists of the aforementioned types, or another dictionary with the same properties. A yaml file with the resulting parameters from the dictionary will be written to a temporary file, the path to which will be passed to the node. Multiple dictionaries/files can be passed: each file path will be passed in in order to the node (where the last definition of a parameter takes effect). :param: package the package in which the node executable can be found :param: node_executable the name of the executable to find :param: node_name the name of the node :param: node_namespace the ros namespace for this Node :param: parameters list of names of yaml files with parameter rules, or dictionaries of parameters. :param: remappings ordered list of 'to' and 'from' string pairs to be passed to the node as ROS remapping rules :param: arguments list of extra arguments for the node """ cmd = [ ExecutableInPackage(package=package, executable=node_executable) ] cmd += [] if arguments is None else arguments # Reserve space for ros specific arguments. # The substitutions will get expanded when the action is executed. cmd += ['--ros-args' ] # Prepend ros specific arguments with --ros-args flag if node_name is not None: cmd += [ '-r', LocalSubstitution("ros_specific_arguments['name']", description='node name') ] if parameters is not None: ensure_argument_type(parameters, (list), 'parameters', 'Node') # All elements in the list are paths to files with parameters (or substitutions that # evaluate to paths), or dictionaries of parameters (fields can be substitutions). i = 0 for param in parameters: cmd += [ LocalSubstitution( "ros_specific_arguments['params'][{}]".format(i), description='parameter {}'.format(i)) ] i += 1 normalized_params = normalize_parameters(parameters) if remappings is not None: i = 0 for remapping in normalize_remap_rules(remappings): k, v = remapping cmd += [ '-r', LocalSubstitution( "ros_specific_arguments['remaps'][{}]".format(i), description='remapping {}'.format(i)) ] i += 1 super().__init__(cmd=cmd, **kwargs) self.__package = package self.__node_executable = node_executable self.__node_name = node_name self.__node_namespace = node_namespace self.__parameters = [] if parameters is None else normalized_params self.__remappings = [] if remappings is None else remappings self.__arguments = arguments self.__expanded_node_name = '<node_name_unspecified>' self.__expanded_node_namespace = '' self.__final_node_name = None # type: Optional[Text] self.__expanded_parameter_files = None # type: Optional[List[Text]] self.__expanded_remappings = None # type: Optional[List[Tuple[Text, Text]]] self.__substitutions_performed = False self.__logger = launch.logging.get_logger(__name__)
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