Ensure all async_track_state_change_event callbacks run if one throws (#37179)

This commit is contained in:
J. Nick Koston 2020-06-27 19:48:27 -05:00 committed by GitHub
parent c1194c90cb
commit a63a11a11a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 18 additions and 1 deletions

View File

@ -1,6 +1,7 @@
"""Helpers for listening to events.""" """Helpers for listening to events."""
from datetime import datetime, timedelta from datetime import datetime, timedelta
import functools as ft import functools as ft
import logging
from typing import Any, Awaitable, Callable, Dict, Iterable, Optional, Union from typing import Any, Awaitable, Callable, Dict, Iterable, Optional, Union
import attr import attr
@ -24,6 +25,8 @@ from homeassistant.util.async_ import run_callback_threadsafe
TRACK_STATE_CHANGE_CALLBACKS = "track_state_change_callbacks" TRACK_STATE_CHANGE_CALLBACKS = "track_state_change_callbacks"
TRACK_STATE_CHANGE_LISTENER = "track_state_change_listener" TRACK_STATE_CHANGE_LISTENER = "track_state_change_listener"
_LOGGER = logging.getLogger(__name__)
# PyLint does not like the use of threaded_listener_factory # PyLint does not like the use of threaded_listener_factory
# pylint: disable=invalid-name # pylint: disable=invalid-name
@ -146,7 +149,12 @@ def async_track_state_change_event(
return return
for action in entity_callbacks[entity_id]: for action in entity_callbacks[entity_id]:
hass.async_run_job(action, event) try:
hass.async_run_job(action, event)
except Exception: # pylint: disable=broad-except
_LOGGER.exception(
"Error while processing state changed for %s", entity_id
)
hass.data[TRACK_STATE_CHANGE_LISTENER] = hass.bus.async_listen( hass.data[TRACK_STATE_CHANGE_LISTENER] = hass.bus.async_listen(
EVENT_STATE_CHANGED, _async_state_change_dispatcher EVENT_STATE_CHANGED, _async_state_change_dispatcher

View File

@ -183,12 +183,19 @@ async def test_async_track_state_change_event(hass):
multiple_entity_id_tracker.append((old_state, new_state)) multiple_entity_id_tracker.append((old_state, new_state))
@ha.callback
def callback_that_throws(event):
raise ValueError
unsub_single = async_track_state_change_event( unsub_single = async_track_state_change_event(
hass, ["light.Bowl"], single_run_callback hass, ["light.Bowl"], single_run_callback
) )
unsub_multi = async_track_state_change_event( unsub_multi = async_track_state_change_event(
hass, ["light.Bowl", "switch.kitchen"], multiple_run_callback hass, ["light.Bowl", "switch.kitchen"], multiple_run_callback
) )
unsub_throws = async_track_state_change_event(
hass, ["light.Bowl", "switch.kitchen"], callback_that_throws
)
# Adding state to state machine # Adding state to state machine
hass.states.async_set("light.Bowl", "on") hass.states.async_set("light.Bowl", "on")
@ -247,6 +254,7 @@ async def test_async_track_state_change_event(hass):
assert len(multiple_entity_id_tracker) == 7 assert len(multiple_entity_id_tracker) == 7
unsub_multi() unsub_multi()
unsub_throws()
async def test_track_template(hass): async def test_track_template(hass):
@ -429,6 +437,7 @@ async def test_track_same_state_simple_trigger_check_funct(hass):
# Adding state to state machine # Adding state to state machine
hass.states.async_set("light.Bowl", "on") hass.states.async_set("light.Bowl", "on")
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done()
assert len(callback_runs) == 0 assert len(callback_runs) == 0
assert check_func[-1][2].state == "on" assert check_func[-1][2].state == "on"
assert check_func[-1][0] == "light.bowl" assert check_func[-1][0] == "light.bowl"