Improve trace of template conditions (#49101)
* Improve trace of template conditions * Refactor * Fix wait_template trace * Update tests
This commit is contained in:
parent
106dc4d28a
commit
ff5fbea1fb
6 changed files with 60 additions and 17 deletions
|
@ -96,6 +96,18 @@ def condition_trace_set_result(result: bool, **kwargs: Any) -> None:
|
||||||
node.set_result(result=result, **kwargs)
|
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
|
@contextmanager
|
||||||
def trace_condition(variables: TemplateVarsType) -> Generator:
|
def trace_condition(variables: TemplateVarsType) -> Generator:
|
||||||
"""Trace condition evaluation."""
|
"""Trace condition evaluation."""
|
||||||
|
@ -118,7 +130,7 @@ def trace_condition_function(condition: ConditionCheckerType) -> ConditionChecke
|
||||||
"""Trace condition."""
|
"""Trace condition."""
|
||||||
with trace_condition(variables):
|
with trace_condition(variables):
|
||||||
result = condition(hass, variables)
|
result = condition(hass, variables)
|
||||||
condition_trace_set_result(result)
|
condition_trace_update_result(result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
@ -644,15 +656,22 @@ def template(
|
||||||
|
|
||||||
|
|
||||||
def async_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:
|
) -> bool:
|
||||||
"""Test if template condition matches."""
|
"""Test if template condition matches."""
|
||||||
try:
|
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:
|
except TemplateError as ex:
|
||||||
raise ConditionErrorMessage("template", str(ex)) from 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(
|
def async_template_from_config(
|
||||||
|
|
|
@ -456,7 +456,7 @@ class _ScriptRun:
|
||||||
wait_template.hass = self._hass
|
wait_template.hass = self._hass
|
||||||
|
|
||||||
# check if condition already okay
|
# 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
|
self._variables["wait"]["completed"] = True
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,11 @@ class TraceElement:
|
||||||
"""Set result."""
|
"""Set result."""
|
||||||
self._result = {**kwargs}
|
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]:
|
def as_dict(self) -> dict[str, Any]:
|
||||||
"""Return dictionary version of this TraceElement."""
|
"""Return dictionary version of this TraceElement."""
|
||||||
result: dict[str, Any] = {"path": self.path, "timestamp": self._timestamp}
|
result: dict[str, Any] = {"path": self.path, "timestamp": self._timestamp}
|
||||||
|
|
|
@ -223,7 +223,8 @@ async def test_get_trace(
|
||||||
assert len(trace["trace"].get("condition/0", [])) == len(condition_results)
|
assert len(trace["trace"].get("condition/0", [])) == len(condition_results)
|
||||||
for idx, condition_result in enumerate(condition_results):
|
for idx, condition_result in enumerate(condition_results):
|
||||||
assert trace["trace"]["condition/0"][idx]["result"] == {
|
assert trace["trace"]["condition/0"][idx]["result"] == {
|
||||||
"result": condition_result
|
"result": condition_result,
|
||||||
|
"entities": [],
|
||||||
}
|
}
|
||||||
contexts[trace["context"]["id"]] = {
|
contexts[trace["context"]["id"]] = {
|
||||||
"run_id": trace["run_id"],
|
"run_id": trace["run_id"],
|
||||||
|
@ -261,7 +262,10 @@ async def test_get_trace(
|
||||||
trace = response["result"]
|
trace = response["result"]
|
||||||
assert set(trace["trace"]) == extra_trace_keys[2]
|
assert set(trace["trace"]) == extra_trace_keys[2]
|
||||||
assert len(trace["trace"]["condition/0"]) == 1
|
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["config"] == moon_config
|
||||||
assert trace["context"]
|
assert trace["context"]
|
||||||
assert "error" not in trace
|
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 "error" not in trace["trace"][f"{prefix}/0"][0]
|
||||||
assert trace["trace"][f"{prefix}/0"][0]["result"] == moon_action
|
assert trace["trace"][f"{prefix}/0"][0]["result"] == moon_action
|
||||||
assert len(trace["trace"]["condition/0"]) == 1
|
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["config"] == moon_config
|
||||||
assert trace["context"]
|
assert trace["context"]
|
||||||
assert "error" not in trace
|
assert "error" not in trace
|
||||||
|
|
|
@ -237,6 +237,14 @@ async def test_and_condition_with_template(hass):
|
||||||
|
|
||||||
hass.states.async_set("sensor.temperature", 120)
|
hass.states.async_set("sensor.temperature", 120)
|
||||||
assert not test(hass)
|
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)
|
hass.states.async_set("sensor.temperature", 105)
|
||||||
assert not test(hass)
|
assert not test(hass)
|
||||||
|
|
|
@ -1449,7 +1449,7 @@ async def test_condition_basic(hass, caplog):
|
||||||
{
|
{
|
||||||
"0": [{"result": {"event": "test_event", "event_data": {}}}],
|
"0": [{"result": {"event": "test_event", "event_data": {}}}],
|
||||||
"1": [{"result": {"result": True}}],
|
"1": [{"result": {"result": True}}],
|
||||||
"1/condition": [{"result": {"result": True}}],
|
"1/condition": [{"result": {"entities": ["test.entity"], "result": True}}],
|
||||||
"2": [{"result": {"event": "test_event", "event_data": {}}}],
|
"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": {}}}],
|
"0": [{"result": {"event": "test_event", "event_data": {}}}],
|
||||||
"1": [{"error_type": script._StopScript, "result": {"result": False}}],
|
"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",
|
expected_script_execution="aborted",
|
||||||
)
|
)
|
||||||
|
@ -1764,9 +1764,9 @@ async def test_repeat_var_in_condition(hass, condition):
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"0/repeat/while/0": [
|
"0/repeat/while/0": [
|
||||||
{"result": {"result": True}},
|
{"result": {"entities": [], "result": True}},
|
||||||
{"result": {"result": True}},
|
{"result": {"entities": [], "result": True}},
|
||||||
{"result": {"result": False}},
|
{"result": {"entities": [], "result": False}},
|
||||||
],
|
],
|
||||||
"0/repeat/sequence/0": [
|
"0/repeat/sequence/0": [
|
||||||
{"result": {"event": "test_event", "event_data": {}}}
|
{"result": {"event": "test_event", "event_data": {}}}
|
||||||
|
@ -1797,8 +1797,8 @@ async def test_repeat_var_in_condition(hass, condition):
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"0/repeat/until/0": [
|
"0/repeat/until/0": [
|
||||||
{"result": {"result": False}},
|
{"result": {"entities": [], "result": False}},
|
||||||
{"result": {"result": True}},
|
{"result": {"entities": [], "result": True}},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
assert_action_trace(expected_trace)
|
assert_action_trace(expected_trace)
|
||||||
|
@ -2058,10 +2058,14 @@ async def test_choose(hass, caplog, var, result):
|
||||||
expected_trace = {"0": [{"result": {"choice": expected_choice}}]}
|
expected_trace = {"0": [{"result": {"choice": expected_choice}}]}
|
||||||
if var >= 1:
|
if var >= 1:
|
||||||
expected_trace["0/choose/0"] = [{"result": {"result": 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:
|
if var >= 2:
|
||||||
expected_trace["0/choose/1"] = [{"result": {"result": 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:
|
if var == 1:
|
||||||
expected_trace["0/choose/0/sequence/0"] = [
|
expected_trace["0/choose/0/sequence/0"] = [
|
||||||
{"result": {"event": "test_event", "event_data": {"choice": "first"}}}
|
{"result": {"event": "test_event", "event_data": {"choice": "first"}}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue