1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 21:06:19 +00:00

Update service call return values and error handling (#94657)

* Update return signature of service calls

* Add timeout error handling in websocket api for service calls

* Update recorder tests to remove assertion on service call

* Remove timeout behavior and update callers that depend on it today

* Fix tests

* Add missing else

* await coro directly

* Fix more tests

* Update the intent task to use wait instead of timeout

* Remove script service call limits and limit constants

* Update tests that depend on service call limits

* Use wait instead of wait_for and add test

* Update homeassistant/helpers/intent.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Allen Porter
2023-06-16 07:01:40 -07:00
committed by GitHub
parent 950b25bf42
commit 12129e9d21
63 changed files with 388 additions and 434 deletions

View File

@@ -130,9 +130,6 @@ DOMAIN = "homeassistant"
# How long to wait to log tasks that are blocking
BLOCK_LOG_TIMEOUT = 60
# How long we wait for the result of a service call
SERVICE_CALL_LIMIT = 10 # seconds
class ConfigSource(StrEnum):
"""Source of core configuration."""
@@ -1807,7 +1804,6 @@ class ServiceRegistry:
service_data: dict[str, Any] | None = None,
blocking: bool = False,
context: Context | None = None,
limit: float | None = SERVICE_CALL_LIMIT,
target: dict[str, Any] | None = None,
) -> bool | None:
"""Call a service.
@@ -1815,9 +1811,7 @@ class ServiceRegistry:
See description of async_call for details.
"""
return asyncio.run_coroutine_threadsafe(
self.async_call(
domain, service, service_data, blocking, context, limit, target
),
self.async_call(domain, service, service_data, blocking, context, target),
self._hass.loop,
).result()
@@ -1828,16 +1822,11 @@ class ServiceRegistry:
service_data: dict[str, Any] | None = None,
blocking: bool = False,
context: Context | None = None,
limit: float | None = SERVICE_CALL_LIMIT,
target: dict[str, Any] | None = None,
) -> bool | None:
) -> None:
"""Call a service.
Specify blocking=True to wait until service is executed.
Waits a maximum of limit, which may be None for no timeout.
If blocking = True, will return boolean if service executed
successfully within limit.
This method will fire an event to indicate the service has been called.
@@ -1888,33 +1877,9 @@ class ServiceRegistry:
coro = self._execute_service(handler, service_call)
if not blocking:
self._run_service_in_background(coro, service_call)
return None
return
task = self._hass.async_create_task(coro)
try:
await asyncio.wait({task}, timeout=limit)
except asyncio.CancelledError:
# Task calling us was cancelled, so cancel service call task, and wait for
# it to be cancelled, within reason, before leaving.
_LOGGER.debug("Service call was cancelled: %s", service_call)
task.cancel()
await asyncio.wait({task}, timeout=SERVICE_CALL_LIMIT)
raise
if task.cancelled():
# Service call task was cancelled some other way, such as during shutdown.
_LOGGER.debug("Service was cancelled: %s", service_call)
raise asyncio.CancelledError
if task.done():
# Propagate any exceptions that might have happened during service call.
task.result()
# Service call completed successfully!
return True
# Service call task did not complete before timeout expired.
# Let it keep running in background.
self._run_service_in_background(task, service_call)
_LOGGER.debug("Service did not complete before timeout: %s", service_call)
return False
await coro
def _run_service_in_background(
self,