Skip to content

Commit ad2477a

Browse files
committed
add split_arguments argument for ExecuteLocal
Signed-off-by: William Woodall <[email protected]>
1 parent 11c0b97 commit ad2477a

File tree

1 file changed

+45
-5
lines changed

1 file changed

+45
-5
lines changed

launch/launch/actions/execute_local.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import logging
2020
import os
2121
import platform
22+
import shlex
2223
import signal
2324
import traceback
2425
from typing import Any # noqa: F401
@@ -82,6 +83,8 @@ def __init__(
8283
*,
8384
process_description: Executable,
8485
shell: bool = False,
86+
split_arguments: SomeSubstitutionsType = LaunchConfiguration(
87+
'split_arguments', default=False),
8588
sigterm_timeout: SomeSubstitutionsType = LaunchConfiguration(
8689
'sigterm_timeout', default=5),
8790
sigkill_timeout: SomeSubstitutionsType = LaunchConfiguration(
@@ -150,6 +153,24 @@ def __init__(
150153
:param: process_description the `launch.descriptions.Executable` to execute
151154
as a local process
152155
:param: shell if True, a shell is used to execute the cmd
156+
:param: split_arguments if True, and shell=False, the arguments are
157+
split by whitespace as if parsed by a shell, e.g. like shlex.split()
158+
from Python's standard library.
159+
Useful if the arguments need to be split, e.g. if a substitution
160+
evaluates to multiple whitespace separated arguments but shell=True
161+
cannot be used.
162+
Usually it does not make sense to split the arguments if shell=True
163+
because the shell will split them again, e.g. the single
164+
substitution '--opt1 --opt2 "Hello world"' would become ['--opt1',
165+
'--opt2', '"Hello World"'] if split_arguments=True, and then become
166+
['--opt1', '--opt2', 'Hello', 'World'] because of shell=True, which
167+
is likely not what was originally intended.
168+
Therefore, when both shell=True and split_arguments=True, the
169+
arguments will not be split before executing, depending on the
170+
shell to split the arguments instead.
171+
If not explicitly passed, the LaunchConfiguration called
172+
'split_arguments' will be used as the default, and if that
173+
LaunchConfiguration is not set, the default will be False.
153174
:param: sigterm_timeout time until shutdown should escalate to SIGTERM,
154175
as a string or a list of strings and Substitutions to be resolved
155176
at runtime, defaults to the LaunchConfiguration called
@@ -188,6 +209,7 @@ def __init__(
188209
super().__init__(**kwargs)
189210
self.__process_description = process_description
190211
self.__shell = shell
212+
self.__split_arguments = normalize_to_list_of_substitutions(split_arguments)
191213
self.__sigterm_timeout = normalize_to_list_of_substitutions(sigterm_timeout)
192214
self.__sigkill_timeout = normalize_to_list_of_substitutions(sigkill_timeout)
193215
self.__emulate_tty = emulate_tty
@@ -234,6 +256,11 @@ def shell(self):
234256
"""Getter for shell."""
235257
return self.__shell
236258

259+
@property
260+
def split_arguments(self):
261+
"""Getter for split_arguments."""
262+
return self.__split_arguments
263+
237264
@property
238265
def emulate_tty(self):
239266
"""Getter for emulate_tty."""
@@ -547,14 +574,27 @@ async def __execute_process(self, context: LaunchContext) -> None:
547574
raise RuntimeError('process_event_args unexpectedly None')
548575

549576
cmd = process_event_args['cmd']
577+
if evaluate_condition_expression(context, self.__split_arguments):
578+
if self.__shell:
579+
self.__logger.debug(
580+
"Ignoring 'split_arguments=True' because 'shell=True'."
581+
)
582+
else:
583+
expanded_cmd = []
584+
for token in cmd:
585+
expanded_cmd.extend(shlex.split(token))
586+
cmd = expanded_cmd
550587
cwd = process_event_args['cwd']
551588
env = process_event_args['env']
552589
if self.__log_cmd:
553-
self.__logger.info("process details: cmd='{}', cwd='{}', custom_env?={}".format(
554-
' '.join(filter(lambda part: part.strip(), cmd)),
555-
cwd,
556-
'True' if env is not None else 'False'
557-
))
590+
self.__logger.info(
591+
"process details: cmd=['{}'], cwd='{}', shell='{}', custom_env?={}".format(
592+
"', ' ".join(cmd),
593+
cwd,
594+
'True' if self.__shell else 'False',
595+
'True' if env is not None else 'False',
596+
)
597+
)
558598

559599
emulate_tty = self.__emulate_tty
560600
if 'emulate_tty' in context.launch_configurations:

0 commit comments

Comments
 (0)