diff --git a/nav2_common/nav2_common/__init__.py b/nav2_common/nav2_common/__init__.py index e69de29bb2d..7bd15ef0f91 100644 --- a/nav2_common/nav2_common/__init__.py +++ b/nav2_common/nav2_common/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) 2025 Open Navigation LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from . import launch + +__all__ = [ + 'launch', +] diff --git a/nav2_common/nav2_common/launch/has_node_params.py b/nav2_common/nav2_common/launch/has_node_params.py index 2544d7bdb35..9493b3b7193 100644 --- a/nav2_common/nav2_common/launch/has_node_params.py +++ b/nav2_common/nav2_common/launch/has_node_params.py @@ -12,10 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Sequence + import launch import yaml +@launch.frontend.expose_substitution('has-node-params') class HasNodeParams(launch.Substitution): """ Substitution that checks if a param file contains parameters for a node. @@ -41,6 +44,18 @@ def __init__( normalize_to_list_of_substitutions(source_file) self.__node_name = node_name + @classmethod + def parse(cls, data: Sequence[launch.SomeSubstitutionsType]): + num_args = len(data) + if num_args != 2: + raise TypeError( + f'has-node-params substitution takes 2 arguments, got {num_args}') + kwargs = { + 'source_file': data[0], + 'node_name': data[1], + } + return cls, kwargs + @property def name(self) -> list[launch.Substitution]: """Getter for name.""" diff --git a/nav2_common/nav2_common/launch/launch_config_as_bool.py b/nav2_common/nav2_common/launch/launch_config_as_bool.py index 3b880d69131..2ef63a8fd95 100644 --- a/nav2_common/nav2_common/launch/launch_config_as_bool.py +++ b/nav2_common/nav2_common/launch/launch_config_as_bool.py @@ -12,12 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Sequence + import launch from launch import LaunchContext from launch.substitutions import LaunchConfiguration from launch.utilities import perform_substitutions +@launch.frontend.expose_substitution('as-bool') class LaunchConfigAsBool(launch.Substitution): """ Converts a LaunchConfiguration value into a normalized boolean string: 'true' or 'false'. @@ -30,6 +33,16 @@ def __init__(self, name: str) -> None: super().__init__() self._config = LaunchConfiguration(name) + @classmethod + def parse(cls, data: Sequence[launch.SomeSubstitutionsType]): + if len(data) != 1: + raise TypeError(f'as-bool substitution takes 1 arg, got {len(data)}') + kwargs = { + 'name': data[0] + } + return cls, kwargs + + def perform(self, context: LaunchContext) -> str: value = perform_substitutions(context, [self._config]) if value.strip().lower() in ['true', '1', 'yes', 'on']: diff --git a/nav2_common/nav2_common/launch/replace_string.py b/nav2_common/nav2_common/launch/replace_string.py index 0a210faab6e..55ec1cea65d 100644 --- a/nav2_common/nav2_common/launch/replace_string.py +++ b/nav2_common/nav2_common/launch/replace_string.py @@ -13,11 +13,12 @@ # limitations under the License. import tempfile -from typing import IO, Optional +from typing import IO, Optional, Sequence import launch +@launch.frontend.expose_substitution('replace-string') class ReplaceString(launch.Substitution): """ Substitution that replaces strings on a given file. @@ -46,6 +47,10 @@ def __init__( ) self.__condition = condition + @classmethod + def parse(cls, data: Sequence[launch.SomeSubstitutionsType]): + raise NotImplementedError() + @property def name(self) -> list[launch.Substitution]: """Getter for name.""" diff --git a/nav2_common/nav2_common/launch/rewritten_yaml.py b/nav2_common/nav2_common/launch/rewritten_yaml.py index 2a80ecf6409..5086bee08c7 100644 --- a/nav2_common/nav2_common/launch/rewritten_yaml.py +++ b/nav2_common/nav2_common/launch/rewritten_yaml.py @@ -14,7 +14,7 @@ from collections.abc import Generator import tempfile -from typing import Optional, TypeAlias, Union +from typing import Optional, TypeAlias, Union, Sequence import launch import yaml @@ -35,6 +35,7 @@ def setValue(self, value: YamlValue) -> None: self.dictionary[self.dictKey] = value +@launch.frontend.expose_substitution('rewritten-yaml') class RewrittenYaml(launch.Substitution): """ Substitution that modifies the given YAML file. @@ -90,6 +91,10 @@ def __init__( if root_key is not None: self.__root_key = normalize_to_list_of_substitutions(root_key) + @classmethod + def parse(cls, data: Sequence[launch.SomeSubstitutionsType]): + raise NotImplementedError() + @property def name(self) -> list[launch.Substitution]: """Getter for name.""" diff --git a/nav2_common/setup.cfg b/nav2_common/setup.cfg new file mode 100644 index 00000000000..4fd51707925 --- /dev/null +++ b/nav2_common/setup.cfg @@ -0,0 +1,3 @@ +[options.entry_points] +launch.frontend.launch_extension: + nav2_common = nav2_common