From e8714b9a6f6a1e1840a0aa6e25d9201de54ce956 Mon Sep 17 00:00:00 2001 From: Matthew Elwin <10161574+m-elwin@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:26:26 -0600 Subject: [PATCH] Let XML executables/nodes be "required" (like in ROS 1) (#751) * Let XML nodes be "required" Essentially on_exit="shutdown" is equivalent to ROS 1 required="true". This feature is implemented using the python launchfile on_exit mechanism. Right now "shutdown" is the only action accepted by on_exit, but theoretically more "on_exit" actions could be added later. Example: * Added tests for yaml Signed-off-by: Matthew Elwin Signed-off-by: Tim Clephas --- launch/launch/actions/execute_process.py | 12 +++++++++++- launch_xml/test/launch_xml/test_executable.py | 17 +++++++++++++++++ .../test/launch_yaml/test_executable.py | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/launch/launch/actions/execute_process.py b/launch/launch/actions/execute_process.py index a6d8b3677..39c4f12d3 100644 --- a/launch/launch/actions/execute_process.py +++ b/launch/launch/actions/execute_process.py @@ -22,7 +22,7 @@ from typing import Text from .execute_local import ExecuteLocal - +from .shutdown_action import Shutdown from ..descriptions import Executable from ..frontend import Entity from ..frontend import expose_action @@ -329,6 +329,16 @@ def parse( if name is not None: kwargs['name'] = parser.parse_substitution(name) + if 'on_exit' not in ignore: + on_exit = entity.get_attr('on_exit', optional=True) + if on_exit is not None: + if on_exit == 'shutdown': + kwargs['on_exit'] = [Shutdown()] + else: + raise ValueError( + 'Attribute on_exit of Entity node expected to be shutdown but got `{}`' + 'Other on_exit actions not yet supported'.format(on_exit)) + if 'prefix' not in ignore: prefix = entity.get_attr('launch-prefix', optional=True) if prefix is not None: diff --git a/launch_xml/test/launch_xml/test_executable.py b/launch_xml/test/launch_xml/test_executable.py index 9d57d251e..878556ea3 100644 --- a/launch_xml/test/launch_xml/test_executable.py +++ b/launch_xml/test/launch_xml/test_executable.py @@ -19,6 +19,7 @@ import textwrap from launch import LaunchService +from launch.actions import Shutdown from launch.frontend import Parser import pytest @@ -67,5 +68,21 @@ def test_executable_wrong_subtag(): assert 'whats_this' in str(excinfo.value) +def test_executable_on_exit(): + xml_file = \ + """\ + + + + """ + xml_file = textwrap.dedent(xml_file) + root_entity, parser = Parser.load(io.StringIO(xml_file)) + ld = parser.parse_description(root_entity) + executable = ld.entities[0] + sub_entities = executable.get_sub_entities() + assert len(sub_entities) == 1 + assert isinstance(sub_entities[0], Shutdown) + + if __name__ == '__main__': test_executable() diff --git a/launch_yaml/test/launch_yaml/test_executable.py b/launch_yaml/test/launch_yaml/test_executable.py index 2f3d537b2..a4e1a9bf2 100644 --- a/launch_yaml/test/launch_yaml/test_executable.py +++ b/launch_yaml/test/launch_yaml/test_executable.py @@ -18,6 +18,7 @@ import textwrap from launch import LaunchService +from launch.actions import Shutdown from launch.frontend import Parser @@ -64,5 +65,22 @@ def test_executable(): assert(0 == ls.run()) +def test_executable_on_exit(): + yaml_file = \ + """\ + launch: + - executable: + cmd: ls + on_exit: shutdown + """ + yaml_file = textwrap.dedent(yaml_file) + root_entity, parser = Parser.load(io.StringIO(yaml_file)) + ld = parser.parse_description(root_entity) + executable = ld.entities[0] + sub_entities = executable.get_sub_entities() + assert len(sub_entities) == 1 + assert isinstance(sub_entities[0], Shutdown) + + if __name__ == '__main__': test_executable()