Skip to content

Commit

Permalink
add GroupAction for conditionally including other actions and scoping (
Browse files Browse the repository at this point in the history
…ros2#133)

Signed-off-by: William Woodall <[email protected]>
  • Loading branch information
wjwwood authored Aug 28, 2018
1 parent b39645c commit fe6b296
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
2 changes: 2 additions & 0 deletions launch/launch/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from .emit_event import EmitEvent
from .execute_process import ExecuteProcess
from .group_action import GroupAction
from .include_launch_description import IncludeLaunchDescription
from .log_info import LogInfo
from .opaque_function import OpaqueFunction
Expand All @@ -28,6 +29,7 @@
__all__ = [
'EmitEvent',
'ExecuteProcess',
'GroupAction',
'IncludeLaunchDescription',
'LogInfo',
'OpaqueFunction',
Expand Down
64 changes: 64 additions & 0 deletions launch/launch/actions/group_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright 2018 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 GroupAction action."""

from typing import Dict
from typing import Iterable
from typing import List
from typing import Optional

from .pop_launch_configurations import PopLaunchConfigurations
from .push_launch_configurations import PushLaunchConfigurations
from .set_launch_configuration import SetLaunchConfiguration
from ..action import Action
from ..launch_context import LaunchContext
from ..some_substitutions_type import SomeSubstitutionsType


class GroupAction(Action):
"""
Action that yields other actions, optionally scoping launch configurations.
This action is used to nest other actions without including a separate
launch description, while also optionally having a condition (like all
other actions), scoping launch configurations, and/or declaring launch
configurations for just the group and its yielded actions.
"""

def __init__(
self,
actions: Iterable[Action],
*,
scoped: bool = True,
launch_configurations: Optional[Dict[SomeSubstitutionsType, SomeSubstitutionsType]] = None,
**left_over_kwargs
) -> None:
"""Constructor."""
super().__init__(**left_over_kwargs)
self.__actions = actions
self.__scoped = scoped
if launch_configurations is not None:
self.__launch_configurations = launch_configurations
else:
self.__launch_configurations = {}

def execute(self, context: LaunchContext) -> Optional[List[Action]]:
"""Execute the action."""
actions = [] # type: List[Action]
actions += [SetLaunchConfiguration(k, v) for k, v in self.__launch_configurations.items()]
actions += list(self.__actions)
if self.__scoped:
return [PushLaunchConfigurations(), *actions, PopLaunchConfigurations()]
return actions
103 changes: 103 additions & 0 deletions launch/test/launch/actions/test_group_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Copyright 2018 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.

"""Tests for the GroupAction action class."""

from launch import Action
from launch import LaunchContext
from launch.actions import GroupAction
from launch.actions import PopLaunchConfigurations
from launch.actions import PushLaunchConfigurations
from launch.actions import SetLaunchConfiguration


def test_group_action_constructors():
"""Test the constructors for the GroupAction class."""
GroupAction([])
GroupAction([Action()])
GroupAction([Action()], scoped=False)
GroupAction([Action()], scoped=False, launch_configurations={'foo': 'FOO'})


def test_group_action_execute():
"""Test the execute() of the the GroupAction class."""
lc1 = LaunchContext()

assert len(lc1.launch_configurations) == 0
assert len(GroupAction([], scoped=False).visit(lc1)) == 0
assert len(lc1.launch_configurations) == 0

assert len(lc1.launch_configurations) == 0
result = GroupAction([]).visit(lc1)
assert len(result) == 2 # push and pop actions, due to scope=True
assert isinstance(result[0], PushLaunchConfigurations)
assert isinstance(result[1], PopLaunchConfigurations)
for a in result:
a.visit(lc1)
assert len(lc1.launch_configurations) == 0

assert len(lc1.launch_configurations) == 0
result = GroupAction([], launch_configurations={'foo': 'FOO'}).visit(lc1)
assert len(result) == 3 # push, set 1 launch_configurations, and pop actions
assert isinstance(result[0], PushLaunchConfigurations)
assert isinstance(result[1], SetLaunchConfiguration)
assert isinstance(result[2], PopLaunchConfigurations)
for a in result:
a.visit(lc1)
assert len(lc1.launch_configurations) == 0

assert len(lc1.launch_configurations) == 0
result = GroupAction([], launch_configurations={'foo': 'FOO', 'bar': 'BAR'}).visit(lc1)
assert len(result) == 4 # push, set 2 launch_configurations, and pop actions
assert isinstance(result[0], PushLaunchConfigurations)
assert isinstance(result[1], SetLaunchConfiguration)
assert isinstance(result[2], SetLaunchConfiguration)
assert isinstance(result[3], PopLaunchConfigurations)
for a in result:
a.visit(lc1)
assert len(lc1.launch_configurations) == 0

assert len(lc1.launch_configurations) == 0
PushLaunchConfigurations().visit(lc1)
result = GroupAction([], scoped=False, launch_configurations={'foo': 'FOO'}).visit(lc1)
assert len(result) == 1 # set 1 launch_configurations
assert isinstance(result[0], SetLaunchConfiguration)
for a in result:
a.visit(lc1)
assert len(lc1.launch_configurations) == 1 # still set after group was included
PopLaunchConfigurations().visit(lc1)
assert len(lc1.launch_configurations) == 0

assert len(lc1.launch_configurations) == 0
PushLaunchConfigurations().visit(lc1)
result = GroupAction([Action()], scoped=False, launch_configurations={'foo': 'FOO'}).visit(lc1)
assert len(result) == 2 # set 1 launch_configurations, then the 1 included actions
assert isinstance(result[0], SetLaunchConfiguration)
assert isinstance(result[1], Action)
for a in result:
a.visit(lc1)
assert len(lc1.launch_configurations) == 1 # still set after group was included
PopLaunchConfigurations().visit(lc1)
assert len(lc1.launch_configurations) == 0

assert len(lc1.launch_configurations) == 0
result = GroupAction([Action()], launch_configurations={'foo': 'FOO'}).visit(lc1)
assert len(result) == 4 # push, set 1 launch_configurations, the 1 action, and pop actions
assert isinstance(result[0], PushLaunchConfigurations)
assert isinstance(result[1], SetLaunchConfiguration)
assert isinstance(result[2], Action)
assert isinstance(result[3], PopLaunchConfigurations)
for a in result:
a.visit(lc1)
assert len(lc1.launch_configurations) == 0

0 comments on commit fe6b296

Please sign in to comment.