def test_valid_substitutions():
    """Test the normalize_to_list_of_substitutions() function with valid input."""
    class MockSubstitution(Substitution):
        pass

    sub = MockSubstitution()
    norm_sub = normalize_to_list_of_substitutions(sub)
    assert isinstance(norm_sub, list)
    assert len(norm_sub) == 1
    assert isinstance(norm_sub[0], Substitution)
    text_sub = 'foo'
    norm_text_sub = normalize_to_list_of_substitutions(text_sub)
    assert isinstance(norm_text_sub, list)
    assert len(norm_text_sub) == 1
    assert isinstance(norm_text_sub[0], Substitution)
    sub_list = ['bar', MockSubstitution()]
    norm_sub_list = normalize_to_list_of_substitutions(sub_list)
    assert isinstance(norm_sub_list, list)
    assert len(norm_sub_list) == 2
    assert isinstance(norm_sub_list[0], Substitution)
    assert isinstance(norm_sub_list[1], Substitution)
    sub_tuple = (MockSubstitution(), '')
    norm_sub_tuple = normalize_to_list_of_substitutions(sub_tuple)
    assert isinstance(norm_sub_tuple, list)
    assert len(norm_sub_tuple) == 2
    assert isinstance(norm_sub_tuple[0], Substitution)
    assert isinstance(norm_sub_tuple[1], Substitution)
    norm_sub_empty = normalize_to_list_of_substitutions([])
    assert isinstance(norm_sub_empty, list)
    assert len(norm_sub_empty) == 0
예제 #2
0
    def __init__(self,
                 source_file: launch.SomeSubstitutionsType,
                 param_rewrites: Dict,
                 root_key: Optional[launch.SomeSubstitutionsType] = None,
                 key_rewrites: Optional[Dict] = None,
                 convert_types=False) -> None:
        super().__init__()
        """
        Construct the substitution

        :param: source_file the original YAML file to modify
        :param: param_rewrites mappings to replace
        :param: root_key if provided, the contents are placed under this key
        :param: key_rewrites keys of mappings to replace
        :param: convert_types whether to attempt converting the string to a number or boolean
        """

        from launch.utilities import normalize_to_list_of_substitutions  # import here to avoid loop
        self.__source_file = normalize_to_list_of_substitutions(source_file)
        self.__param_rewrites = {}
        self.__key_rewrites = {}
        self.__convert_types = convert_types
        self.__root_key = None
        for key in param_rewrites:
            self.__param_rewrites[key] = normalize_to_list_of_substitutions(
                param_rewrites[key])
        if key_rewrites is not None:
            for key in key_rewrites:
                self.__key_rewrites[key] = normalize_to_list_of_substitutions(
                    key_rewrites[key])
        if root_key is not None:
            self.__root_key = normalize_to_list_of_substitutions(root_key)
예제 #3
0
 def expand_dict(input_dict):
     expanded_dict = {}
     for k, v in input_dict.items():
         # Key (parameter/group name) can only be a string/Substitutions that evaluates
         # to a string.
         expanded_key = perform_substitutions(
             context, normalize_to_list_of_substitutions(k))
         if isinstance(v, dict):
             # Expand the nested dict.
             expanded_value = expand_dict(v)
         elif isinstance(v, list):
             # Expand each element.
             expanded_value = []
             for e in v:
                 if isinstance(e, list):
                     raise TypeError(
                         'Nested lists are not supported for parameters: {} found in {}'
                         .format(e, v))
                 expanded_value.append(
                     perform_substitution_if_applicable(context, e))
         # Tuples are treated as Substitution(s) to be concatenated.
         elif isinstance(v, tuple):
             for e in v:
                 ensure_argument_type(
                     e, SomeSubstitutionsType_types_tuple,
                     'parameter dictionary tuple entry', 'Node')
             expanded_value = perform_substitutions(
                 context, normalize_to_list_of_substitutions(v))
         else:
             expanded_value = perform_substitution_if_applicable(
                 context, v)
         expanded_dict[expanded_key] = expanded_value
     return expanded_dict
