Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filtering mechanism for executable prefix application #522

Merged
merged 10 commits into from Aug 24, 2021
20 changes: 18 additions & 2 deletions launch/launch/actions/execute_process.py
Expand Up @@ -18,6 +18,7 @@
import io
import os
import platform
import re
import shlex
import signal
import threading
Expand Down Expand Up @@ -187,7 +188,10 @@ def __init__(
'emulate_tty' configuration does not represent a boolean.
:param: prefix a set of commands/arguments to preceed the cmd, used for
things like gdb/valgrind and defaults to the LaunchConfiguration
called 'launch-prefix'
called 'launch-prefix'. Note that a non-default prefix provided in
a launch file will override the prefix provided via the `launch-prefix`
launch configuration regardless of whether the `launch-prefix-filter` launch
configuration is provided.
:param: output configuration for process output logging. Defaults to 'log'
i.e. log both stdout and stderr to launch main log file and stderr to
the screen.
Expand Down Expand Up @@ -230,6 +234,9 @@ def __init__(
self.__prefix = normalize_to_list_of_substitutions(
LaunchConfiguration('launch-prefix', default='') if prefix is None else prefix
)
self.__prefix_filter = normalize_to_list_of_substitutions(
LaunchConfiguration('launch-prefix-filter', default='')
) if prefix is None else None
self.__output = os.environ.get('OVERRIDE_LAUNCH_PROCESS_OUTPUT', output)
self.__output_format = output_format

Expand Down Expand Up @@ -681,7 +688,16 @@ def __expand_substitutions(self, context):
cmd = [perform_substitutions(context, x) for x in self.__cmd]
name = os.path.basename(cmd[0]) if self.__name is None \
else perform_substitutions(context, self.__name)
cmd = shlex.split(perform_substitutions(context, self.__prefix)) + cmd

# Perform filtering for prefix application
should_apply_prefix = True # by default
if self.__prefix_filter is not None: # no prefix given on construction
prefix_filter = perform_substitutions(context, self.__prefix_filter)
# Apply if filter regex matches (empty regex matches all strings)
should_apply_prefix = re.match(prefix_filter, os.path.basename(cmd[0]))
if should_apply_prefix:
cmd = shlex.split(perform_substitutions(context, self.__prefix)) + cmd

with _global_process_counter_lock:
global _global_process_counter
_global_process_counter += 1
Expand Down
60 changes: 60 additions & 0 deletions launch/test/launch/test_execute_process.py
Expand Up @@ -14,13 +14,16 @@

"""Tests for the ExecuteProcess Action."""

import asyncio
import os
import platform
import signal
import sys

from launch import LaunchContext
from launch import LaunchDescription
from launch import LaunchService
from launch.actions import SetLaunchConfiguration
from launch.actions.emit_event import EmitEvent
from launch.actions.execute_process import ExecuteProcess
from launch.actions.opaque_function import OpaqueFunction
Expand Down Expand Up @@ -168,3 +171,60 @@ def generate_launch_description():
ls.include_launch_description(generate_launch_description())
assert 0 == ls.run()
assert expected_called_count == on_exit_callback.called_count


def test_execute_process_prefix_filter_match():
lc = LaunchContext()
lc._set_asyncio_loop(asyncio.get_event_loop())
SetLaunchConfiguration('launch-prefix', 'time').visit(lc)
assert len(lc.launch_configurations) == 1
SetLaunchConfiguration(
'launch-prefix-filter',
f'{os.path.basename(sys.executable)}').visit(lc)
assert len(lc.launch_configurations) == 2

test_process = ExecuteProcess(
cmd=[sys.executable, '-c', "print('action')"],
output='screen'
)

test_process.execute(lc)
assert 'time' in test_process.process_details['cmd']


def test_execute_process_prefix_filter_no_match():
lc = LaunchContext()
lc._set_asyncio_loop(asyncio.get_event_loop())
SetLaunchConfiguration('launch-prefix', 'time').visit(lc)
assert len(lc.launch_configurations) == 1
SetLaunchConfiguration(
'launch-prefix-filter', 'no-match').visit(lc)
assert len(lc.launch_configurations) == 2

test_process = ExecuteProcess(
cmd=[sys.executable, '-c', "print('action')"],
output='screen'
)

test_process.execute(lc)
assert 'time' not in test_process.process_details['cmd']
camm73 marked this conversation as resolved.
Show resolved Hide resolved


def test_execute_process_prefix_filter_override_in_launch_file():
lc = LaunchContext()
lc._set_asyncio_loop(asyncio.get_event_loop())
SetLaunchConfiguration('launch-prefix', 'time').visit(lc)
assert len(lc.launch_configurations) == 1
SetLaunchConfiguration(
'launch-prefix-filter', 'no-match').visit(lc)
assert len(lc.launch_configurations) == 2

test_process = ExecuteProcess(
prefix='echo',
cmd=[sys.executable, '-c', "print('action')"],
output='screen'
)

test_process.execute(lc)
assert 'echo' in test_process.process_details['cmd'] and \
'time' not in test_process.process_details['cmd']