Refactor state_reported listener setup to avoid merge in async_fire_internal (#117953)

* Refactor state_reported listener setup to avoid merge in async_fire_internal

Instead of merging the listeners in async_fire_internal, setup the listener for
state_changed at the same time so async_fire_internal can avoid having to copy
another list

* Refactor state_reported listener setup to avoid merge in async_fire_internal

Instead of merging the listeners in async_fire_internal, setup the listener for
state_changed at the same time so async_fire_internal can avoid having to copy
another list

* tweak

* tweak

* tweak

* tweak

* tweak
pull/117958/head
J. Nick Koston 2024-05-22 17:14:50 -10:00 committed by GitHub
parent 1f7245ecf2
commit e663d4f602
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 33 additions and 18 deletions

View File

@ -1500,7 +1500,6 @@ class EventBus:
This method must be run in the event loop.
"""
if self._debug:
_LOGGER.debug(
"Bus:Handling %s", _event_repr(event_type, origin, event_data)
@ -1511,17 +1510,9 @@ class EventBus:
match_all_listeners = self._match_all_listeners
else:
match_all_listeners = EMPTY_LIST
if event_type == EVENT_STATE_CHANGED:
aliased_listeners = self._listeners.get(EVENT_STATE_REPORTED, EMPTY_LIST)
else:
aliased_listeners = EMPTY_LIST
listeners = listeners + match_all_listeners + aliased_listeners
if not listeners:
return
event: Event[_DataT] | None = None
for job, event_filter in listeners:
for job, event_filter in listeners + match_all_listeners:
if event_filter is not None:
try:
if event_data is None or not event_filter(event_data):
@ -1599,18 +1590,32 @@ class EventBus:
if event_filter is not None and not is_callback_check_partial(event_filter):
raise HomeAssistantError(f"Event filter {event_filter} is not a callback")
filterable_job = (HassJob(listener, f"listen {event_type}"), event_filter)
if event_type == EVENT_STATE_REPORTED:
if not event_filter:
raise HomeAssistantError(
f"Event filter is required for event {event_type}"
)
return self._async_listen_filterable_job(
event_type,
(
HassJob(listener, f"listen {event_type}"),
event_filter,
),
)
# Special case for EVENT_STATE_REPORTED, we also want to listen to
# EVENT_STATE_CHANGED
self._listeners[EVENT_STATE_REPORTED].append(filterable_job)
self._listeners[EVENT_STATE_CHANGED].append(filterable_job)
return functools.partial(
self._async_remove_multiple_listeners,
(EVENT_STATE_REPORTED, EVENT_STATE_CHANGED),
filterable_job,
)
return self._async_listen_filterable_job(event_type, filterable_job)
@callback
def _async_remove_multiple_listeners(
self,
keys: Iterable[EventType[_DataT] | str],
filterable_job: _FilterableJobType[Any],
) -> None:
"""Remove multiple listeners for specific event_types."""
for key in keys:
self._async_remove_listener(key, filterable_job)
@callback
def _async_listen_filterable_job(
@ -1618,6 +1623,7 @@ class EventBus:
event_type: EventType[_DataT] | str,
filterable_job: _FilterableJobType[_DataT],
) -> CALLBACK_TYPE:
"""Listen for all events or events of a specific type."""
self._listeners[event_type].append(filterable_job)
return functools.partial(
self._async_remove_listener, event_type, filterable_job

View File

@ -3346,7 +3346,9 @@ async def test_statemachine_report_state(hass: HomeAssistant) -> None:
hass.states.async_set("light.bowl", "on", {})
state_changed_events = async_capture_events(hass, EVENT_STATE_CHANGED)
state_reported_events = []
hass.bus.async_listen(EVENT_STATE_REPORTED, listener, event_filter=mock_filter)
unsub = hass.bus.async_listen(
EVENT_STATE_REPORTED, listener, event_filter=mock_filter
)
hass.states.async_set("light.bowl", "on")
await hass.async_block_till_done()
@ -3368,6 +3370,13 @@ async def test_statemachine_report_state(hass: HomeAssistant) -> None:
assert len(state_changed_events) == 3
assert len(state_reported_events) == 4
unsub()
hass.states.async_set("light.bowl", "on")
await hass.async_block_till_done()
assert len(state_changed_events) == 4
assert len(state_reported_events) == 4
async def test_report_state_listener_restrictions(hass: HomeAssistant) -> None:
"""Test we enforce requirements for EVENT_STATE_REPORTED listeners."""