Fix inner callback decorators with partials (#102873)

This commit is contained in:
J. Nick Koston 2023-10-28 08:38:42 -05:00 committed by GitHub
parent 524e20536d
commit 009dc91b97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 21 deletions

View file

@ -209,6 +209,18 @@ def is_callback(func: Callable[..., Any]) -> bool:
return getattr(func, "_hass_callback", False) is True
def is_callback_check_partial(target: Callable[..., Any]) -> bool:
"""Check if function is safe to be called in the event loop.
This version of is_callback will also check if the target is a partial
and walk the chain of partials to find the original function.
"""
check_target = target
while isinstance(check_target, functools.partial):
check_target = check_target.func
return is_callback(check_target)
class _Hass(threading.local):
"""Container which makes a HomeAssistant instance available to the event loop."""
@ -1141,9 +1153,9 @@ class EventBus:
This method must be run in the event loop.
"""
if event_filter is not None and not is_callback(event_filter):
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(listener):
if run_immediately and not is_callback_check_partial(listener):
raise HomeAssistantError(f"Event listener {listener} is not a callback")
return self._async_listen_filterable_job(
event_type,