Add bool template filter and function (#74068)

Co-authored-by: Erik <erik@montnemery.com>
This commit is contained in:
[pʲɵs] 2022-06-28 16:22:09 +02:00 committed by GitHub
parent ed7ea1423a
commit 0e9164b082
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 9 deletions

View File

@ -19,7 +19,7 @@ import re
import statistics
from struct import error as StructError, pack, unpack_from
import sys
from typing import Any, cast
from typing import Any, NoReturn, TypeVar, cast, overload
from urllib.parse import urlencode as urllib_urlencode
import weakref
@ -92,6 +92,8 @@ _COLLECTABLE_STATE_ATTRIBUTES = {
"name",
}
_T = TypeVar("_T")
ALL_STATES_RATE_LIMIT = timedelta(minutes=1)
DOMAIN_STATES_RATE_LIMIT = timedelta(seconds=1)
@ -946,6 +948,31 @@ def _resolve_state(
return None
@overload
def forgiving_boolean(value: Any) -> bool | object:
...
@overload
def forgiving_boolean(value: Any, default: _T) -> bool | _T:
...
def forgiving_boolean(
value: Any, default: _T | object = _SENTINEL
) -> bool | _T | object:
"""Try to convert value to a boolean."""
try:
# Import here, not at top-level to avoid circular import
from . import config_validation as cv # pylint: disable=import-outside-toplevel
return cv.boolean(value)
except vol.Invalid:
if default is _SENTINEL:
raise_no_default("bool", value)
return default
def result_as_boolean(template_result: Any | None) -> bool:
"""Convert the template result to a boolean.
@ -956,13 +983,7 @@ def result_as_boolean(template_result: Any | None) -> bool:
if template_result is None:
return False
try:
# Import here, not at top-level to avoid circular import
from . import config_validation as cv # pylint: disable=import-outside-toplevel
return cv.boolean(template_result)
except vol.Invalid:
return False
return forgiving_boolean(template_result, default=False)
def expand(hass: HomeAssistant, *args: Any) -> Iterable[State]:
@ -1368,7 +1389,7 @@ def utcnow(hass: HomeAssistant) -> datetime:
return dt_util.utcnow()
def raise_no_default(function, value):
def raise_no_default(function: str, value: Any) -> NoReturn:
"""Log warning if no default is specified."""
template, action = template_cv.get() or ("", "rendering or compiling")
raise ValueError(
@ -1981,6 +2002,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
self.filters["relative_time"] = relative_time
self.filters["slugify"] = slugify
self.filters["iif"] = iif
self.filters["bool"] = forgiving_boolean
self.globals["log"] = logarithm
self.globals["sin"] = sine
self.globals["cos"] = cosine
@ -2012,6 +2034,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
self.globals["unpack"] = struct_unpack
self.globals["slugify"] = slugify
self.globals["iif"] = iif
self.globals["bool"] = forgiving_boolean
self.tests["is_number"] = is_number
self.tests["match"] = regex_match
self.tests["search"] = regex_search

View File

@ -296,6 +296,34 @@ def test_int_function(hass):
assert render(hass, "{{ int('bad', default=1) }}") == 1
def test_bool_function(hass):
"""Test bool function."""
assert render(hass, "{{ bool(true) }}") is True
assert render(hass, "{{ bool(false) }}") is False
assert render(hass, "{{ bool('on') }}") is True
assert render(hass, "{{ bool('off') }}") is False
with pytest.raises(TemplateError):
render(hass, "{{ bool('unknown') }}")
with pytest.raises(TemplateError):
render(hass, "{{ bool(none) }}")
assert render(hass, "{{ bool('unavailable', none) }}") is None
assert render(hass, "{{ bool('unavailable', default=none) }}") is None
def test_bool_filter(hass):
"""Test bool filter."""
assert render(hass, "{{ true | bool }}") is True
assert render(hass, "{{ false | bool }}") is False
assert render(hass, "{{ 'on' | bool }}") is True
assert render(hass, "{{ 'off' | bool }}") is False
with pytest.raises(TemplateError):
render(hass, "{{ 'unknown' | bool }}")
with pytest.raises(TemplateError):
render(hass, "{{ none | bool }}")
assert render(hass, "{{ 'unavailable' | bool(none) }}") is None
assert render(hass, "{{ 'unavailable' | bool(default=none) }}") is None
@pytest.mark.parametrize(
"value, expected",
[