From b697b3a54eb96709c6bf75d9aafe802ca576dcfd Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 8 Apr 2026 12:10:22 +0200 Subject: [PATCH] Extract version template function into a version Jinja2 extension (#167172) Co-authored-by: Joostlek --- homeassistant/helpers/template/__init__.py | 10 +----- .../helpers/template/extensions/__init__.py | 2 ++ .../helpers/template/extensions/version.py | 35 +++++++++++++++++++ .../template/extensions/test_version.py | 28 +++++++++++++++ tests/helpers/template/test_init.py | 18 ---------- 5 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 homeassistant/helpers/template/extensions/version.py create mode 100644 tests/helpers/template/extensions/test_version.py diff --git a/homeassistant/helpers/template/__init__.py b/homeassistant/helpers/template/__init__.py index 728f11bc365..5976cfe88e3 100644 --- a/homeassistant/helpers/template/__init__.py +++ b/homeassistant/helpers/template/__init__.py @@ -18,7 +18,6 @@ from types import CodeType from typing import TYPE_CHECKING, Any, Concatenate, Literal, NoReturn, Self, overload import weakref -from awesomeversion import AwesomeVersion import jinja2 from jinja2 import pass_context, pass_eval_context from jinja2.runtime import AsyncLoopContext, LoopContext @@ -1456,11 +1455,6 @@ def add(value, amount, default=_SENTINEL): return default -def version(value): - """Filter and function to get version object of the value.""" - return AwesomeVersion(value) - - def make_logging_undefined( strict: bool | None, log_fn: Callable[[int, str], None] | None ) -> type[jinja2.Undefined]: @@ -1611,13 +1605,11 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.add_extension( "homeassistant.helpers.template.extensions.TypeCastExtension" ) - - self.globals["version"] = version + self.add_extension("homeassistant.helpers.template.extensions.VersionExtension") self.filters["add"] = add self.filters["multiply"] = multiply self.filters["round"] = forgiving_round - self.filters["version"] = version if hass is None: return diff --git a/homeassistant/helpers/template/extensions/__init__.py b/homeassistant/helpers/template/extensions/__init__.py index c2c9755d06b..65792528adc 100644 --- a/homeassistant/helpers/template/extensions/__init__.py +++ b/homeassistant/helpers/template/extensions/__init__.py @@ -15,6 +15,7 @@ from .regex import RegexExtension from .serialization import SerializationExtension from .string import StringExtension from .type_cast import TypeCastExtension +from .version import VersionExtension __all__ = [ "AreaExtension", @@ -32,4 +33,5 @@ __all__ = [ "SerializationExtension", "StringExtension", "TypeCastExtension", + "VersionExtension", ] diff --git a/homeassistant/helpers/template/extensions/version.py b/homeassistant/helpers/template/extensions/version.py new file mode 100644 index 00000000000..e5b6133b530 --- /dev/null +++ b/homeassistant/helpers/template/extensions/version.py @@ -0,0 +1,35 @@ +"""Version functions for Home Assistant templates.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from awesomeversion import AwesomeVersion + +from .base import BaseTemplateExtension, TemplateFunction + +if TYPE_CHECKING: + from homeassistant.helpers.template import TemplateEnvironment + + +class VersionExtension(BaseTemplateExtension): + """Jinja2 extension for version functions.""" + + def __init__(self, environment: TemplateEnvironment) -> None: + """Initialize the version extension.""" + super().__init__( + environment, + functions=[ + TemplateFunction( + "version", + self.version, + as_global=True, + as_filter=True, + ), + ], + ) + + @staticmethod + def version(value: str) -> AwesomeVersion: + """Filter and function to get version object of the value.""" + return AwesomeVersion(value) diff --git a/tests/helpers/template/extensions/test_version.py b/tests/helpers/template/extensions/test_version.py new file mode 100644 index 00000000000..d03fbfbc299 --- /dev/null +++ b/tests/helpers/template/extensions/test_version.py @@ -0,0 +1,28 @@ +"""Test version functions for Home Assistant templates.""" + +from __future__ import annotations + +import pytest + +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import TemplateError + +from tests.helpers.template.helpers import render + + +def test_version(hass: HomeAssistant) -> None: + """Test version filter and function.""" + filter_result = render(hass, "{{ '2099.9.9' | version}}") + function_result = render(hass, "{{ version('2099.9.9')}}") + assert filter_result == function_result == "2099.9.9" + + filter_result = render(hass, "{{ '2099.9.9' | version < '2099.9.10' }}") + function_result = render(hass, "{{ version('2099.9.9') < '2099.9.10' }}") + assert filter_result is function_result is True + + filter_result = render(hass, "{{ '2099.9.9' | version == '2099.9.9' }}") + function_result = render(hass, "{{ version('2099.9.9') == '2099.9.9' }}") + assert filter_result is function_result is True + + with pytest.raises(TemplateError): + render(hass, "{{ version(None) < '2099.9.10' }}") diff --git a/tests/helpers/template/test_init.py b/tests/helpers/template/test_init.py index 15dd73126fa..da1460409d3 100644 --- a/tests/helpers/template/test_init.py +++ b/tests/helpers/template/test_init.py @@ -954,24 +954,6 @@ def test_timedelta(mock_is_safe, hass: HomeAssistant) -> None: assert result == "15 days" -def test_version(hass: HomeAssistant) -> None: - """Test version filter and function.""" - filter_result = render(hass, "{{ '2099.9.9' | version}}") - function_result = render(hass, "{{ version('2099.9.9')}}") - assert filter_result == function_result == "2099.9.9" - - filter_result = render(hass, "{{ '2099.9.9' | version < '2099.9.10' }}") - function_result = render(hass, "{{ version('2099.9.9') < '2099.9.10' }}") - assert filter_result is function_result is True - - filter_result = render(hass, "{{ '2099.9.9' | version == '2099.9.9' }}") - function_result = render(hass, "{{ version('2099.9.9') == '2099.9.9' }}") - assert filter_result is function_result is True - - with pytest.raises(TemplateError): - render(hass, "{{ version(None) < '2099.9.10' }}") - - def test_distance_function_with_1_state(hass: HomeAssistant) -> None: """Test distance function with 1 state.""" _set_up_units(hass)