Report usage of extract_entities by custom components (#39185)

Block core usage usage of extract_entities

Suggest event.async_track_template_result instead.
This commit is contained in:
J. Nick Koston 2020-08-23 12:38:45 -05:00 committed by GitHub
parent 15c101e85d
commit c3ad493bb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 8 deletions

View file

@ -30,6 +30,7 @@ from homeassistant.const import (
from homeassistant.core import State, callback, split_entity_id, valid_entity_id
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, location as loc_helper
from homeassistant.helpers.frame import report
from homeassistant.helpers.typing import HomeAssistantType, TemplateVarsType
from homeassistant.loader import bind_hass
from homeassistant.util import convert, dt as dt_util, location as loc_util
@ -98,6 +99,11 @@ def extract_entities(
variables: TemplateVarsType = None,
) -> Union[str, List[str]]:
"""Extract all entities for state_changed listener from template string."""
report(
"called template.extract_entities. Please use event.async_track_template_result instead as it can accurately handle watching entities"
)
if template is None or not is_template_string(template):
return []

View file

@ -20,7 +20,14 @@ from homeassistant.helpers import template
import homeassistant.util.dt as dt_util
from homeassistant.util.unit_system import UnitSystem
from tests.async_mock import patch
from tests.async_mock import Mock, patch
@pytest.fixture()
def allow_extract_entities():
"""Allow extract entities."""
with patch("homeassistant.helpers.template.report"):
yield
def _set_up_units(hass):
@ -1758,7 +1765,7 @@ def test_closest_function_no_location_states(hass):
)
def test_extract_entities_none_exclude_stuff(hass):
def test_extract_entities_none_exclude_stuff(hass, allow_extract_entities):
"""Test extract entities function with none or exclude stuff."""
assert template.extract_entities(hass, None) == []
@ -1780,7 +1787,7 @@ def test_extract_entities_none_exclude_stuff(hass):
)
def test_extract_entities_no_match_entities(hass):
def test_extract_entities_no_match_entities(hass, allow_extract_entities):
"""Test extract entities function with none entities stuff."""
assert (
template.extract_entities(
@ -1920,7 +1927,7 @@ async def test_async_render_to_info_in_conditional(hass):
assert_result_info(info, "oink", ["sensor.xyz", "sensor.pig"], [])
async def test_extract_entities_match_entities(hass):
async def test_extract_entities_match_entities(hass, allow_extract_entities):
"""Test extract entities function with entities stuff."""
assert (
template.extract_entities(
@ -2028,7 +2035,7 @@ states.sensor.pick_humidity.state ~ " %"
]
def test_extract_entities_with_variables(hass):
def test_extract_entities_with_variables(hass, allow_extract_entities):
"""Test extract entities function with variables and entities stuff."""
hass.states.async_set("input_boolean.switch", "on")
assert ["input_boolean.switch"] == template.extract_entities(
@ -2063,7 +2070,7 @@ def test_extract_entities_with_variables(hass):
)
def test_extract_entities_domain_states_inner(hass):
def test_extract_entities_domain_states_inner(hass, allow_extract_entities):
"""Test extract entities function by domain."""
hass.states.async_set("light.switch", "on")
hass.states.async_set("light.switch2", "on")
@ -2078,7 +2085,7 @@ def test_extract_entities_domain_states_inner(hass):
) == {"light.switch", "light.switch2", "light.switch3"}
def test_extract_entities_domain_states_outer(hass):
def test_extract_entities_domain_states_outer(hass, allow_extract_entities):
"""Test extract entities function by domain."""
hass.states.async_set("light.switch", "on")
hass.states.async_set("light.switch2", "on")
@ -2093,7 +2100,7 @@ def test_extract_entities_domain_states_outer(hass):
) == {"light.switch", "light.switch2", "light.switch3"}
def test_extract_entities_domain_states_outer_with_group(hass):
def test_extract_entities_domain_states_outer_with_group(hass, allow_extract_entities):
"""Test extract entities function by domain."""
hass.states.async_set("light.switch", "on")
hass.states.async_set("light.switch2", "on")
@ -2110,6 +2117,43 @@ def test_extract_entities_domain_states_outer_with_group(hass):
) == {"light.switch", "light.switch2", "light.switch3", "group.lights"}
def test_extract_entities_blocked_from_core_code(hass):
"""Test extract entities is blocked from core code."""
with pytest.raises(RuntimeError):
template.extract_entities(
hass, "{{ states.light }}", {},
)
def test_extract_entities_warns_and_logs_from_an_integration(hass, caplog):
"""Test extract entities works from a custom_components with a log message."""
correct_frame = Mock(
filename="/config/custom_components/burncpu/light.py",
lineno="23",
line="self.light.is_on",
)
with patch(
"homeassistant.helpers.frame.extract_stack",
return_value=[
Mock(
filename="/home/dev/homeassistant/core.py",
lineno="23",
line="do_something()",
),
correct_frame,
Mock(filename="/home/dev/mdns/lights.py", lineno="2", line="something()",),
],
):
template.extract_entities(
hass, "{{ states.light }}", {},
)
assert "custom_components/burncpu/light.py" in caplog.text
assert "23" in caplog.text
assert "self.light.is_on" in caplog.text
def test_jinja_namespace(hass):
"""Test Jinja's namespace command can be used."""
test_template = template.Template(