From a1e42cac7a16363ace58a8ef809544a12a0a0524 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Tue, 8 Oct 2024 18:35:06 +0200 Subject: [PATCH] Fix merge_response template not mutate original object (#127865) * Fix merge_response template not mutate original object * Add comment --- homeassistant/helpers/template.py | 4 +++- tests/helpers/test_template.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 9f8eb628e63..6d56fe708d0 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -9,6 +9,7 @@ import collections.abc from collections.abc import Callable, Generator, Iterable from contextlib import AbstractContextManager from contextvars import ContextVar +from copy import deepcopy from datetime import date, datetime, time, timedelta from functools import cache, cached_property, lru_cache, partial, wraps import json @@ -2166,7 +2167,8 @@ def merge_response(value: ServiceResponse) -> list[Any]: is_single_list = False response_items: list = [] - for entity_id, entity_response in value.items(): # pylint: disable=too-many-nested-blocks + input_service_response = deepcopy(value) + for entity_id, entity_response in input_service_response.items(): # pylint: disable=too-many-nested-blocks if not isinstance(entity_response, dict): raise TypeError("Response is not a dictionary") for value_key, type_response in entity_response.items(): diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 339b372f137..9a594408465 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -6564,3 +6564,21 @@ def test_warn_no_hass(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) -> template.Template("blah", hass) assert message not in caplog.text caplog.clear() + + +async def test_merge_response_not_mutate_original_object( + hass: HomeAssistant, snapshot: SnapshotAssertion +) -> None: + """Test the merge_response does not mutate original service response value.""" + + value = '{"calendar.family": {"events": [{"summary": "An event"}]}' + _template = ( + "{% set calendar_response = " + value + "} %}" + "{{ merge_response(calendar_response) }}" + # We should be able to merge the same response again + # as the merge is working on a copy of the original object (response) + "{{ merge_response(calendar_response) }}" + ) + + tpl = template.Template(_template, hass) + assert tpl.async_render()