Пример #1
0
    def __init__(self,
                 node_name,
                 *,
                 cli_args=None,
                 namespace=None,
                 use_global_arguments=True):
        self._handle = None
        self.publishers = []
        self.subscriptions = []
        self.clients = []
        self.services = []
        self.timers = []
        self.guards = []
        self._default_callback_group = MutuallyExclusiveCallbackGroup()

        namespace = namespace or ''
        if not ok():
            raise NotInitializedException('cannot create node')
        try:
            self._handle = _rclpy.rclpy_create_node(node_name, namespace,
                                                    cli_args,
                                                    use_global_arguments)
        except ValueError:
            # these will raise more specific errors if the name or namespace is bad
            validate_node_name(node_name)
            # emulate what rcl_node_init() does to accept '' and relative namespaces
            if not namespace:
                namespace = '/'
            if not namespace.startswith('/'):
                namespace = '/' + namespace
            validate_namespace(namespace)
            # Should not get to this point
            raise RuntimeError('rclpy_create_node failed for unknown reason')
        self._logger = get_logger(
            _rclpy.rclpy_get_node_logger_name(self.handle))
Пример #2
0
 def _validate_topic_or_service_name(self, topic_or_service_name, *, is_service=False):
     name = self.get_name()
     namespace = self.get_namespace()
     validate_node_name(name)
     validate_namespace(namespace)
     validate_topic_name(topic_or_service_name, is_service=is_service)
     expanded_topic_or_service_name = expand_topic_name(topic_or_service_name, name, namespace)
     validate_full_topic_name(expanded_topic_or_service_name, is_service=is_service)
Пример #3
0
 def test_validate_namespace_failures(self):
     # namespace must not be empty
     with self.assertRaisesRegex(InvalidNamespaceException, 'empty'):
         validate_namespace('')
     # namespace must start with /
     with self.assertRaisesRegex(InvalidNamespaceException,
                                 'must be absolute'):
         validate_namespace('invalid_namespace')
Пример #4
0
 def test_validate_namespace(self):
     tests = [
         '/my_ns',
         '/',
     ]
     for topic in tests:
         # Will raise if invalid
         validate_namespace(topic)
Пример #5
0
    def __init__(
        self, node_name, *, context=None, cli_args=None, namespace=None, use_global_arguments=True,
        start_parameter_services=True, initial_parameters=None
    ):
        self._handle = None
        self._context = get_default_context() if context is None else context
        self._parameters = {}
        self.publishers = []
        self.subscriptions = []
        self.clients = []
        self.services = []
        self.timers = []
        self.guards = []
        self.waitables = []
        self._default_callback_group = MutuallyExclusiveCallbackGroup()
        self._parameters_callback = None

        namespace = namespace or ''
        if not self._context.ok():
            raise NotInitializedException('cannot create node')
        try:
            self._handle = _rclpy.rclpy_create_node(
                node_name, namespace, self._context.handle, cli_args, use_global_arguments)
        except ValueError:
            # these will raise more specific errors if the name or namespace is bad
            validate_node_name(node_name)
            # emulate what rcl_node_init() does to accept '' and relative namespaces
            if not namespace:
                namespace = '/'
            if not namespace.startswith('/'):
                namespace = '/' + namespace
            validate_namespace(namespace)
            # Should not get to this point
            raise RuntimeError('rclpy_create_node failed for unknown reason')
        self._logger = get_logger(_rclpy.rclpy_get_node_logger_name(self.handle))

        # Clock that has support for ROS time.
        # TODO(dhood): use sim time if parameter has been set on the node.
        self._clock = ROSClock()
        self._time_source = TimeSource(node=self)
        self._time_source.attach_clock(self._clock)

        self.__executor_weakref = None

        self._parameter_event_publisher = self.create_publisher(
            ParameterEvent, 'parameter_events', qos_profile=qos_profile_parameter_events)

        node_parameters = _rclpy.rclpy_get_node_parameters(Parameter, self.handle)
        # Combine parameters from params files with those from the node constructor and
        # use the set_parameters_atomically API so a parameter event is published.
        if initial_parameters is not None:
            node_parameters.update({p.name: p for p in initial_parameters})
        self.set_parameters_atomically(node_parameters.values())

        if start_parameter_services:
            self._parameter_service = ParameterService(self)
