1
mirror of https://github.com/home-assistant/core synced 2024-07-21 14:24:50 +02:00

Avoid looking up the callable type for HassJob when we already know it (#102962)

* Avoid looking up the callable type for HassJob when we already know it

When we connect the frontend we call async_listen with run_immediately MANY
times when we already know the job type (it will always be a callback). This
reduces the latency to get the frontend going

* missing coverage
This commit is contained in:
J. Nick Koston 2023-10-30 06:45:22 -05:00 committed by GitHub
parent 7dbe0c3a48
commit b3743937de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 8 deletions

View File

@ -282,11 +282,12 @@ class HassJob(Generic[_P, _R_co]):
name: str | None = None,
*,
cancel_on_shutdown: bool | None = None,
job_type: HassJobType | None = None,
) -> None:
"""Create a job object."""
self.target = target
self.name = name
self.job_type = _get_hassjob_callable_job_type(target)
self.job_type = job_type or _get_hassjob_callable_job_type(target)
self._cancel_on_shutdown = cancel_on_shutdown
@property
@ -1153,13 +1154,20 @@ class EventBus:
This method must be run in the event loop.
"""
job_type: HassJobType | None = None
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")
if run_immediately and not is_callback_check_partial(listener):
raise HomeAssistantError(f"Event listener {listener} is not a callback")
if run_immediately:
if not is_callback_check_partial(listener):
raise HomeAssistantError(f"Event listener {listener} is not a callback")
job_type = HassJobType.Callback
return self._async_listen_filterable_job(
event_type,
(HassJob(listener, f"listen {event_type}"), event_filter, run_immediately),
(
HassJob(listener, f"listen {event_type}", job_type=job_type),
event_filter,
run_immediately,
),
)
@callback
@ -1234,7 +1242,11 @@ class EventBus:
)
filterable_job = (
HassJob(_onetime_listener, f"onetime listen {event_type} {listener}"),
HassJob(
_onetime_listener,
f"onetime listen {event_type} {listener}",
job_type=HassJobType.Callback,
),
None,
False,
)

View File

@ -24,6 +24,7 @@ from homeassistant.const import (
from homeassistant.core import (
CALLBACK_TYPE,
HassJob,
HassJobType,
HomeAssistant,
State,
callback,
@ -1376,6 +1377,7 @@ def async_track_point_in_time(
utc_converter,
name=f"{job.name} UTC converter",
cancel_on_shutdown=job.cancel_on_shutdown,
job_type=HassJobType.Callback,
)
return async_track_point_in_utc_time(hass, track_job, point_in_time)
@ -1531,7 +1533,10 @@ def async_track_time_interval(
job_name = f"track time interval {interval} {action}"
interval_listener_job = HassJob(
interval_listener, job_name, cancel_on_shutdown=cancel_on_shutdown
interval_listener,
job_name,
cancel_on_shutdown=cancel_on_shutdown,
job_type=HassJobType.Callback,
)
remove = async_call_later(hass, interval_seconds, interval_listener_job)
@ -1703,6 +1708,7 @@ def async_track_utc_time_change(
pattern_time_change_listener_job = HassJob(
pattern_time_change_listener,
f"time change listener {hour}:{minute}:{second} {action}",
job_type=HassJobType.Callback,
)
time_listener = async_track_point_in_utc_time(
hass, pattern_time_change_listener_job, calculate_next(dt_util.utcnow())

View File

@ -16,7 +16,14 @@ import requests
from homeassistant import config_entries
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import CALLBACK_TYPE, Event, HassJob, HomeAssistant, callback
from homeassistant.core import (
CALLBACK_TYPE,
Event,
HassJob,
HassJobType,
HomeAssistant,
callback,
)
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
ConfigEntryError,
@ -104,7 +111,11 @@ class DataUpdateCoordinator(BaseDataUpdateCoordinatorProtocol, Generic[_DataT]):
job_name += f" {name}"
if entry := self.config_entry:
job_name += f" {entry.title} {entry.domain} {entry.entry_id}"
self._job = HassJob(self._handle_refresh_interval, job_name)
self._job = HassJob(
self._handle_refresh_interval,
job_name,
job_type=HassJobType.Coroutinefunction,
)
self._unsub_refresh: CALLBACK_TYPE | None = None
self._unsub_shutdown: CALLBACK_TYPE | None = None
self._request_refresh_task: asyncio.TimerHandle | None = None

View File

@ -816,6 +816,16 @@ async def test_eventbus_run_immediately(hass: HomeAssistant) -> None:
unsub()
async def test_eventbus_run_immediately_not_callback(hass: HomeAssistant) -> None:
"""Test we raise when passing a non-callback with run_immediately."""
def listener(event):
"""Mock listener."""
with pytest.raises(HomeAssistantError):
hass.bus.async_listen("test", listener, run_immediately=True)
async def test_eventbus_unsubscribe_listener(hass: HomeAssistant) -> None:
"""Test unsubscribe listener from returned function."""
calls = []
@ -2534,3 +2544,25 @@ def test_is_callback_check_partial():
assert HassJob(ha.callback(functools.partial(not_callback_func))).job_type == (
ha.HassJobType.Executor
)
def test_hassjob_passing_job_type():
"""Test passing the job type to HassJob when we already know it."""
@ha.callback
def callback_func():
pass
def not_callback_func():
pass
assert (
HassJob(callback_func, job_type=ha.HassJobType.Callback).job_type
== ha.HassJobType.Callback
)
# We should trust the job_type passed in
assert (
HassJob(not_callback_func, job_type=ha.HassJobType.Callback).job_type
== ha.HassJobType.Callback
)