def _normalize_parameter_array_value(
        value: SomeParameterValue) -> ParameterValue:
    """Normalize substitutions while preserving the type if it's not a substitution."""
    if not isinstance(value, Sequence):
        raise TypeError('Value {} must be a sequence'.format(repr(value)))

    # Figure out what type the list should be
    has_types = set()  # type: Set[type]
    for subvalue in value:
        allowed_subtypes = (float, int, str,
                            bool) + SomeSubstitutionsType_types_tuple
        ensure_argument_type(subvalue, allowed_subtypes, 'subvalue')

        if isinstance(subvalue, Substitution):
            has_types.add(Substitution)
        elif isinstance(subvalue, str):
            has_types.add(str)
        elif isinstance(subvalue, bool):
            has_types.add(bool)
        elif isinstance(subvalue, int):
            has_types.add(int)
        elif isinstance(subvalue, float):
            has_types.add(float)
        elif isinstance(subvalue, Sequence):
            has_types.add(tuple)
        else:
            raise RuntimeError('Failed to handle type {}'.format(
                repr(subvalue)))

    if {int} == has_types:
        # everything is an integer
        make_mypy_happy_int = cast(List[int], value)
        return tuple(int(e) for e in make_mypy_happy_int)
    elif has_types in ({float}, {int, float}):
        # all were floats or ints, so return floats
        make_mypy_happy_float = cast(List[Union[int, float]], value)
        return tuple(float(e) for e in make_mypy_happy_float)
    elif Substitution in has_types and has_types.issubset(
        {str, Substitution, tuple}):
        # make a list of substitutions forming a single string
        return tuple(
            normalize_to_list_of_substitutions(
                cast(SomeSubstitutionsType, value)))
    elif {bool} == has_types:
        # all where bools
        return tuple(bool(e) for e in value)
    else:
        # Should evaluate to a list of strings
        # Normalize to a list of lists of substitutions
        new_value = []  # type: List[SomeSubstitutionsType]
        for element in value:
            if isinstance(element, float) or isinstance(
                    element, int) or isinstance(element, bool):
                new_value.append(str(element))
            else:
                new_value.append(element)
        return tuple(normalize_to_list_of_substitutions(e) for e in new_value)
    def __init__(self, source_file: launch.SomeSubstitutionsType,
                 replacements: Dict) -> None:
        super().__init__()

        from launch.utilities import normalize_to_list_of_substitutions  # import here to avoid loop
        self.__source_file = normalize_to_list_of_substitutions(source_file)
        self.__replacements = {}
        for key in replacements:
            self.__replacements[key] = normalize_to_list_of_substitutions(
                replacements[key])
def normalize_remap_rule(remap_rule: SomeRemapRule) -> RemapRule:
    """Normalize a remap rule to a specific type."""
    ensure_argument_type(remap_rule, (tuple), 'remap_rule')
    if len(remap_rule) != 2:
        raise TypeError(
            'remap_rule must be a tuple of length 2, got length {}'.format(
                len(remap_rule)))
    from_sub = tuple(normalize_to_list_of_substitutions(remap_rule[0]))
    to_sub = tuple(normalize_to_list_of_substitutions(remap_rule[1]))
    return from_sub, to_sub
예제 #7
0
    def __init__(self,
                 source_file: launch.SomeSubstitutionsType,
                 rewrites: Dict,
                 convert_types=False) -> None:
        super().__init__()

        from launch.utilities import normalize_to_list_of_substitutions  # import here to avoid loop
        self.__source_file = normalize_to_list_of_substitutions(source_file)
        self.__rewrites = {}
        self.__convert_types = convert_types
        for key in rewrites:
            self.__rewrites[key] = normalize_to_list_of_substitutions(
                rewrites[key])
    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)
예제 #9
0
 def __init__(
     self,
     package: SomeSubstitutionsType,
 ) -> None:
     """Constructor."""
     super().__init__()
     self.__package = normalize_to_list_of_substitutions(package)
    def __init__(self,
                 *,
                 sandbox_name: Optional[SomeSubstitutionsType] = None,
                 policy: Optional[Policy] = None,
                 node_descriptions: Optional[List[SandboxedNode]] = None,
                 **kwargs) -> None:
        """
        Initialize the SandboxedNodeContainer.

        :param: sandbox_name is an optional name assigned to the sandbox environment.
        :param: policy defines the sandboxing strategy used by the sandbox environment.
        :param: node_descriptions are the list of nodes to launch inside the sandbox environment.
        """
        super().__init__(**kwargs)

        self.__sandbox_name = None
        if sandbox_name is not None:
            self.__sandbox_name = normalize_to_list_of_substitutions(
                sandbox_name)

        self.__node_descriptions = None
        if node_descriptions is not None:
            self.__node_descriptions = node_descriptions

        self.__policy = policy
예제 #11
0
 def __init__(self, name: SomeSubstitutionsType, value: SomeParameterValue,
              **kwargs) -> None:
     """Create a SetParameter action."""
     super().__init__(**kwargs)
     normalized_name = normalize_to_list_of_substitutions(name)
     self.__param_dict = normalize_parameter_dict(
         {tuple(normalized_name): value})
예제 #12
0
    def __init__(self,
                 *,
                 command: str,
                 arguments: Iterable[SomeSubstitutionsType],
                 actions: Iterable[Action] = [],
                 expected_output: Iterable[str] = [],
                 description: Optional[str] = None):
        """
        Constructor.

        :param command: `ros2` command to be tested.
        :param description: description of the test being done. command to be tested.
            It usually contains the verb and arguments being tested.
        :param arguments: A list of `SomeSubstitutionsType`, which are passed
            as arguments to the command.
        :param actions: A list of actions, which are launched before the `ros2` command.
        :param expected_output: A list of str, which are checked to be contained in the output
            of the `ros2` command.
        """
        self.command = command
        self.arguments = [
            normalize_to_list_of_substitutions(arg) for arg in arguments
        ]
        self.actions = actions
        self.expected_output = expected_output
        self.description = description

        def describe(some_subs: SomeSubstitutionsType):
            return ''.join([sub.describe() for sub in some_subs])

        if description is None:
            self.description = 'ros2 {} {}'.format(
                command,
                ' '.join([describe(some_subs)
                          for some_subs in self.arguments]))
예제 #13
0
    def execute(self, context: LaunchContext) -> Optional[List[Action]]:
        """Execute the action."""
        # resolve target container node name

        if is_a_subclass(self.__target_container, ComposableNodeContainer):
            self.__final_target_container_name = self.__target_container.node_name
        elif isinstance(self.__target_container,
                        SomeSubstitutionsType_types_tuple):
            subs = normalize_to_list_of_substitutions(self.__target_container)
            self.__final_target_container_name = perform_substitutions(
                context, subs)
        else:
            self.__logger.error(
                'target container is neither a ComposableNodeContainer nor a SubstitutionType'
            )
            return

        # Create a client to load nodes in the target container.
        self.__rclpy_load_node_client = get_ros_node(context).create_client(
            composition_interfaces.srv.LoadNode,
            '{}/_container/load_node'.format(
                self.__final_target_container_name))

        # Generate load requests before execute() exits to avoid race with context changing
        # due to scope change (e.g. if loading nodes from within a GroupAction).
        load_node_requests = [
            get_composable_node_load_request(node_description, context)
            for node_description in self.__composable_node_descriptions
        ]

        context.add_completion_future(
            context.asyncio_loop.run_in_executor(None, self._load_in_sequence,
                                                 load_node_requests, context))
예제 #14
0
def normalize_parameters(parameters: SomeParameters) -> Parameters:
    """
    Normalize the types used to store parameters to substitution types.

    The passed parameters must be an iterable where each element is
    a path to a yaml file or a dict.
    The normalized parameters will have all paths converted to a list of :class:`Substitution`,
    and dictionaries normalized using :meth:`normalize_parameter_dict`.
    """
    if isinstance(parameters, str) or not isinstance(parameters, Sequence):
        raise TypeError(
            'Expecting list of parameters, got {}'.format(parameters))

    normalized_params = []  # type: List[Union[ParameterFile, ParametersDict]]
    for param in parameters:
        if isinstance(param, Mapping):
            normalized_params.append(normalize_parameter_dict(param))
        else:
            # It's a path, normalize to a list of substitutions
            if isinstance(param, pathlib.Path):
                param = str(param)
            ensure_argument_type(param, SomeSubstitutionsType_types_tuple,
                                 'parameters')
            normalized_params.append(
                tuple(normalize_to_list_of_substitutions(param)))
    return tuple(normalized_params)
예제 #15
0
    def __init__(self,
                 name: SomeSubstitutionsType,
                 value: SomeValueType,
                 *,
                 value_type: Optional[AllowedTypesType] = None) -> None:
        """
        Construct a parameter description.

        :param name: Name of the parameter.
        :param value: Value of the parameter.
        :param value_type: Used when `value` is a substitution, to coerce the result.
            Can be one of:
                - A scalar type: `int`, `str`, `float`, `bool`.
                  `bool` are written like in `yaml`.
                  Both `1` and `1.` are valid floats.
                - An uniform list: `List[int]`, `List[str]`, `List[float]`, `List[bool]`.
                  Lists are written like in `yaml`.
                - `None`, which means that yaml rules will be used.
                  The result of the convertion must be one of the above types,
                  if not `ValueError` is raised.
            If value is not a substitution and this parameter is provided,
            it will be used to check `value` type.
        """
        ensure_argument_type(name, SomeSubstitutionsType_types_tuple, 'name')

        self.__name = normalize_to_list_of_substitutions(name)
        self.__parameter_value = ParameterValue(value, value_type=value_type)
        self.__evaluated_parameter_name: Optional[Text] = None
        self.__evaluated_parameter_rule: Optional[Tuple[
            Text, 'EvaluatedParameterValue']] = None
예제 #16
0
    def execute(
        self,
        context: LaunchContext
    ) -> Optional[List[Action]]:
        """Execute the action."""
        # resolve target container node name

        if is_a_subclass(self.__target_container, ComposableNodeContainer):
            self.__final_target_container_name = self.__target_container.node_name
        elif isinstance(self.__target_container, SomeSubstitutionsType_types_tuple):
            subs = normalize_to_list_of_substitutions(self.__target_container)
            self.__final_target_container_name = perform_substitutions(
                context, subs)
        else:
            self.__logger.error(
                'target container is neither a ComposableNodeContainer nor a SubstitutionType')
            return

        # Create a client to load nodes in the target container.
        self.__rclpy_load_node_client = get_ros_node(context).create_client(
            composition_interfaces.srv.LoadNode, '{}/_container/load_node'.format(
                self.__final_target_container_name
            )
        )

        context.add_completion_future(
            context.asyncio_loop.run_in_executor(
                None, self._load_in_sequence, self.__composable_node_descriptions, context
            )
        )
예제 #17
0
    def __init__(self,
                 param_file: Union[FilePath, SomeSubstitutionsType],
                 *,
                 allow_substs: [bool, SomeSubstitutionsType] = False) -> None:
        """
        Construct a parameter file description.

        :param param_file: Path to a parameter file.
        :param allow_subst: Allow substitutions in the parameter file.
        """
        # In Python, __del__ is called even if the constructor throws an
        # exception.  It is possible for ensure_argument_type() below to
        # throw an exception and try to access these member variables
        # during cleanup, so make sure to initialize them here.
        self.__evaluated_param_file: Optional[Path] = None
        self.__created_tmp_file = False

        ensure_argument_type(
            param_file,
            SomeSubstitutionsType_types_tuple + (os.PathLike, bytes),
            'param_file', 'ParameterFile()')
        ensure_argument_type(allow_substs, bool, 'allow_subst',
                             'ParameterFile()')
        self.__param_file: Union[List[Substitution], FilePath] = param_file
        if isinstance(param_file, SomeSubstitutionsType_types_tuple):
            self.__param_file = normalize_to_list_of_substitutions(param_file)
        self.__allow_substs = normalize_typed_substitution(allow_substs,
                                                           data_type=bool)
        self.__evaluated_allow_substs: Optional[bool] = None
예제 #18
0
 def __init__(
     self,
     namespace: SomeSubstitutionsType,
     **kwargs
 ) -> None:
     """Create a PushRosNamespace action."""
     super().__init__(**kwargs)
     self.__namespace = normalize_to_list_of_substitutions(namespace)
예제 #19
0
 def __init__(
     self,
     namespace: SomeSubstitutionsType,
     **kwargs
 ) -> None:
     """Constructor."""
     super().__init__(**kwargs)
     self.__namespace = normalize_to_list_of_substitutions(namespace)
def test_invalid_substitutions():
    """Test the normalize_to_list_of_substitutions() function with invalid input."""
    with pytest.raises(TypeError):
        normalize_to_list_of_substitutions(None)
    with pytest.raises(TypeError):
        normalize_to_list_of_substitutions(1)
    with pytest.raises(TypeError):
        normalize_to_list_of_substitutions([1.4142, 'bar'])
    with pytest.raises(TypeError):
        normalize_to_list_of_substitutions(['foo', ['bar']])
예제 #21
0
    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)
예제 #22
0
파일: node.py 프로젝트: hfz-Nick/ROS
 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.__node_name)
         self.__expanded_node_name.lstrip('/')
         if self.__node_namespace is not None:
             self.__expanded_node_namespace = perform_substitutions(
                 context,
                 normalize_to_list_of_substitutions(self.__node_namespace))
         if not self.__expanded_node_namespace.startswith('/'):
             self.__expanded_node_namespace = '/' + self.__expanded_node_namespace
         validate_namespace(self.__expanded_node_namespace)
     except Exception:
         print(
             "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 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[key] = value
예제 #23
0
 def perform_substitution_if_applicable(context, var):
     if isinstance(var, (int, float, str)):
         # No substitution necessary.
         return var
     if isinstance(var, Substitution):
         return perform_substitutions(
             context, normalize_to_list_of_substitutions(var))
     if isinstance(var, tuple):
         try:
             return perform_substitutions(
                 context, normalize_to_list_of_substitutions(var))
         except TypeError:
             raise TypeError(
                 'Invalid element received in parameters dictionary '
                 '(not all tuple elements are Substitutions): {}'.
                 format(var))
     else:
         raise TypeError(
             'Unsupported type received in parameters dictionary: {}'
             .format(type(var)))
예제 #24
0
    def __init__(self, source_file: launch.SomeSubstitutionsType,
                 node_name: Text) -> None:
        super().__init__()
        """
    Construct the substitution

    :param: source_file the parameter YAML file
    :param: node_name the name of the node to check
    """

        from launch.utilities import normalize_to_list_of_substitutions  # import here to avoid loop
        self.__source_file = normalize_to_list_of_substitutions(source_file)
        self.__node_name = node_name
예제 #25
0
 def execute(self, context: LaunchContext):
     continue_after_fail = self.__continue_after_fail
     if not isinstance(continue_after_fail, bool):
         continue_after_fail = perform_substitutions(
             context, normalize_to_list_of_substitutions(
                 continue_after_fail)).lower()
         if continue_after_fail in ['true', 'on', '1']:
             continue_after_fail = True
         elif continue_after_fail in ['false', 'off', '1']:
             continue_after_fail = False
         else:
             raise ValueError(
                 'continue_after_fail should be a boolean, got {}'.format(
                     continue_after_fail))
     on_first_action_exited = OnProcessExit(
         target_action=self.__actions[0],
         on_exit=lambda event, context:
         ([InOrderGroup(self.__actions[1:])]
          if event.exitcode == 0 or continue_after_fail else []))
     return [
         self.__actions[0],
         RegisterEventHandler(on_first_action_exited)
     ]
예제 #26
0
    def __init__(self,
                 param_file: Union[FilePath, SomeSubstitutionsType],
                 *,
                 allow_substs: [bool, SomeSubstitutionsType] = False) -> None:
        """
        Construct a parameter file description.

        :param param_file: Path to a parameter file.
        :param allow_subst: Allow substitutions in the parameter file.
        """
        ensure_argument_type(
            param_file,
            SomeSubstitutionsType_types_tuple + (os.PathLike, bytes),
            'param_file', 'ParameterFile()')
        ensure_argument_type(allow_substs, bool, 'allow_subst',
                             'ParameterFile()')
        self.__param_file: Union[List[Substitution], FilePath] = param_file
        if isinstance(param_file, SomeSubstitutionsType_types_tuple):
            self.__param_file = normalize_to_list_of_substitutions(param_file)
        self.__allow_substs = normalize_typed_substitution(allow_substs,
                                                           data_type=bool)
        self.__evaluated_allow_substs: Optional[bool] = None
        self.__evaluated_param_file: Optional[Path] = None
        self.__created_tmp_file = False
예제 #27
0
    def execute(self, context: LaunchContext) -> Optional[List[Action]]:
        """Execute the action."""
        # resolve target container node name

        if is_a_subclass(self.__target_container, ComposableNodeContainer):
            self.__final_target_container_name = self.__target_container.node_name
        elif isinstance(self.__target_container,
                        SomeSubstitutionsType_types_tuple):
            subs = normalize_to_list_of_substitutions(self.__target_container)
            self.__final_target_container_name = perform_substitutions(
                context, subs)
        else:
            self.__logger.error(
                'target container is neither a ComposableNodeContainer nor a SubstitutionType'
            )
            return

        # Create a client to load nodes in the target container.
        self.__rclpy_load_node_client = context.locals.launch_ros_node.create_client(
            composition_interfaces.srv.LoadNode,
            '{}/_container/load_node'.format(
                self.__final_target_container_name))
        # Assume user has configured `LoadComposableNodes` to happen after container action
        self._load_in_sequence(self.__composable_node_descriptions, context)
예제 #28
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 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_arguments = []
     if global_params is not None:
         param_file_path = self._create_params_file_from_dict(global_params)
         self.__expanded_parameter_arguments.append((param_file_path, True))
         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 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])
예제 #29
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))
예제 #30
0
 def __init__(self, filename: SomeSubstitutionsType, **kwargs) -> None:
     """Create a SetParameterFromFile action."""
     super().__init__(**kwargs)
     self._input_file = normalize_to_list_of_substitutions(filename)