Service config calls will no longer mutate original config (#3628)

pull/3641/head
Paulus Schoutsen 2016-09-30 23:26:15 -07:00
parent 9cf2ad0b55
commit ef0e018cbb
3 changed files with 34 additions and 27 deletions

View File

@ -62,22 +62,18 @@ def call_from_config(hass, config, blocking=False, variables=None,
domain, service_name = domain_service.split('.', 1) domain, service_name = domain_service.split('.', 1)
service_data = dict(config.get(CONF_SERVICE_DATA, {})) service_data = dict(config.get(CONF_SERVICE_DATA, {}))
def _data_template_creator(value):
"""Recursive template creator helper function."""
if isinstance(value, list):
for idx, element in enumerate(value):
value[idx] = _data_template_creator(element)
return value
if isinstance(value, dict):
for key, element in value.items():
value[key] = _data_template_creator(element)
return value
value.hass = hass
return value.render(variables)
if CONF_SERVICE_DATA_TEMPLATE in config: if CONF_SERVICE_DATA_TEMPLATE in config:
for key, value in config[CONF_SERVICE_DATA_TEMPLATE].items(): def _data_template_creator(value):
service_data[key] = _data_template_creator(value) """Recursive template creator helper function."""
if isinstance(value, list):
return [_data_template_creator(item) for item in value]
elif isinstance(value, dict):
return {key: _data_template_creator(item)
for key, item in value.items()}
value.hass = hass
return value.render(variables)
service_data.update(_data_template_creator(
config[CONF_SERVICE_DATA_TEMPLATE]))
if CONF_SERVICE_ENTITY_ID in config: if CONF_SERVICE_ENTITY_ID in config:
service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID] service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]

View File

@ -159,6 +159,12 @@ class Template(object):
return self._compiled return self._compiled
def __eq__(self, other):
"""Compare template with another."""
return (self.__class__ == other.__class__ and
self.template == other.template and
self.hass == other.hass)
class AllStates(object): class AllStates(object):
"""Class to expose all HA states as attributes.""" """Class to expose all HA states as attributes."""

View File

@ -1,4 +1,5 @@
"""Test service helpers.""" """Test service helpers."""
from copy import deepcopy
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
@ -6,7 +7,8 @@ from unittest.mock import patch
import homeassistant.components # noqa import homeassistant.components # noqa
from homeassistant import core as ha, loader from homeassistant import core as ha, loader
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID
from homeassistant.helpers import service from homeassistant.helpers import service, template
import homeassistant.helpers.config_validation as cv
from tests.common import get_test_home_assistant, mock_service from tests.common import get_test_home_assistant, mock_service
@ -97,22 +99,25 @@ class TestServiceHelpers(unittest.TestCase):
def test_not_mutate_input(self): def test_not_mutate_input(self):
"""Test for immutable input.""" """Test for immutable input."""
orig = { config = cv.SERVICE_SCHEMA({
'service': 'test_domain.test_service', 'service': 'test_domain.test_service',
'entity_id': 'hello.world, sensor.beer', 'entity_id': 'hello.world, sensor.beer',
'data': { 'data': {
'hello': 1, 'hello': 1,
}, },
} 'data_template': {
service.call_from_config(self.hass, orig) 'nested': {
self.hass.block_till_done() 'value': '{{ 1 + 1 }}'
self.assertEqual({ }
'service': 'test_domain.test_service', }
'entity_id': 'hello.world, sensor.beer', })
'data': { orig = deepcopy(config)
'hello': 1,
}, # Only change after call is each template getting hass attached
}, orig) template.attach(self.hass, orig)
service.call_from_config(self.hass, config, validate_config=False)
assert orig == config
@patch('homeassistant.helpers.service._LOGGER.error') @patch('homeassistant.helpers.service._LOGGER.error')
def test_fail_silently_if_no_service(self, mock_log): def test_fail_silently_if_no_service(self, mock_log):