mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Use built-in Jinja min and max filters in templates (#60327)
* Use built-in Jinja min and max filters in templates * use built-in filter for global * lint * less generic name * more tests * even more tests
This commit is contained in:
parent
2d0aaeba6b
commit
04606f05a4
@ -24,7 +24,7 @@ from urllib.parse import urlencode as urllib_urlencode
|
|||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
from jinja2 import pass_context
|
from jinja2 import pass_context, pass_environment
|
||||||
from jinja2.sandbox import ImmutableSandboxedEnvironment
|
from jinja2.sandbox import ImmutableSandboxedEnvironment
|
||||||
from jinja2.utils import Namespace
|
from jinja2.utils import Namespace
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -1525,6 +1525,30 @@ def fail_when_undefined(value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def min_max_from_filter(builtin_filter: Any, name: str) -> Any:
|
||||||
|
"""
|
||||||
|
Convert a built-in min/max Jinja filter to a global function.
|
||||||
|
|
||||||
|
The parameters may be passed as an iterable or as separate arguments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pass_environment
|
||||||
|
@wraps(builtin_filter)
|
||||||
|
def wrapper(environment: jinja2.Environment, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
if len(args) == 0:
|
||||||
|
raise TypeError(f"{name} expected at least 1 argument, got 0")
|
||||||
|
|
||||||
|
if len(args) == 1:
|
||||||
|
if isinstance(args[0], Iterable):
|
||||||
|
return builtin_filter(environment, args[0], **kwargs)
|
||||||
|
|
||||||
|
raise TypeError(f"'{type(args[0]).__name__}' object is not iterable")
|
||||||
|
|
||||||
|
return builtin_filter(environment, args, **kwargs)
|
||||||
|
|
||||||
|
return pass_environment(wrapper)
|
||||||
|
|
||||||
|
|
||||||
def average(*args: Any) -> float:
|
def average(*args: Any) -> float:
|
||||||
"""
|
"""
|
||||||
Filter and function to calculate the arithmetic mean of an iterable or of two or more arguments.
|
Filter and function to calculate the arithmetic mean of an iterable or of two or more arguments.
|
||||||
@ -1865,8 +1889,6 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||||||
self.filters["from_json"] = from_json
|
self.filters["from_json"] = from_json
|
||||||
self.filters["is_defined"] = fail_when_undefined
|
self.filters["is_defined"] = fail_when_undefined
|
||||||
self.filters["average"] = average
|
self.filters["average"] = average
|
||||||
self.filters["max"] = max
|
|
||||||
self.filters["min"] = min
|
|
||||||
self.filters["random"] = random_every_time
|
self.filters["random"] = random_every_time
|
||||||
self.filters["base64_encode"] = base64_encode
|
self.filters["base64_encode"] = base64_encode
|
||||||
self.filters["base64_decode"] = base64_decode
|
self.filters["base64_decode"] = base64_decode
|
||||||
@ -1909,8 +1931,8 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||||||
self.globals["strptime"] = strptime
|
self.globals["strptime"] = strptime
|
||||||
self.globals["urlencode"] = urlencode
|
self.globals["urlencode"] = urlencode
|
||||||
self.globals["average"] = average
|
self.globals["average"] = average
|
||||||
self.globals["max"] = max
|
self.globals["max"] = min_max_from_filter(self.filters["max"], "max")
|
||||||
self.globals["min"] = min
|
self.globals["min"] = min_max_from_filter(self.filters["min"], "min")
|
||||||
self.globals["is_number"] = is_number
|
self.globals["is_number"] = is_number
|
||||||
self.globals["int"] = forgiving_int
|
self.globals["int"] = forgiving_int
|
||||||
self.globals["pack"] = struct_pack
|
self.globals["pack"] = struct_pack
|
||||||
|
@ -835,6 +835,15 @@ def test_min(hass):
|
|||||||
assert template.Template("{{ min([1, 2, 3]) }}", hass).async_render() == 1
|
assert template.Template("{{ min([1, 2, 3]) }}", hass).async_render() == 1
|
||||||
assert template.Template("{{ min(1, 2, 3) }}", hass).async_render() == 1
|
assert template.Template("{{ min(1, 2, 3) }}", hass).async_render() == 1
|
||||||
|
|
||||||
|
with pytest.raises(TemplateError):
|
||||||
|
template.Template("{{ 1 | min }}", hass).async_render()
|
||||||
|
|
||||||
|
with pytest.raises(TemplateError):
|
||||||
|
template.Template("{{ min() }}", hass).async_render()
|
||||||
|
|
||||||
|
with pytest.raises(TemplateError):
|
||||||
|
template.Template("{{ min(1) }}", hass).async_render()
|
||||||
|
|
||||||
|
|
||||||
def test_max(hass):
|
def test_max(hass):
|
||||||
"""Test the max filter."""
|
"""Test the max filter."""
|
||||||
@ -842,6 +851,82 @@ def test_max(hass):
|
|||||||
assert template.Template("{{ max([1, 2, 3]) }}", hass).async_render() == 3
|
assert template.Template("{{ max([1, 2, 3]) }}", hass).async_render() == 3
|
||||||
assert template.Template("{{ max(1, 2, 3) }}", hass).async_render() == 3
|
assert template.Template("{{ max(1, 2, 3) }}", hass).async_render() == 3
|
||||||
|
|
||||||
|
with pytest.raises(TemplateError):
|
||||||
|
template.Template("{{ 1 | max }}", hass).async_render()
|
||||||
|
|
||||||
|
with pytest.raises(TemplateError):
|
||||||
|
template.Template("{{ max() }}", hass).async_render()
|
||||||
|
|
||||||
|
with pytest.raises(TemplateError):
|
||||||
|
template.Template("{{ max(1) }}", hass).async_render()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"attribute",
|
||||||
|
(
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_min_max_attribute(hass, attribute):
|
||||||
|
"""Test the min and max filters with attribute."""
|
||||||
|
hass.states.async_set(
|
||||||
|
"test.object",
|
||||||
|
"test",
|
||||||
|
{
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"a": 1,
|
||||||
|
"b": 2,
|
||||||
|
"c": 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a": 2,
|
||||||
|
"b": 1,
|
||||||
|
"c": 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a": 3,
|
||||||
|
"b": 3,
|
||||||
|
"c": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
template.Template(
|
||||||
|
"{{ (state_attr('test.object', 'objects') | min(attribute='%s'))['%s']}}"
|
||||||
|
% (attribute, attribute),
|
||||||
|
hass,
|
||||||
|
).async_render()
|
||||||
|
== 1
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
template.Template(
|
||||||
|
"{{ (min(state_attr('test.object', 'objects'), attribute='%s'))['%s']}}"
|
||||||
|
% (attribute, attribute),
|
||||||
|
hass,
|
||||||
|
).async_render()
|
||||||
|
== 1
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
template.Template(
|
||||||
|
"{{ (state_attr('test.object', 'objects') | max(attribute='%s'))['%s']}}"
|
||||||
|
% (attribute, attribute),
|
||||||
|
hass,
|
||||||
|
).async_render()
|
||||||
|
== 3
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
template.Template(
|
||||||
|
"{{ (max(state_attr('test.object', 'objects'), attribute='%s'))['%s']}}"
|
||||||
|
% (attribute, attribute),
|
||||||
|
hass,
|
||||||
|
).async_render()
|
||||||
|
== 3
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_ord(hass):
|
def test_ord(hass):
|
||||||
"""Test the ord filter."""
|
"""Test the ord filter."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user