Improve trace of template conditions (#49101)

* Improve trace of template conditions

* Refactor

* Fix wait_template trace

* Update tests
This commit is contained in:
Erik Montnemery 2021-04-12 20:22:28 +02:00 committed by GitHub
parent 106dc4d28a
commit ff5fbea1fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 17 deletions

View File

@ -96,6 +96,18 @@ def condition_trace_set_result(result: bool, **kwargs: Any) -> None:
node.set_result(result=result, **kwargs)
def condition_trace_update_result(result: bool, **kwargs: Any) -> None:
"""Update the result of TraceElement at the top of the stack."""
node = trace_stack_top(trace_stack_cv)
# The condition function may be called directly, in which case tracing
# is not setup
if not node:
return
node.update_result(result=result, **kwargs)
@contextmanager
def trace_condition(variables: TemplateVarsType) -> Generator:
"""Trace condition evaluation."""
@ -118,7 +130,7 @@ def trace_condition_function(condition: ConditionCheckerType) -> ConditionChecke
"""Trace condition."""
with trace_condition(variables):
result = condition(hass, variables)
condition_trace_set_result(result)
condition_trace_update_result(result)
return result
return wrapper
@ -644,15 +656,22 @@ def template(
def async_template(
hass: HomeAssistant, value_template: Template, variables: TemplateVarsType = None
hass: HomeAssistant,
value_template: Template,
variables: TemplateVarsType = None,
trace_result: bool = True,
) -> bool:
"""Test if template condition matches."""
try:
value: str = value_template.async_render(variables, parse_result=False)
info = value_template.async_render_to_info(variables, parse_result=False)
value = info.result()
except TemplateError as ex:
raise ConditionErrorMessage("template", str(ex)) from ex
return value.lower() == "true"
result = value.lower() == "true"
if trace_result:
condition_trace_set_result(result, entities=list(info.entities))
return result
def async_template_from_config(

View File

@ -456,7 +456,7 @@ class _ScriptRun:
wait_template.hass = self._hass
# check if condition already okay
if condition.async_template(self._hass, wait_template, self._variables):
if condition.async_template(self._hass, wait_template, self._variables, False):
self._variables["wait"]["completed"] = True
return

View File

@ -51,6 +51,11 @@ class TraceElement:
"""Set result."""
self._result = {**kwargs}
def update_result(self, **kwargs: Any) -> None:
"""Set result."""
old_result = self._result or {}
self._result = {**old_result, **kwargs}
def as_dict(self) -> dict[str, Any]:
"""Return dictionary version of this TraceElement."""
result: dict[str, Any] = {"path": self.path, "timestamp": self._timestamp}

View File

@ -223,7 +223,8 @@ async def test_get_trace(
assert len(trace["trace"].get("condition/0", [])) == len(condition_results)
for idx, condition_result in enumerate(condition_results):
assert trace["trace"]["condition/0"][idx]["result"] == {
"result": condition_result
"result": condition_result,
"entities": [],
}
contexts[trace["context"]["id"]] = {
"run_id": trace["run_id"],
@ -261,7 +262,10 @@ async def test_get_trace(
trace = response["result"]
assert set(trace["trace"]) == extra_trace_keys[2]
assert len(trace["trace"]["condition/0"]) == 1
assert trace["trace"]["condition/0"][0]["result"] == {"result": False}
assert trace["trace"]["condition/0"][0]["result"] == {
"result": False,
"entities": [],
}
assert trace["config"] == moon_config
assert trace["context"]
assert "error" not in trace
@ -303,7 +307,10 @@ async def test_get_trace(
assert "error" not in trace["trace"][f"{prefix}/0"][0]
assert trace["trace"][f"{prefix}/0"][0]["result"] == moon_action
assert len(trace["trace"]["condition/0"]) == 1
assert trace["trace"]["condition/0"][0]["result"] == {"result": True}
assert trace["trace"]["condition/0"][0]["result"] == {
"result": True,
"entities": [],
}
assert trace["config"] == moon_config
assert trace["context"]
assert "error" not in trace

View File

@ -237,6 +237,14 @@ async def test_and_condition_with_template(hass):
hass.states.async_set("sensor.temperature", 120)
assert not test(hass)
assert_condition_trace(
{
"": [{"result": {"result": False}}],
"conditions/0": [
{"result": {"entities": ["sensor.temperature"], "result": False}}
],
}
)
hass.states.async_set("sensor.temperature", 105)
assert not test(hass)

View File

@ -1449,7 +1449,7 @@ async def test_condition_basic(hass, caplog):
{
"0": [{"result": {"event": "test_event", "event_data": {}}}],
"1": [{"result": {"result": True}}],
"1/condition": [{"result": {"result": True}}],
"1/condition": [{"result": {"entities": ["test.entity"], "result": True}}],
"2": [{"result": {"event": "test_event", "event_data": {}}}],
}
)
@ -1466,7 +1466,7 @@ async def test_condition_basic(hass, caplog):
{
"0": [{"result": {"event": "test_event", "event_data": {}}}],
"1": [{"error_type": script._StopScript, "result": {"result": False}}],
"1/condition": [{"result": {"result": False}}],
"1/condition": [{"result": {"entities": ["test.entity"], "result": False}}],
},
expected_script_execution="aborted",
)
@ -1764,9 +1764,9 @@ async def test_repeat_var_in_condition(hass, condition):
},
],
"0/repeat/while/0": [
{"result": {"result": True}},
{"result": {"result": True}},
{"result": {"result": False}},
{"result": {"entities": [], "result": True}},
{"result": {"entities": [], "result": True}},
{"result": {"entities": [], "result": False}},
],
"0/repeat/sequence/0": [
{"result": {"event": "test_event", "event_data": {}}}
@ -1797,8 +1797,8 @@ async def test_repeat_var_in_condition(hass, condition):
},
],
"0/repeat/until/0": [
{"result": {"result": False}},
{"result": {"result": True}},
{"result": {"entities": [], "result": False}},
{"result": {"entities": [], "result": True}},
],
}
assert_action_trace(expected_trace)
@ -2058,10 +2058,14 @@ async def test_choose(hass, caplog, var, result):
expected_trace = {"0": [{"result": {"choice": expected_choice}}]}
if var >= 1:
expected_trace["0/choose/0"] = [{"result": {"result": var == 1}}]
expected_trace["0/choose/0/conditions/0"] = [{"result": {"result": var == 1}}]
expected_trace["0/choose/0/conditions/0"] = [
{"result": {"entities": [], "result": var == 1}}
]
if var >= 2:
expected_trace["0/choose/1"] = [{"result": {"result": var == 2}}]
expected_trace["0/choose/1/conditions/0"] = [{"result": {"result": var == 2}}]
expected_trace["0/choose/1/conditions/0"] = [
{"result": {"entities": [], "result": var == 2}}
]
if var == 1:
expected_trace["0/choose/0/sequence/0"] = [
{"result": {"event": "test_event", "event_data": {"choice": "first"}}}