Pass hass instance when validating templates (#89242)

* Pass hass instance when validating templates

* Update tests

* Fix validating templates without hass

* Update service tests
This commit is contained in:
Erik Montnemery 2023-03-08 17:28:53 +01:00 committed by GitHub
parent b0013247ff
commit 18cb53a35c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 14 deletions

View file

@ -85,7 +85,12 @@ from homeassistant.const import (
WEEKDAYS,
UnitOfTemperature,
)
from homeassistant.core import split_entity_id, valid_entity_id
from homeassistant.core import (
HomeAssistant,
async_get_hass,
split_entity_id,
valid_entity_id,
)
from homeassistant.exceptions import TemplateError
from homeassistant.generated import currencies
from homeassistant.generated.countries import COUNTRIES
@ -597,7 +602,11 @@ def template(value: Any | None) -> template_helper.Template:
if isinstance(value, (list, dict, template_helper.Template)):
raise vol.Invalid("template value should be a string")
template_value = template_helper.Template(str(value))
hass: HomeAssistant | None = None
with contextlib.suppress(LookupError):
hass = async_get_hass()
template_value = template_helper.Template(str(value), hass)
try:
template_value.ensure_valid()
@ -615,7 +624,12 @@ def dynamic_template(value: Any | None) -> template_helper.Template:
if not template_helper.is_template_string(str(value)):
raise vol.Invalid("template value does not contain a dynamic template")
template_value = template_helper.Template(str(value))
hass: HomeAssistant | None = None
with contextlib.suppress(LookupError):
hass = async_get_hass()
template_value = template_helper.Template(str(value), hass)
try:
template_value.ensure_valid()
return template_value

View file

@ -561,11 +561,16 @@ def test_x10_address() -> None:
schema("C11")
def test_template() -> None:
def test_template(hass: HomeAssistant) -> None:
"""Test template validator."""
schema = vol.Schema(cv.template)
for value in (None, "{{ partial_print }", "{% if True %}Hello", ["test"]):
for value in (
None,
"{{ partial_print }",
"{% if True %}Hello",
["test"],
):
with pytest.raises(vol.Invalid):
schema(value)
@ -574,12 +579,43 @@ def test_template() -> None:
"Hello",
"{{ beer }}",
"{% if 1 == 1 %}Hello{% else %}World{% endif %}",
# Function added as an extension by Home Assistant
"{{ expand('group.foo')|map(attribute='entity_id')|list }}",
# Filter added as an extension by Home Assistant
"{{ ['group.foo']|expand|map(attribute='entity_id')|list }}",
)
for value in options:
schema(value)
def test_dynamic_template() -> None:
async def test_template_no_hass(hass: HomeAssistant) -> None:
"""Test template validator."""
schema = vol.Schema(cv.template)
for value in (
None,
"{{ partial_print }",
"{% if True %}Hello",
["test"],
# Filter added as an extension by Home Assistant
"{{ ['group.foo']|expand|map(attribute='entity_id')|list }}",
):
with pytest.raises(vol.Invalid):
await hass.async_add_executor_job(schema, value)
options = (
1,
"Hello",
"{{ beer }}",
"{% if 1 == 1 %}Hello{% else %}World{% endif %}",
# Function added as an extension by Home Assistant
"{{ expand('group.foo')|map(attribute='entity_id')|list }}",
)
for value in options:
await hass.async_add_executor_job(schema, value)
def test_dynamic_template(hass: HomeAssistant) -> None:
"""Test dynamic template validator."""
schema = vol.Schema(cv.dynamic_template)
@ -597,11 +633,42 @@ def test_dynamic_template() -> None:
options = (
"{{ beer }}",
"{% if 1 == 1 %}Hello{% else %}World{% endif %}",
# Function added as an extension by Home Assistant
"{{ expand('group.foo')|map(attribute='entity_id')|list }}",
# Filter added as an extension by Home Assistant
"{{ ['group.foo']|expand|map(attribute='entity_id')|list }}",
)
for value in options:
schema(value)
async def test_dynamic_template_no_hass(hass: HomeAssistant) -> None:
"""Test dynamic template validator."""
schema = vol.Schema(cv.dynamic_template)
for value in (
None,
1,
"{{ partial_print }",
"{% if True %}Hello",
["test"],
"just a string",
# Filter added as an extension by Home Assistant
"{{ ['group.foo']|expand|map(attribute='entity_id')|list }}",
):
with pytest.raises(vol.Invalid):
await hass.async_add_executor_job(schema, value)
options = (
"{{ beer }}",
"{% if 1 == 1 %}Hello{% else %}World{% endif %}",
# Function added as an extension by Home Assistant
"{{ expand('group.foo')|map(attribute='entity_id')|list }}",
)
for value in options:
await hass.async_add_executor_job(schema, value)
def test_template_complex() -> None:
"""Test template_complex validator."""
schema = vol.Schema(cv.template_complex)

View file

@ -383,16 +383,18 @@ async def test_split_entity_string(hass: HomeAssistant):
async def test_not_mutate_input(hass: HomeAssistant):
"""Test for immutable input."""
async_mock_service(hass, "test_domain", "test_service")
config = cv.SERVICE_SCHEMA(
{
"service": "test_domain.test_service",
"entity_id": "hello.world, sensor.beer",
"data": {"hello": 1},
"data_template": {"nested": {"value": "{{ 1 + 1 }}"}},
}
)
config = {
"service": "test_domain.test_service",
"entity_id": "hello.world, sensor.beer",
"data": {"hello": 1},
"data_template": {"nested": {"value": "{{ 1 + 1 }}"}},
}
orig = deepcopy(config)
# Validate both the original and the copy
config = cv.SERVICE_SCHEMA(config)
orig = cv.SERVICE_SCHEMA(orig)
# Only change after call is each template getting hass attached
template.attach(hass, orig)