Skip to content

Commit

Permalink
Add parameter substitution (ros2#297)
Browse files Browse the repository at this point in the history
* Add Parameter substitution

Signed-off-by: Kenji Miyake <kenji.miyake@tier4.jp>

* Fix bug of set_parameter.py

Signed-off-by: Kenji Miyake <kenji.miyake@tier4.jp>

* Add a test for SubstitutionFailure

Signed-off-by: Kenji Miyake <kenji.miyake@tier4.jp>
  • Loading branch information
kenji-miyake committed Feb 28, 2022
1 parent 835b40c commit 0331a2e
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 2 deletions.
4 changes: 2 additions & 2 deletions launch_ros/launch_ros/actions/set_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ def parse(cls, entity: Entity, parser: Parser):
@property
def name(self) -> ParameterName:
"""Getter for name."""
return self.__param_dict.keys()[0]
return list(self.__param_dict.keys())[0]

@property
def value(self) -> ParameterValue:
"""Getter for value."""
return self.__param_dict.values()[0]
return list(self.__param_dict.values())[0]

def execute(self, context: LaunchContext):
"""Execute the action."""
Expand Down
2 changes: 2 additions & 0 deletions launch_ros/launch_ros/substitutions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
from .find_package import FindPackage
from .find_package import FindPackagePrefix
from .find_package import FindPackageShare
from .parameter import Parameter


__all__ = [
'ExecutableInPackage',
'FindPackage',
'FindPackagePrefix',
'FindPackageShare',
'Parameter',
]
72 changes: 72 additions & 0 deletions launch_ros/launch_ros/substitutions/parameter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright 2022 Open Source Robotics Foundation, Inc.
#
# 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.

"""Module for the Parameter substitution."""

from typing import Iterable
from typing import List
from typing import Text

from launch.frontend import expose_substitution
from launch.launch_context import LaunchContext
from launch.some_substitutions_type import SomeSubstitutionsType
from launch.substitution import Substitution
from launch.substitutions.substitution_failure import SubstitutionFailure
from launch.utilities import normalize_to_list_of_substitutions
from launch.utilities import perform_substitutions


@expose_substitution('param')
class Parameter(Substitution):
"""
Substitution that tries to get a parameter that was set by SetParameter.
:raise: SubstitutionFailure when param is not found
"""

def __init__(
self,
name: SomeSubstitutionsType,
) -> None:
"""Create a Parameter substitution."""
super().__init__()
self.__name = normalize_to_list_of_substitutions(name)

@classmethod
def parse(cls, data: Iterable[SomeSubstitutionsType]):
"""Parse a Parameter substitution."""
if not data or len(data) != 1:
raise AttributeError('param substitutions expect 1 argument')
kwargs = {'name': data[0]}
return cls, kwargs

@property
def name(self) -> List[Substitution]:
"""Getter for name."""
return self.__name

def describe(self) -> Text:
"""Return a description of this substitution as a string."""
name_str = ' + '.join([sub.describe() for sub in self.name])
return '{}(name={})'.format(self.__class__.__name__, name_str)

def perform(self, context: LaunchContext) -> Text:
"""Perform the substitution."""
name = perform_substitutions(context, self.name)
params_container = context.launch_configurations.get('global_params', None)
for param in params_container:
if isinstance(param, tuple):
if param[0] == name:
return param[1]
raise SubstitutionFailure("parameter '{}' not found".format(name))
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright 2022 Open Source Robotics Foundation, Inc.
#
# 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.

import io
import textwrap

from launch import LaunchService
from launch.frontend import Parser
from launch.utilities import perform_substitutions


def test_parameter_substitution_yaml():
yaml_file = textwrap.dedent(
r"""
launch:
- set_parameter:
name: name
value: value
- let:
name: result
value: $(param name)
"""
)
with io.StringIO(yaml_file) as f:
check_parameter_substitution(f)


def test_parameter_substitution_xml():
xml_file = textwrap.dedent(
r"""
<launch>
<set_parameter name="name" value="value" />
<let name="result" value="$(param name)" />
</launch>
"""
)
with io.StringIO(xml_file) as f:
check_parameter_substitution(f)


def check_parameter_substitution(file):
root_entity, parser = Parser.load(file)
ld = parser.parse_description(root_entity)
ls = LaunchService()
ls.include_launch_description(ld)
assert 0 == ls.run()

def perform(substitution):
return perform_substitutions(ls.context, substitution)

set_parameter, let = ld.describe_sub_entities()
assert perform(set_parameter.name) == 'name'
assert perform(set_parameter.value) == 'value'
assert perform(let.name) == 'result'
assert perform(let.value) == 'value'
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2022 Open Source Robotics Foundation, Inc.
#
# 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.

"""Test for the Parameter substitutions."""

from launch import LaunchContext

from launch.substitutions.substitution_failure import SubstitutionFailure

from launch_ros.actions import SetParameter
from launch_ros.substitutions import Parameter

import pytest


def test_parameter_substitution():
context = LaunchContext()
SetParameter('name', 'value').execute(context)
assert Parameter('name').perform(context) == 'value'
with pytest.raises(SubstitutionFailure):
Parameter('name-invalid').perform(context)

0 comments on commit 0331a2e

Please sign in to comment.