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()