Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 19 additions & 16 deletions launch/launch/actions/execute_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,25 +377,28 @@ def parse(
)
kwargs['respawn_delay'] = respawn_delay

if 'sigkill_timeout' not in ignore:
sigkill_timeout = entity.get_attr('sigkill_timeout', data_type=float, optional=True)
if sigkill_timeout is not None:
if sigkill_timeout < 0.0:
raise ValueError(
'Attribute sigkill_timeout of Entity node expected to be '
'a non-negative value but got `{}`'.format(sigkill_timeout)
for timeout_name in ('sigkill_timeout', 'sigterm_timeout'):
if timeout_name in ignore:
continue
timeout = entity.get_attr(
timeout_name, data_type=float, optional=True, can_be_str=True)
if timeout is None:
continue
if isinstance(timeout, str):
parsed_timeout = parser.parse_if_substitutions(timeout)
if isinstance(parsed_timeout, str):
raise TypeError(
'Attribute {} of Entity node expected to be a float or substitution '
'but got `{}`'.format(timeout_name, parsed_timeout)
)
kwargs['sigkill_timeout'] = str(sigkill_timeout)

if 'sigterm_timeout' not in ignore:
sigterm_timeout = entity.get_attr('sigterm_timeout', data_type=float, optional=True)
if sigterm_timeout is not None:
if sigterm_timeout < 0.0:
kwargs[timeout_name] = parsed_timeout
else:
if timeout < 0.0:
raise ValueError(
'Attribute sigterm_timeout of Entity node expected to be '
'a non-negative value but got `{}`'.format(sigterm_timeout)
'Attribute {} of Entity node expected to be a non-negative value '
'but got `{}`'.format(timeout_name, timeout)
)
kwargs['sigterm_timeout'] = str(sigterm_timeout)
kwargs[timeout_name] = str(timeout)

if 'shell' not in ignore:
shell = entity.get_attr('shell', data_type=bool, optional=True)
Expand Down
26 changes: 26 additions & 0 deletions launch_xml/test/launch_xml/test_executable.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from pathlib import Path
import textwrap

from launch import LaunchContext
from launch import LaunchService
from launch.actions import Shutdown

Expand Down Expand Up @@ -85,5 +86,30 @@ def test_executable_on_exit():
assert isinstance(sub_entities[0], Shutdown)


def test_executable_timeout_substitutions():
xml_file = \
"""\
<launch>
<executable cmd="echo" sigkill_timeout="$(var timeout)" sigterm_timeout="$(var timeout)"/>
</launch>
""" # noqa, line too long
xml_file = textwrap.dedent(xml_file)
root_entity, parser = load_no_extensions(io.StringIO(xml_file))
ld = parser.parse_description(root_entity)
executable = ld.entities[0]
context = LaunchContext()
context.launch_configurations['timeout'] = '3.5'
assert executable.sigkill_timeout[0].perform(context) == '3.5'
assert executable.sigterm_timeout[0].perform(context) == '3.5'


@pytest.mark.parametrize('timeout_name', ('sigkill_timeout', 'sigterm_timeout'))
def test_executable_invalid_timeout(timeout_name):
xml_file = '<launch><executable cmd="echo" {}="invalid"/></launch>'.format(timeout_name)
root_entity, parser = load_no_extensions(io.StringIO(xml_file))
with pytest.raises(TypeError, match=timeout_name):
parser.parse_description(root_entity)


if __name__ == '__main__':
test_executable()
37 changes: 37 additions & 0 deletions launch_yaml/test/launch_yaml/test_executable.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
import io
import textwrap

from launch import LaunchContext
from launch import LaunchService
from launch.actions import Shutdown

from parser_no_extensions import load_no_extensions

import pytest


def test_executable():
"""Parse executable yaml example."""
Expand Down Expand Up @@ -82,5 +85,39 @@ def test_executable_on_exit():
assert isinstance(sub_entities[0], Shutdown)


def test_executable_timeout_substitutions():
yaml_file = \
"""\
launch:
- executable:
cmd: echo
sigkill_timeout: $(var timeout)
sigterm_timeout: $(var timeout)
"""
yaml_file = textwrap.dedent(yaml_file)
root_entity, parser = load_no_extensions(io.StringIO(yaml_file))
ld = parser.parse_description(root_entity)
executable = ld.entities[0]
context = LaunchContext()
context.launch_configurations['timeout'] = '3.5'
assert executable.sigkill_timeout[0].perform(context) == '3.5'
assert executable.sigterm_timeout[0].perform(context) == '3.5'


@pytest.mark.parametrize('timeout_name', ('sigkill_timeout', 'sigterm_timeout'))
def test_executable_invalid_timeout(timeout_name):
yaml_file = \
"""\
launch:
- executable:
cmd: echo
{}: invalid
""".format(timeout_name)
yaml_file = textwrap.dedent(yaml_file)
root_entity, parser = load_no_extensions(io.StringIO(yaml_file))
with pytest.raises(TypeError, match=timeout_name):
parser.parse_description(root_entity)


if __name__ == '__main__':
test_executable()