From b520efb87ae422faba8d2fcddecd201865fe1df0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 24 Apr 2024 09:56:59 +0200 Subject: [PATCH] Small speed up to async_track_event (#116083) --- homeassistant/helpers/event.py | 36 +++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 7fae0976686..5cffe992c0d 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -3,11 +3,12 @@ from __future__ import annotations import asyncio +from collections import defaultdict from collections.abc import Callable, Coroutine, Iterable, Mapping, Sequence import copy from dataclasses import dataclass from datetime import datetime, timedelta -import functools as ft +from functools import partial, wraps import logging from random import randint import time @@ -161,7 +162,7 @@ def threaded_listener_factory( ) -> Callable[Concatenate[HomeAssistant, _P], CALLBACK_TYPE]: """Convert an async event helper to a threaded one.""" - @ft.wraps(async_factory) + @wraps(async_factory) def factory( hass: HomeAssistant, *args: _P.args, **kwargs: _P.kwargs ) -> CALLBACK_TYPE: @@ -170,7 +171,7 @@ def threaded_listener_factory( raise TypeError("First parameter needs to be a hass instance") async_remove = run_callback_threadsafe( - hass.loop, ft.partial(async_factory, hass, *args, **kwargs) + hass.loop, partial(async_factory, hass, *args, **kwargs) ).result() def remove() -> None: @@ -409,19 +410,16 @@ def _async_track_event( return _remove_empty_listener hass_data = hass.data - callbacks_key = tracker.callbacks_key - - callbacks: dict[str, list[HassJob[[Event[_TypedDictT]], Any]]] | None - if not (callbacks := hass_data.get(callbacks_key)): - callbacks = hass_data[callbacks_key] = {} + callbacks: defaultdict[str, list[HassJob[[Event[_TypedDictT]], Any]]] | None + if not (callbacks := hass_data.get(tracker.callbacks_key)): + callbacks = hass_data[tracker.callbacks_key] = defaultdict(list) listeners_key = tracker.listeners_key - - if listeners_key not in hass_data: - hass_data[listeners_key] = hass.bus.async_listen( + if tracker.listeners_key not in hass_data: + hass_data[tracker.listeners_key] = hass.bus.async_listen( tracker.event_type, - ft.partial(tracker.dispatcher_callable, hass, callbacks), - event_filter=ft.partial(tracker.filter_callable, hass, callbacks), + partial(tracker.dispatcher_callable, hass, callbacks), + event_filter=partial(tracker.filter_callable, hass, callbacks), ) job = HassJob(action, f"track {tracker.event_type} event {keys}", job_type=job_type) @@ -432,19 +430,13 @@ def _async_track_event( # here because this function gets called ~20000 times # during startup, and we want to avoid the overhead of # creating empty lists and throwing them away. - if callback_list := callbacks.get(keys): - callback_list.append(job) - else: - callbacks[keys] = [job] + callbacks[keys].append(job) keys = [keys] else: for key in keys: - if callback_list := callbacks.get(key): - callback_list.append(job) - else: - callbacks[key] = [job] + callbacks[key].append(job) - return ft.partial(_remove_listener, hass, listeners_key, keys, job, callbacks) + return partial(_remove_listener, hass, listeners_key, keys, job, callbacks) @callback