Пример #6
0
    def __init__(self,
                 node_name,
                 *,
                 cli_args=None,
                 namespace=None,
                 use_global_arguments=True,
                 start_parameter_services=True):
        self._handle = None
        self._parameters = {}
        self.publishers = []
        self.subscriptions = []
        self.clients = []
        self.services = []
        self.timers = []
        self.guards = []
        self._default_callback_group = MutuallyExclusiveCallbackGroup()
        self._parameters_callback = None

        namespace = namespace or ''
        if not ok():
            raise NotInitializedException('cannot create node')
        try:
            self._handle = _rclpy.rclpy_create_node(node_name, namespace,
                                                    cli_args,
                                                    use_global_arguments)
        except ValueError:
            # these will raise more specific errors if the name or namespace is bad
            validate_node_name(node_name)
            # emulate what rcl_node_init() does to accept '' and relative namespaces
            if not namespace:
                namespace = '/'
            if not namespace.startswith('/'):
                namespace = '/' + namespace
            validate_namespace(namespace)
            # Should not get to this point
            raise RuntimeError('rclpy_create_node failed for unknown reason')
        self._logger = get_logger(
            _rclpy.rclpy_get_node_logger_name(self.handle))

        # Clock that has support for ROS time.
        # TODO(dhood): use sim time if parameter has been set on the node.
        self._clock = ROSClock()
        self._time_source = TimeSource(node=self)
        self._time_source.attach_clock(self._clock)

        self.__executor_weakref = None

        if start_parameter_services:
            self._parameter_service = ParameterService(self)
Пример #7
0
 def execute(self, context: LaunchContext):
     """Execute the action."""
     pushed_namespace = perform_substitutions(context, self.namespace)
     previous_namespace = context.launch_configurations.get(
         'ros_namespace', None)
     namespace = make_namespace_absolute(
         prefix_namespace(previous_namespace, pushed_namespace))
     try:
         validate_namespace(namespace)
     except Exception:
         raise SubstitutionFailure(
             'The resulting namespace is invalid:'
             " [previous_namespace='{}', pushed_namespace='{}']".format(
                 previous_namespace, pushed_namespace))
     context.launch_configurations['ros_namespace'] = namespace
Пример #8
0
def _is_enclave_name_valid(name: str) -> bool:
    # TODO(ivanpauno): Use validate_enclave_name when it's propagated to `rclpy`.
    #   This is not to bad for the moment.
    #   Related with https://github.com/ros2/rclpy/issues/528.
    try:
        return validate_namespace(name)
    except InvalidNamespaceException as e:
        print(e)
        return False
Пример #9
0
 def execute(self, context: LaunchContext):
     """Execute the action."""
     pushed_namespace = perform_substitutions(context, self.namespace)
     previous_namespace = context.launch_configurations.get(
         'ros_namespace', '')
     namespace = pushed_namespace
     if not pushed_namespace.startswith('/'):
         namespace = previous_namespace + '/' + pushed_namespace
     namespace = namespace.rstrip('/')
     if namespace != '':
         try:
             validate_namespace(namespace)
         except Exception:
             raise SubstitutionFailure(
                 'The resulting namespace is invalid:'
                 " [previous_namespace='{}', pushed_namespace='{}']".format(
                     previous_namespace, pushed_namespace))
     context.launch_configurations['ros_namespace'] = namespace
Пример #10
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.__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
Пример #11
0
def is_key_name_valid(name):
    ns_and_name = name.rsplit('/', 1)
    if len(ns_and_name) != 2:
        print("The key name needs to start with '/'")
        return False
    node_ns = ns_and_name[0] if ns_and_name[0] else '/'
    node_name = ns_and_name[1]

    try:
        return (validate_namespace(node_ns) and validate_node_name(node_name))
    except (InvalidNamespaceException, InvalidNodeNameException) as e:
        print('{}'.format(e))
        return False
