Skip to content

Commit 9aa8a78

Browse files
authored
Add template function: flatten (home-assistant#140157)
1 parent 62c025f commit 9aa8a78

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

homeassistant/helpers/template.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2765,6 +2765,25 @@ def typeof(value: Any) -> Any:
27652765
return value.__class__.__name__
27662766

27672767

2768+
def flatten(value: Iterable[Any], levels: int | None = None) -> list[Any]:
2769+
"""Flattens list of lists."""
2770+
if not isinstance(value, Iterable) or isinstance(value, str):
2771+
raise TypeError(f"flatten expected a list, got {type(value).__name__}")
2772+
2773+
flattened: list[Any] = []
2774+
for item in value:
2775+
if isinstance(item, Iterable) and not isinstance(item, str):
2776+
if levels is None:
2777+
flattened.extend(flatten(item))
2778+
elif levels >= 1:
2779+
flattened.extend(flatten(item, levels=(levels - 1)))
2780+
else:
2781+
flattened.append(item)
2782+
else:
2783+
flattened.append(item)
2784+
return flattened
2785+
2786+
27682787
class TemplateContextManager(AbstractContextManager):
27692788
"""Context manager to store template being parsed or rendered in a ContextVar."""
27702789

@@ -2967,6 +2986,7 @@ def __init__(
29672986
self.filters["contains"] = contains
29682987
self.filters["shuffle"] = shuffle
29692988
self.filters["typeof"] = typeof
2989+
self.filters["flatten"] = flatten
29702990
self.globals["log"] = logarithm
29712991
self.globals["sin"] = sine
29722992
self.globals["cos"] = cosine
@@ -3006,6 +3026,7 @@ def __init__(
30063026
self.globals["zip"] = zip
30073027
self.globals["shuffle"] = shuffle
30083028
self.globals["typeof"] = typeof
3029+
self.globals["flatten"] = flatten
30093030
self.tests["is_number"] = is_number
30103031
self.tests["list"] = _is_list
30113032
self.tests["set"] = _is_set

tests/helpers/test_template.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6751,3 +6751,40 @@ def test_typeof(hass: HomeAssistant) -> None:
67516751
template.Template("{{ typeof('Home Assistant') }}", hass).async_render()
67526752
== "str"
67536753
)
6754+
6755+
6756+
def test_flatten(hass: HomeAssistant) -> None:
6757+
"""Test the flatten function and filter."""
6758+
assert template.Template(
6759+
"{{ flatten([1, [2, [3]], 4, [5 , 6]]) }}", hass
6760+
).async_render() == [1, 2, 3, 4, 5, 6]
6761+
6762+
assert template.Template(
6763+
"{{ [1, [2, [3]], 4, [5 , 6]] | flatten }}", hass
6764+
).async_render() == [1, 2, 3, 4, 5, 6]
6765+
6766+
assert template.Template(
6767+
"{{ flatten([1, [2, [3]], 4, [5 , 6]], 1) }}", hass
6768+
).async_render() == [1, 2, [3], 4, 5, 6]
6769+
6770+
assert template.Template(
6771+
"{{ flatten([1, [2, [3]], 4, [5 , 6]], levels=1) }}", hass
6772+
).async_render() == [1, 2, [3], 4, 5, 6]
6773+
6774+
assert template.Template(
6775+
"{{ [1, [2, [3]], 4, [5 , 6]] | flatten(1) }}", hass
6776+
).async_render() == [1, 2, [3], 4, 5, 6]
6777+
6778+
assert template.Template(
6779+
"{{ [1, [2, [3]], 4, [5 , 6]] | flatten(levels=1) }}", hass
6780+
).async_render() == [1, 2, [3], 4, 5, 6]
6781+
6782+
assert template.Template("{{ flatten([]) }}", hass).async_render() == []
6783+
6784+
assert template.Template("{{ [] | flatten }}", hass).async_render() == []
6785+
6786+
with pytest.raises(TemplateError):
6787+
template.Template("{{ 'string' | flatten }}", hass).async_render()
6788+
6789+
with pytest.raises(TemplateError):
6790+
template.Template("{{ flatten() }}", hass).async_render()

0 commit comments

Comments
 (0)