mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add bool template filter and function (#74068)
Co-authored-by: Erik <erik@montnemery.com>
This commit is contained in:
parent
ed7ea1423a
commit
0e9164b082
@ -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
|
||||
|
@ -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",
|
||||
[
|
||||
|
Loading…
x
Reference in New Issue
Block a user