From fc7dec59349acc3e2c8ad7f51795b344ac4e6608 Mon Sep 17 00:00:00 2001 From: Old-Ding <35417409+Old-Ding@users.noreply.github.com> Date: Sat, 11 Jul 2026 16:19:14 +0800 Subject: [PATCH 1/2] Preserve iterable executable arguments Materialize extra arguments once so command construction does not exhaust the iterable exposed by the public arguments property. Signed-off-by: Old-Ding <35417409+Old-Ding@users.noreply.github.com> --- launch/launch/descriptions/executable.py | 6 +++--- launch/test/launch/test_executable.py | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/launch/launch/descriptions/executable.py b/launch/launch/descriptions/executable.py index 73aaeeefc..5b4f666ef 100644 --- a/launch/launch/descriptions/executable.py +++ b/launch/launch/descriptions/executable.py @@ -74,9 +74,10 @@ def __init__( additional_env. :param arguments: list of extra arguments for the executable """ + self.__arguments = None if arguments is None else list(arguments) self.__cmd = [normalize_to_list_of_substitutions(x) for x in cmd] - self.__cmd += ([] if arguments is None - else [normalize_to_list_of_substitutions(x) for x in arguments]) + self.__cmd += ([] if self.__arguments is None + else [normalize_to_list_of_substitutions(x) for x in self.__arguments]) self.__prefix = normalize_to_list_of_substitutions( LaunchConfiguration('launch-prefix', default='') if prefix is None else prefix ) @@ -99,7 +100,6 @@ def __init__( self.__additional_env.append(( normalize_to_list_of_substitutions(key), normalize_to_list_of_substitutions(value))) - self.__arguments = arguments self.__final_cmd: Optional[List[str]] = None self.__final_cwd: Optional[str] = None self.__final_env: Optional[Dict[str, str]] = None diff --git a/launch/test/launch/test_executable.py b/launch/test/launch/test_executable.py index a62d33baf..8747cd9b2 100644 --- a/launch/test/launch/test_executable.py +++ b/launch/test/launch/test_executable.py @@ -45,6 +45,14 @@ def test_cmd_multiple_arguments_in_string(): assert all(a == b for a, b in zip(exe.final_cmd, ['ls', '-opt1', '-opt2', '-opt3'])) +def test_arguments_generator_is_preserved(): + arguments = (value for value in ['--flag', 'value']) + exe = Executable(cmd=['test'], arguments=arguments) + exe.prepare(LaunchContext(), None) + assert exe.final_cmd == ['test', '--flag', 'value'] + assert list(exe.arguments) == ['--flag', 'value'] + + def test_passthrough_properties(): name = 'name' cwd = 'cwd' From a9d660c68d5d5d54af48a455495119e16703d80d Mon Sep 17 00:00:00 2001 From: Old-Ding <35417409+Old-Ding@users.noreply.github.com> Date: Mon, 13 Jul 2026 04:27:38 +0800 Subject: [PATCH 2/2] Preserve nested iterable executable arguments Materialize each nested argument iterable before command construction so the public arguments property retains the values consumed by normalization. Signed-off-by: Old-Ding <35417409+Old-Ding@users.noreply.github.com> --- launch/launch/descriptions/executable.py | 6 +++++- launch/test/launch/test_executable.py | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/launch/launch/descriptions/executable.py b/launch/launch/descriptions/executable.py index 5b4f666ef..eb766f2e0 100644 --- a/launch/launch/descriptions/executable.py +++ b/launch/launch/descriptions/executable.py @@ -18,6 +18,7 @@ """Module for a description of an Executable.""" import os +import pathlib import re import shlex import threading @@ -74,7 +75,10 @@ def __init__( additional_env. :param arguments: list of extra arguments for the executable """ - self.__arguments = None if arguments is None else list(arguments) + self.__arguments = None if arguments is None else [ + argument if isinstance(argument, (str, pathlib.Path, Substitution)) else list(argument) + for argument in arguments + ] self.__cmd = [normalize_to_list_of_substitutions(x) for x in cmd] self.__cmd += ([] if self.__arguments is None else [normalize_to_list_of_substitutions(x) for x in self.__arguments]) diff --git a/launch/test/launch/test_executable.py b/launch/test/launch/test_executable.py index 8747cd9b2..0a9465f8a 100644 --- a/launch/test/launch/test_executable.py +++ b/launch/test/launch/test_executable.py @@ -53,6 +53,14 @@ def test_arguments_generator_is_preserved(): assert list(exe.arguments) == ['--flag', 'value'] +def test_nested_argument_generator_is_preserved(): + argument = (value for value in ['--', 'flag']) + exe = Executable(cmd=['test'], arguments=[argument]) + exe.prepare(LaunchContext(), None) + assert exe.final_cmd == ['test', '--flag'] + assert exe.arguments == [['--', 'flag']] + + def test_passthrough_properties(): name = 'name' cwd = 'cwd'