From 62192f2c00282353b434e39fb4512b924bd25c4d Mon Sep 17 00:00:00 2001 From: Dennis Lanov Date: Wed, 8 Jul 2026 12:14:36 -0500 Subject: [PATCH] Allow eval substitutions with spaced expressions Signed-off-by: Dennis Lanov --- .../launch/substitutions/python_expression.py | 19 ++++++++++++++++--- .../substitutions/test_python_expression.py | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/launch/launch/substitutions/python_expression.py b/launch/launch/substitutions/python_expression.py index f6afe19fd..11f0973b8 100644 --- a/launch/launch/substitutions/python_expression.py +++ b/launch/launch/substitutions/python_expression.py @@ -68,9 +68,22 @@ def __init__(self, expression: SomeSubstitutionsType, def parse(cls, data: Sequence[SomeSubstitutionsType] ) -> Tuple[Type['PythonExpression'], Dict[str, Any]]: """Parse `PythonExpression` substitution.""" - if len(data) < 1 or len(data) > 2: - raise TypeError('eval substitution expects 1 or 2 arguments') - kwargs = {'expression': data[0]} + if len(data) < 1: + raise TypeError('eval substitution expects at least 1 argument') + kwargs: Dict[str, Any] = {} + if len(data) <= 2: + kwargs['expression'] = data[0] + else: + # The expression is split into multiple arguments when it contains + # spaces, e.g. `$(eval 1 == 1)`. + # Join the arguments back with spaces to recover the expression. + from ..utilities import normalize_to_list_of_substitutions + expression: List[Substitution] = [] + for i, sub_expression in enumerate(data): + if i > 0: + expression += normalize_to_list_of_substitutions(' ') + expression += normalize_to_list_of_substitutions(sub_expression) + kwargs['expression'] = expression if len(data) == 2: # We get a text substitution from XML, # whose contents are comma-separated module names diff --git a/launch/test/launch/substitutions/test_python_expression.py b/launch/test/launch/substitutions/test_python_expression.py index 5ac78fdb0..709b81fad 100644 --- a/launch/test/launch/substitutions/test_python_expression.py +++ b/launch/test/launch/substitutions/test_python_expression.py @@ -137,3 +137,20 @@ def test_python_substitution_submodule(): # The expression should evaluate to True assert result + + +def test_python_substitution_parse_multiple_arguments(): + """Check that parse() joins an expression split across multiple arguments.""" + lc = LaunchContext() + + # The frontend splits an expression containing spaces, e.g. `$(eval 1 == 1)` + cls, kwargs = PythonExpression.parse(['1', '==', '1']) + subst = cls(**kwargs) + result = subst.perform(lc) + + assert result == 'True' + + # Test the describe() method + assert subst.describe() == ( + "PythonExpr('1' + ' ' + '==' + ' ' + '1', ['math'])" + )