From 2f9d03d115db15103ecf66110ed7305bae069358 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 6 Mar 2021 12:57:21 +0100 Subject: [PATCH] Merge action and condition traces (#47373) * Merge action and condition traces * Update __init__.py * Add typing to AutomationTrace * Make trace_get prepare a new trace by default * Correct typing of trace_cv * Fix tests --- .../components/automation/__init__.py | 101 ++++++++------ homeassistant/helpers/condition.py | 114 ++-------------- homeassistant/helpers/script.py | 128 +++--------------- homeassistant/helpers/trace.py | 120 ++++++++++++---- tests/helpers/test_condition.py | 6 +- tests/helpers/test_script.py | 6 +- 6 files changed, 191 insertions(+), 284 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index f12a7398f7e..df7102effde 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -1,8 +1,20 @@ """Allow to set up simple automation rules via the config file.""" from collections import deque from contextlib import contextmanager +import datetime as dt import logging -from typing import Any, Awaitable, Callable, Dict, List, Optional, Set, Union, cast +from typing import ( + Any, + Awaitable, + Callable, + Deque, + Dict, + List, + Optional, + Set, + Union, + cast, +) import voluptuous as vol from voluptuous.humanize import humanize_error @@ -42,11 +54,6 @@ from homeassistant.exceptions import ( HomeAssistantError, ) from homeassistant.helpers import condition, extract_domain_configs, template -from homeassistant.helpers.condition import ( - condition_path, - condition_trace_clear, - condition_trace_get, -) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity_component import EntityComponent @@ -57,12 +64,10 @@ from homeassistant.helpers.script import ( CONF_MAX, CONF_MAX_EXCEEDED, Script, - action_path, - action_trace_clear, - action_trace_get, ) from homeassistant.helpers.script_variables import ScriptVariables from homeassistant.helpers.service import async_register_admin_service +from homeassistant.helpers.trace import TraceElement, trace_get, trace_path from homeassistant.helpers.trigger import async_initialize_triggers from homeassistant.helpers.typing import TemplateVarsType from homeassistant.loader import bind_hass @@ -235,44 +240,55 @@ async def async_setup(hass, config): class AutomationTrace: """Container for automation trace.""" - def __init__(self, unique_id, config, trigger, context, action_trace): + def __init__( + self, + unique_id: Optional[str], + config: Dict[str, Any], + trigger: Dict[str, Any], + context: Context, + ): """Container for automation trace.""" - self._action_trace = action_trace - self._condition_trace = None - self._config = config - self._context = context - self._error = None - self._state = "running" - self._timestamp_finish = None - self._timestamp_start = dt_util.utcnow() - self._trigger = trigger - self._unique_id = unique_id - self._variables = None + self._action_trace: Optional[Dict[str, Deque[TraceElement]]] = None + self._condition_trace: Optional[Dict[str, Deque[TraceElement]]] = None + self._config: Dict[str, Any] = config + self._context: Context = context + self._error: Optional[Exception] = None + self._state: str = "running" + self._timestamp_finish: Optional[dt.datetime] = None + self._timestamp_start: dt.datetime = dt_util.utcnow() + self._trigger: Dict[str, Any] = trigger + self._unique_id: Optional[str] = unique_id + self._variables: Optional[Dict[str, Any]] = None - def set_error(self, ex): + def set_action_trace(self, trace: Dict[str, Deque[TraceElement]]) -> None: + """Set action trace.""" + self._action_trace = trace + + def set_condition_trace(self, trace: Dict[str, Deque[TraceElement]]) -> None: + """Set condition trace.""" + self._condition_trace = trace + + def set_error(self, ex: Exception) -> None: """Set error.""" self._error = ex - def set_variables(self, variables): + def set_variables(self, variables: Dict[str, Any]) -> None: """Set variables.""" self._variables = variables - def set_condition_trace(self, condition_trace): - """Set condition trace.""" - self._condition_trace = condition_trace - - def finished(self): + def finished(self) -> None: """Set finish time.""" self._timestamp_finish = dt_util.utcnow() self._state = "stopped" - def as_dict(self): + def as_dict(self) -> Dict[str, Any]: """Return dictionary version of this AutomationTrace.""" action_traces = {} condition_traces = {} - for key, trace_list in self._action_trace.items(): - action_traces[key] = [item.as_dict() for item in trace_list] + if self._action_trace: + for key, trace_list in self._action_trace.items(): + action_traces[key] = [item.as_dict() for item in trace_list] if self._condition_trace: for key, trace_list in self._condition_trace.items(): @@ -300,11 +316,7 @@ class AutomationTrace: @contextmanager def trace_automation(hass, unique_id, config, trigger, context): """Trace action execution of automation with automation_id.""" - action_trace_clear() - action_trace = action_trace_get() - automation_trace = AutomationTrace( - unique_id, config, trigger, context, action_trace - ) + automation_trace = AutomationTrace(unique_id, config, trigger, context) if unique_id: if unique_id not in hass.data[DATA_AUTOMATION_TRACE]: @@ -325,7 +337,7 @@ def trace_automation(hass, unique_id, config, trigger, context): "Automation finished. Summary:\n\ttrigger: %s\n\tcondition: %s\n\taction: %s", automation_trace._trigger, # pylint: disable=protected-access automation_trace._condition_trace, # pylint: disable=protected-access - action_trace, + automation_trace._action_trace, # pylint: disable=protected-access ) @@ -510,6 +522,9 @@ class AutomationEntity(ToggleEntity, RestoreEntity): variables = run_variables automation_trace.set_variables(variables) + # Prepare tracing the evaluation of the automation's conditions + automation_trace.set_condition_trace(trace_get()) + if ( not skip_condition and self._cond_func is not None @@ -517,12 +532,12 @@ class AutomationEntity(ToggleEntity, RestoreEntity): ): self._logger.debug( "Conditions not met, aborting automation. Condition summary: %s", - condition_trace_get(), + trace_get(clear=False), ) - automation_trace.set_condition_trace(condition_trace_get()) return - automation_trace.set_condition_trace(condition_trace_get()) - condition_trace_clear() + + # Prepare tracing the execution of the automation's actions + automation_trace.set_action_trace(trace_get()) # Create a new context referring to the old context. parent_id = None if context is None else context.id @@ -543,7 +558,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity): ) try: - with action_path("action"): + with trace_path("action"): await self.action_script.async_run( variables, trigger_context, started_action ) @@ -763,7 +778,7 @@ async def _async_process_if(hass, name, config, p_config): errors = [] for index, check in enumerate(checks): try: - with condition_path(["condition", str(index)]): + with trace_path(["condition", str(index)]): if not check(hass, variables): return False except ConditionError as ex: diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index bc1ff21b9cc..71bbaa5f0f4 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -2,24 +2,12 @@ import asyncio from collections import deque from contextlib import contextmanager -from contextvars import ContextVar from datetime import datetime, timedelta import functools as ft import logging import re import sys -from typing import ( - Any, - Callable, - Container, - Dict, - Generator, - List, - Optional, - Set, - Union, - cast, -) +from typing import Any, Callable, Container, Generator, List, Optional, Set, Union, cast from homeassistant.components import zone as zone_cmp from homeassistant.components.device_automation import ( @@ -67,6 +55,9 @@ import homeassistant.util.dt as dt_util from .trace import ( TraceElement, trace_append_element, + trace_path, + trace_path_get, + trace_stack_cv, trace_stack_pop, trace_stack_push, trace_stack_top, @@ -84,79 +75,16 @@ INPUT_ENTITY_ID = re.compile( ConditionCheckerType = Callable[[HomeAssistant, TemplateVarsType], bool] -# Context variables for tracing -# Trace of condition being evaluated -condition_trace = ContextVar("condition_trace", default=None) -# Stack of TraceElements -condition_trace_stack: ContextVar[Optional[List[TraceElement]]] = ContextVar( - "condition_trace_stack", default=None -) -# Current location in config tree -condition_path_stack: ContextVar[Optional[List[str]]] = ContextVar( - "condition_path_stack", default=None -) - - -def condition_trace_stack_push(node: TraceElement) -> None: - """Push a TraceElement to the top of the trace stack.""" - trace_stack_push(condition_trace_stack, node) - - -def condition_trace_stack_pop() -> None: - """Remove the top element from the trace stack.""" - trace_stack_pop(condition_trace_stack) - - -def condition_trace_stack_top() -> Optional[TraceElement]: - """Return the element at the top of the trace stack.""" - return cast(Optional[TraceElement], trace_stack_top(condition_trace_stack)) - - -def condition_path_push(suffix: Union[str, List[str]]) -> int: - """Go deeper in the config tree.""" - if isinstance(suffix, str): - suffix = [suffix] - for node in suffix: - trace_stack_push(condition_path_stack, node) - return len(suffix) - - -def condition_path_pop(count: int) -> None: - """Go n levels up in the config tree.""" - for _ in range(count): - trace_stack_pop(condition_path_stack) - - -def condition_path_get() -> str: - """Return a string representing the current location in the config tree.""" - path = condition_path_stack.get() - if not path: - return "" - return "/".join(path) - - -def condition_trace_get() -> Optional[Dict[str, TraceElement]]: - """Return the trace of the condition that was evaluated.""" - return condition_trace.get() - - -def condition_trace_clear() -> None: - """Clear the condition trace.""" - condition_trace.set(None) - condition_trace_stack.set(None) - condition_path_stack.set(None) - - def condition_trace_append(variables: TemplateVarsType, path: str) -> TraceElement: """Append a TraceElement to trace[path].""" trace_element = TraceElement(variables) - trace_append_element(condition_trace, trace_element, path) + trace_append_element(trace_element, path) return trace_element def condition_trace_set_result(result: bool, **kwargs: Any) -> None: """Set the result of TraceElement at the top of the stack.""" - node = condition_trace_stack_top() + node = trace_stack_top(trace_stack_cv) # The condition function may be called directly, in which case tracing # is not setup @@ -169,25 +97,15 @@ def condition_trace_set_result(result: bool, **kwargs: Any) -> None: @contextmanager def trace_condition(variables: TemplateVarsType) -> Generator: """Trace condition evaluation.""" - trace_element = condition_trace_append(variables, condition_path_get()) - condition_trace_stack_push(trace_element) + trace_element = condition_trace_append(variables, trace_path_get()) + trace_stack_push(trace_stack_cv, trace_element) try: yield trace_element except Exception as ex: # pylint: disable=broad-except trace_element.set_error(ex) raise ex finally: - condition_trace_stack_pop() - - -@contextmanager -def condition_path(suffix: Union[str, List[str]]) -> Generator: - """Go deeper in the config tree.""" - count = condition_path_push(suffix) - try: - yield - finally: - condition_path_pop(count) + trace_stack_pop(trace_stack_cv) def trace_condition_function(condition: ConditionCheckerType) -> ConditionCheckerType: @@ -260,7 +178,7 @@ async def async_and_from_config( errors = [] for index, check in enumerate(checks): try: - with condition_path(["conditions", str(index)]): + with trace_path(["conditions", str(index)]): if not check(hass, variables): return False except ConditionError as ex: @@ -295,7 +213,7 @@ async def async_or_from_config( errors = [] for index, check in enumerate(checks): try: - with condition_path(["conditions", str(index)]): + with trace_path(["conditions", str(index)]): if check(hass, variables): return True except ConditionError as ex: @@ -330,7 +248,7 @@ async def async_not_from_config( errors = [] for index, check in enumerate(checks): try: - with condition_path(["conditions", str(index)]): + with trace_path(["conditions", str(index)]): if check(hass, variables): return False except ConditionError as ex: @@ -509,9 +427,7 @@ def async_numeric_state_from_config( errors = [] for index, entity_id in enumerate(entity_ids): try: - with condition_path(["entity_id", str(index)]), trace_condition( - variables - ): + with trace_path(["entity_id", str(index)]), trace_condition(variables): if not async_numeric_state( hass, entity_id, @@ -623,9 +539,7 @@ def state_from_config( errors = [] for index, entity_id in enumerate(entity_ids): try: - with condition_path(["entity_id", str(index)]), trace_condition( - variables - ): + with trace_path(["entity_id", str(index)]), trace_condition(variables): if not state(hass, entity_id, req_states, for_period, attribute): return False except ConditionError as ex: diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index 3925535d06b..8ee9b12bc1b 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -1,7 +1,6 @@ """Helpers to execute scripts.""" import asyncio from contextlib import contextmanager -from contextvars import ContextVar from datetime import datetime, timedelta from functools import partial import itertools @@ -65,12 +64,7 @@ from homeassistant.core import ( callback, ) from homeassistant.helpers import condition, config_validation as cv, service, template -from homeassistant.helpers.condition import ( - condition_path, - condition_trace_clear, - condition_trace_get, - trace_condition_function, -) +from homeassistant.helpers.condition import trace_condition_function from homeassistant.helpers.event import async_call_later, async_track_template from homeassistant.helpers.script_variables import ScriptVariables from homeassistant.helpers.trigger import ( @@ -84,9 +78,12 @@ from homeassistant.util.dt import utcnow from .trace import ( TraceElement, trace_append_element, + trace_path, + trace_path_get, + trace_set_result, + trace_stack_cv, trace_stack_pop, trace_stack_push, - trace_stack_top, ) # mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs @@ -125,111 +122,26 @@ _SHUTDOWN_MAX_WAIT = 60 ACTION_TRACE_NODE_MAX_LEN = 20 # Max the length of a trace node for repeated actions -action_trace = ContextVar("action_trace", default=None) -action_trace_stack = ContextVar("action_trace_stack", default=None) -action_path_stack = ContextVar("action_path_stack", default=None) - - -def action_trace_stack_push(node): - """Push a TraceElement to the top of the trace stack.""" - trace_stack_push(action_trace_stack, node) - - -def action_trace_stack_pop(): - """Remove the top element from the trace stack.""" - trace_stack_pop(action_trace_stack) - - -def action_trace_stack_top(): - """Return the element at the top of the trace stack.""" - return trace_stack_top(action_trace_stack) - - -def action_path_push(suffix): - """Go deeper in the config tree.""" - if isinstance(suffix, str): - suffix = [suffix] - for node in suffix: - trace_stack_push(action_path_stack, node) - return len(suffix) - - -def action_path_pop(count): - """Go n levels up in the config tree.""" - for _ in range(count): - trace_stack_pop(action_path_stack) - - -def action_path_get(): - """Return a string representing the current location in the config tree.""" - path = action_path_stack.get() - if not path: - return "" - return "/".join(path) - - -def action_trace_get(): - """Return the trace of the script that was executed.""" - return action_trace.get() - - -def action_trace_clear(): - """Clear the action trace.""" - action_trace.set({}) - action_trace_stack.set(None) - action_path_stack.set(None) - def action_trace_append(variables, path): """Append a TraceElement to trace[path].""" trace_element = TraceElement(variables) - trace_append_element(action_trace, trace_element, path, ACTION_TRACE_NODE_MAX_LEN) + trace_append_element(trace_element, path, ACTION_TRACE_NODE_MAX_LEN) return trace_element -def action_trace_set_result(**kwargs): - """Set the result of TraceElement at the top of the stack.""" - node = action_trace_stack_top() - node.set_result(**kwargs) - - -def action_trace_add_conditions(): - """Add the result of condition evaluation to the action trace.""" - condition_trace = condition_trace_get() - condition_trace_clear() - - if condition_trace is None: - return - - _action_path = action_path_get() - for cond_path, conditions in condition_trace.items(): - path = _action_path + "/" + cond_path if cond_path else _action_path - for cond in conditions: - trace_append_element(action_trace, cond, path) - - @contextmanager def trace_action(variables): """Trace action execution.""" - trace_element = action_trace_append(variables, action_path_get()) - action_trace_stack_push(trace_element) + trace_element = action_trace_append(variables, trace_path_get()) + trace_stack_push(trace_stack_cv, trace_element) try: yield trace_element except Exception as ex: # pylint: disable=broad-except trace_element.set_error(ex) raise ex finally: - action_trace_stack_pop() - - -@contextmanager -def action_path(suffix): - """Go deeper in the config tree.""" - count = action_path_push(suffix) - try: - yield - finally: - action_path_pop(count) + trace_stack_pop(trace_stack_cv) def make_script_schema(schema, default_script_mode, extra=vol.PREVENT_EXTRA): @@ -382,7 +294,7 @@ class _ScriptRun: self._finish() async def _async_step(self, log_exceptions): - with action_path(str(self._step)), trace_action(None): + with trace_path(str(self._step)), trace_action(None): try: handler = f"_async_{cv.determine_script_action(self._action)}_step" await getattr(self, handler)() @@ -638,15 +550,14 @@ class _ScriptRun: ) cond = await self._async_get_condition(self._action) try: - with condition_path("condition"): + with trace_path("condition"): check = cond(self._hass, self._variables) except exceptions.ConditionError as ex: _LOGGER.warning("Error in 'condition' evaluation:\n%s", ex) check = False self._log("Test condition %s: %s", self._script.last_action, check) - action_trace_set_result(result=check) - action_trace_add_conditions() + trace_set_result(result=check) if not check: raise _StopScript @@ -654,9 +565,9 @@ class _ScriptRun: @trace_condition_function def traced_test_conditions(hass, variables): try: - with condition_path("conditions"): + with trace_path("conditions"): for idx, cond in enumerate(conditions): - with condition_path(str(idx)): + with trace_path(str(idx)): if not cond(hass, variables): return False except exceptions.ConditionError as ex: @@ -666,7 +577,6 @@ class _ScriptRun: return True result = traced_test_conditions(self._hass, self._variables) - action_trace_add_conditions() return result async def _async_repeat_step(self): @@ -687,7 +597,7 @@ class _ScriptRun: async def async_run_sequence(iteration, extra_msg=""): self._log("Repeating %s: Iteration %i%s", description, iteration, extra_msg) - with action_path(str(self._step)): + with trace_path(str(self._step)): await self._async_run_script(script) if CONF_COUNT in repeat: @@ -754,18 +664,18 @@ class _ScriptRun: choose_data = await self._script._async_get_choose_data(self._step) for idx, (conditions, script) in enumerate(choose_data["choices"]): - with action_path(str(idx)): + with trace_path(str(idx)): try: if self._test_conditions(conditions, "choose"): - action_trace_set_result(choice=idx) + trace_set_result(choice=idx) await self._async_run_script(script) return except exceptions.ConditionError as ex: _LOGGER.warning("Error in 'choose' evaluation:\n%s", ex) if choose_data["default"]: - action_trace_set_result(choice="default") - with action_path("default"): + trace_set_result(choice="default") + with trace_path("default"): await self._async_run_script(choose_data["default"]) async def _async_wait_for_trigger_step(self): diff --git a/homeassistant/helpers/trace.py b/homeassistant/helpers/trace.py index 450faa0336f..8c5c181ea24 100644 --- a/homeassistant/helpers/trace.py +++ b/homeassistant/helpers/trace.py @@ -1,33 +1,13 @@ """Helpers for script and condition tracing.""" from collections import deque +from contextlib import contextmanager from contextvars import ContextVar -from typing import Any, Dict, Optional +from typing import Any, Deque, Dict, Generator, List, Optional, Union, cast from homeassistant.helpers.typing import TemplateVarsType import homeassistant.util.dt as dt_util -def trace_stack_push(trace_stack_var: ContextVar, node: Any) -> None: - """Push an element to the top of a trace stack.""" - trace_stack = trace_stack_var.get() - if trace_stack is None: - trace_stack = [] - trace_stack_var.set(trace_stack) - trace_stack.append(node) - - -def trace_stack_pop(trace_stack_var: ContextVar) -> None: - """Remove the top element from a trace stack.""" - trace_stack = trace_stack_var.get() - trace_stack.pop() - - -def trace_stack_top(trace_stack_var: ContextVar) -> Optional[Any]: - """Return the element at the top of a trace stack.""" - trace_stack = trace_stack_var.get() - return trace_stack[-1] if trace_stack else None - - class TraceElement: """Container for trace data.""" @@ -62,17 +42,105 @@ class TraceElement: return result +# Context variables for tracing +# Current trace +trace_cv: ContextVar[Optional[Dict[str, Deque[TraceElement]]]] = ContextVar( + "trace_cv", default=None +) +# Stack of TraceElements +trace_stack_cv: ContextVar[Optional[List[TraceElement]]] = ContextVar( + "trace_stack_cv", default=None +) +# Current location in config tree +trace_path_stack_cv: ContextVar[Optional[List[str]]] = ContextVar( + "trace_path_stack_cv", default=None +) + + +def trace_stack_push(trace_stack_var: ContextVar, node: Any) -> None: + """Push an element to the top of a trace stack.""" + trace_stack = trace_stack_var.get() + if trace_stack is None: + trace_stack = [] + trace_stack_var.set(trace_stack) + trace_stack.append(node) + + +def trace_stack_pop(trace_stack_var: ContextVar) -> None: + """Remove the top element from a trace stack.""" + trace_stack = trace_stack_var.get() + trace_stack.pop() + + +def trace_stack_top(trace_stack_var: ContextVar) -> Optional[Any]: + """Return the element at the top of a trace stack.""" + trace_stack = trace_stack_var.get() + return trace_stack[-1] if trace_stack else None + + +def trace_path_push(suffix: Union[str, List[str]]) -> int: + """Go deeper in the config tree.""" + if isinstance(suffix, str): + suffix = [suffix] + for node in suffix: + trace_stack_push(trace_path_stack_cv, node) + return len(suffix) + + +def trace_path_pop(count: int) -> None: + """Go n levels up in the config tree.""" + for _ in range(count): + trace_stack_pop(trace_path_stack_cv) + + +def trace_path_get() -> str: + """Return a string representing the current location in the config tree.""" + path = trace_path_stack_cv.get() + if not path: + return "" + return "/".join(path) + + def trace_append_element( - trace_var: ContextVar, trace_element: TraceElement, path: str, maxlen: Optional[int] = None, ) -> None: """Append a TraceElement to trace[path].""" - trace = trace_var.get() + trace = trace_cv.get() if trace is None: - trace_var.set({}) - trace = trace_var.get() + trace = {} + trace_cv.set(trace) if path not in trace: trace[path] = deque(maxlen=maxlen) trace[path].append(trace_element) + + +def trace_get(clear: bool = True) -> Optional[Dict[str, Deque[TraceElement]]]: + """Return the current trace.""" + if clear: + trace_clear() + return trace_cv.get() + + +def trace_clear() -> None: + """Clear the trace.""" + trace_cv.set({}) + trace_stack_cv.set(None) + trace_path_stack_cv.set(None) + + +def trace_set_result(**kwargs: Any) -> None: + """Set the result of TraceElement at the top of the stack.""" + node = cast(TraceElement, trace_stack_top(trace_stack_cv)) + node.set_result(**kwargs) + + +@contextmanager +def trace_path(suffix: Union[str, List[str]]) -> Generator: + """Go deeper in the config tree.""" + count = trace_path_push(suffix) + try: + yield + finally: + trace_path_pop(count) diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index cfed8ebbcf6..c1bf727bc0f 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -4,7 +4,7 @@ from unittest.mock import patch import pytest from homeassistant.exceptions import ConditionError, HomeAssistantError -from homeassistant.helpers import condition +from homeassistant.helpers import condition, trace from homeassistant.helpers.template import Template from homeassistant.setup import async_setup_component from homeassistant.util import dt @@ -25,8 +25,8 @@ def assert_element(trace_element, expected_element, path): def assert_condition_trace(expected): """Assert a trace condition sequence is as expected.""" - condition_trace = condition.condition_trace_get() - condition.condition_trace_clear() + condition_trace = trace.trace_get(clear=False) + trace.trace_clear() expected_trace_keys = list(expected.keys()) assert list(condition_trace.keys()) == expected_trace_keys for trace_key_index, key in enumerate(expected_trace_keys): diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index 04f922b685e..027254ee03e 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -17,7 +17,7 @@ from homeassistant import exceptions import homeassistant.components.scene as scene from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON from homeassistant.core import Context, CoreState, callback -from homeassistant.helpers import config_validation as cv, script +from homeassistant.helpers import config_validation as cv, script, trace from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -45,8 +45,8 @@ def assert_element(trace_element, expected_element, path): def assert_action_trace(expected): """Assert a trace condition sequence is as expected.""" - action_trace = script.action_trace_get() - script.action_trace_clear() + action_trace = trace.trace_get(clear=False) + trace.trace_clear() expected_trace_keys = list(expected.keys()) assert list(action_trace.keys()) == expected_trace_keys for trace_key_index, key in enumerate(expected_trace_keys):