Пример #12
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))
Пример #13
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])
Пример #14
0
    def __init__(
            self,
            node_name: str,
            *,
            context: Context = None,
            cli_args: List[str] = None,
            namespace: str = None,
            use_global_arguments: bool = True,
            start_parameter_services: bool = True,
            initial_parameters: List[Parameter] = None,
            allow_undeclared_parameters: bool = False,
            automatically_declare_initial_parameters: bool = True) -> None:
        """
        Constructor.

        :param node_name: A name to give to this node. Validated by :func:`validate_node_name`.
        :param context: The context to be associated with, or ``None`` for the default global
            context.
        :param cli_args: A list of strings of command line args to be used only by this node.
        :param namespace: The namespace to which relative topic and service names will be prefixed.
            Validated by :func:`validate_namespace`.
        :param use_global_arguments: ``False`` if the node should ignore process-wide command line
            args.
        :param start_parameter_services: ``False`` if the node should not create parameter
            services.
        :param initial_parameters: A list of parameters to be set during node creation.
        :param allow_undeclared_parameters: True if undeclared parameters are allowed.
            This flag affects the behavior of parameter-related operations.
        :param automatically_declare_initial_parameters: True if initial parameters have to be
            declared upon node creation, false otherwise.
        """
        self.__handle = None
        self._context = get_default_context() if context is None else context
        self._parameters: dict = {}
        self.__publishers: List[Publisher] = []
        self.__subscriptions: List[Subscription] = []
        self.__clients: List[Client] = []
        self.__services: List[Service] = []
        self.__timers: List[WallTimer] = []
        self.__guards: List[GuardCondition] = []
        self.__waitables: List[Waitable] = []
        self._default_callback_group = MutuallyExclusiveCallbackGroup()
        self._parameters_callback = None
        self._allow_undeclared_parameters = allow_undeclared_parameters
        self._initial_parameters = {}
        self._descriptors = {}

        namespace = namespace or ''
        if not self._context.ok():
            raise NotInitializedException('cannot create node')
        try:
            self.__handle = Handle(
                _rclpy.rclpy_create_node(node_name, namespace,
                                         self._context.handle, cli_args,
                                         use_global_arguments))
        except ValueError:
            # these will raise more specific errors if the name or namespace is bad
            validate_node_name(node_name)
            # emulate what rcl_node_init() does to accept '' and relative namespaces
            if not namespace:
                namespace = '/'
            if not namespace.startswith('/'):
                namespace = '/' + namespace
            validate_namespace(namespace)
            # Should not get to this point
            raise RuntimeError('rclpy_create_node failed for unknown reason')
        with self.handle as capsule:
            self._logger = get_logger(
                _rclpy.rclpy_get_node_logger_name(capsule))

        # Clock that has support for ROS time.
        self._clock = ROSClock()
        self._time_source = TimeSource(node=self)
        self._time_source.attach_clock(self._clock)

        self.__executor_weakref = None

        self._parameter_event_publisher = self.create_publisher(
            ParameterEvent,
            'parameter_events',
            qos_profile=qos_profile_parameter_events)

        with self.handle as capsule:
            self._initial_parameters = _rclpy.rclpy_get_node_parameters(
                Parameter, capsule)
        # Combine parameters from params files with those from the node constructor and
        # use the set_parameters_atomically API so a parameter event is published.
        if initial_parameters is not None:
            self._initial_parameters.update(
                {p.name: p
                 for p in initial_parameters})

        if automatically_declare_initial_parameters:
            self._parameters.update(self._initial_parameters)
            self._descriptors.update(
                {p: ParameterDescriptor()
                 for p in self._parameters})

        if start_parameter_services:
            self._parameter_service = ParameterService(self)
Пример #15
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))
Пример #16
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('/')
         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:
         _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 = []
         for params in self.__parameters:
             if isinstance(params, dict):
                 param_file_path = self._create_params_file_from_dict(
                     context, params)
             else:
                 if isinstance(params, pathlib.Path):
                     param_file_path = str(params)
                 else:
                     param_file_path = perform_substitutions(
                         context,
                         normalize_to_list_of_substitutions(params))
             if not os.path.isfile(param_file_path):
                 _logger.warn(
                     '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))