diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index cb6d8fe81b85e..b96b3cc889c20 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -2019,6 +2019,11 @@ def add(value, amount, default=_SENTINEL): return default +def apply(value, fn, *args, **kwargs): + """Call the given callable with the provided arguments and keyword arguments.""" + return fn(value, *args, **kwargs) + + def logarithm(value, base=math.e, default=_SENTINEL): """Filter and function to get logarithm of the value with a specific base.""" try: @@ -3110,6 +3115,7 @@ def __init__( self.filters["acos"] = arc_cosine self.filters["add"] = add + self.filters["apply"] = apply self.filters["as_datetime"] = as_datetime self.filters["as_local"] = dt_util.as_local self.filters["as_timedelta"] = as_timedelta @@ -3169,6 +3175,7 @@ def __init__( self.filters["unpack"] = struct_unpack self.filters["version"] = version + self.tests["apply"] = apply self.tests["contains"] = contains self.tests["datetime"] = _is_datetime self.tests["is_number"] = is_number diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 43efe79e96fe8..d139aa35f2348 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -772,6 +772,62 @@ def test_add(hass: HomeAssistant) -> None: assert render(hass, "{{ 'no_number' | add(10, default=1) }}") == 1 +def test_apply(hass: HomeAssistant) -> None: + """Test apply.""" + assert template.Template( + """ + {%- macro add_foo(arg) -%} + {{arg}}foo + {%- endmacro -%} + {{ ["a", "b", "c"] | map('apply', add_foo) | list }} + """, + hass, + ).async_render() == ["afoo", "bfoo", "cfoo"] + + assert template.Template( + """ + {{ ['1', '2', '3', '4', '5'] | map('apply', int) | list }} + """, + hass, + ).async_render() == [1, 2, 3, 4, 5] + + +def test_apply_macro_with_arguments(hass: HomeAssistant) -> None: + """Test apply macro with positional, named, and mixed arguments.""" + # Test macro with positional arguments + assert template.Template( + """ + {%- macro greet(name, greeting) -%} + {{ greeting }}, {{ name }}! + {%- endmacro %} + {{ ["Alice", "Bob"] | map('apply', greet, "Hello") | list }} + """, + hass, + ).async_render() == ["Hello, Alice!", "Hello, Bob!"] + + # Test macro with named arguments + assert template.Template( + """ + {%- macro greet(name, greeting="Hi") -%} + {{ greeting }}, {{ name }}! + {%- endmacro %} + {{ ["Alice", "Bob"] | map('apply', greet, greeting="Hello") | list }} + """, + hass, + ).async_render() == ["Hello, Alice!", "Hello, Bob!"] + + # Test macro with mixed positional and named arguments + assert template.Template( + """ + {%- macro greet(name, separator, greeting="Hi") -%} + {{ greeting }}{{separator}} {{ name }}! + {%- endmacro %} + {{ ["Alice", "Bob"] | map('apply', greet, "," , greeting="Hey") | list }} + """, + hass, + ).async_render() == ["Hey, Alice!", "Hey, Bob!"] + + def test_logarithm(hass: HomeAssistant) -> None: """Test logarithm.""" tests = [