From f07e676c823784b815a4ac6150905defd40023bf Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 30 Nov 2021 15:01:43 +0100 Subject: [PATCH] Allow template int filter to render from a bytes based integer (#60452) * Allow template int to render bytes * re-triggering tests * Add warning when base !=10 and rendering bytes * re-trigger tests * Re-trigger tests * remove period * Update homeassistant/helpers/template.py Co-authored-by: Erik Montnemery * Fix logger syntax * remove parentheses Co-authored-by: Erik Montnemery --- homeassistant/helpers/template.py | 18 +++++++++++++-- tests/helpers/test_template.py | 37 +++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 62e9f3fab6a..3c7e1cb2952 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -1567,8 +1567,15 @@ def forgiving_float_filter(value, default=_SENTINEL): return default -def forgiving_int(value, default=_SENTINEL, base=10): +def forgiving_int(value, default=_SENTINEL, base=10, little_endian=False): """Try to convert value to an int, and warn if it fails.""" + if isinstance(value, bytes) and value: + if base != 10: + _LOGGER.warning( + "Template warning: 'int' got 'bytes' type input, ignoring base=%s parameter", + base, + ) + return convert_to_int(value, little_endian=little_endian) result = jinja2.filters.do_int(value, default=default, base=base) if result is _SENTINEL: warn_no_default("int", value, value) @@ -1576,8 +1583,15 @@ def forgiving_int(value, default=_SENTINEL, base=10): return result -def forgiving_int_filter(value, default=_SENTINEL, base=10): +def forgiving_int_filter(value, default=_SENTINEL, base=10, little_endian=False): """Try to convert value to an int, and warn if it fails.""" + if isinstance(value, bytes) and value: + if base != 10: + _LOGGER.warning( + "Template warning: 'int' got 'bytes' type input, ignoring base=%s parameter", + base, + ) + return convert_to_int(value, little_endian=little_endian) result = jinja2.filters.do_int(value, default=default, base=base) if result is _SENTINEL: warn_no_default("int", value, 0) diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index bd405acf597..40e390b2070 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -252,7 +252,7 @@ def test_float_filter(hass): assert render(hass, "{{ 'bad' | float(default=1) }}") == 1 -def test_int_filter(hass): +def test_int_filter(hass, caplog): """Test int filter.""" hass.states.async_set("sensor.temperature", "12.2") assert render(hass, "{{ states.sensor.temperature.state | int }}") == 12 @@ -265,8 +265,25 @@ def test_int_filter(hass): assert render(hass, "{{ 'bad' | int(1) }}") == 1 assert render(hass, "{{ 'bad' | int(default=1) }}") == 1 + # Test with bytes based integer + variables = {"value": b"\xde\xad\xbe\xef"} + assert (render(hass, "{{ value | int }}", variables=variables)) == 0xDEADBEEF + assert ( + render(hass, "{{ value | int(little_endian=True) }}", variables=variables) + == 0xEFBEADDE + ) -def test_int_function(hass): + # Test with base and base parameter set + assert ( + render(hass, "{{ value | int(base=16) }}", variables=variables) + ) == 0xDEADBEEF + assert ( + "Template warning: 'int' got 'bytes' type input, ignoring base=16 parameter" + in caplog.text + ) + + +def test_int_function(hass, caplog): """Test int filter.""" hass.states.async_set("sensor.temperature", "12.2") assert render(hass, "{{ int(states.sensor.temperature.state) }}") == 12 @@ -279,6 +296,22 @@ def test_int_function(hass): assert render(hass, "{{ int('bad', 1) }}") == 1 assert render(hass, "{{ int('bad', default=1) }}") == 1 + # Test with base and base parameter set + variables = {"value": b"\xde\xad\xbe\xef"} + assert (render(hass, "{{ int(value) }}", variables=variables)) == 0xDEADBEEF + assert ( + render(hass, "{{ int(value, little_endian=True) }}", variables=variables) + == 0xEFBEADDE + ) + + assert ( + render(hass, "{{ int(value, base=16) }}", variables=variables) + ) == 0xDEADBEEF + assert ( + "Template warning: 'int' got 'bytes' type input, ignoring base=16 parameter" + in caplog.text + ) + @pytest.mark.parametrize( "value, expected",