Add ability to shutdown a Debouncer (#91439)
* Add ability to shutdown a Debouncer * Use async_create_task
This commit is contained in:
parent
698345e88b
commit
19a6530c3c
2 changed files with 52 additions and 4 deletions
|
@ -44,6 +44,7 @@ class Debouncer(Generic[_R_co]):
|
|||
function, f"debouncer cooldown={cooldown}, immediate={immediate}"
|
||||
)
|
||||
)
|
||||
self._shutdown_requested = False
|
||||
|
||||
@property
|
||||
def function(self) -> Callable[[], _R_co] | None:
|
||||
|
@ -62,6 +63,8 @@ class Debouncer(Generic[_R_co]):
|
|||
|
||||
async def async_call(self) -> None:
|
||||
"""Call the function."""
|
||||
if self._shutdown_requested:
|
||||
raise RuntimeError("Debouncer has been shutdown.")
|
||||
assert self._job is not None
|
||||
|
||||
if self._timer_task:
|
||||
|
@ -115,6 +118,12 @@ class Debouncer(Generic[_R_co]):
|
|||
# Schedule a new timer to prevent new runs during cooldown
|
||||
self._schedule_timer()
|
||||
|
||||
@callback
|
||||
def async_shutdown(self) -> None:
|
||||
"""Cancel any scheduled call, and prevent new runs."""
|
||||
self._shutdown_requested = True
|
||||
self.async_cancel()
|
||||
|
||||
@callback
|
||||
def async_cancel(self) -> None:
|
||||
"""Cancel any scheduled call."""
|
||||
|
@ -137,4 +146,7 @@ class Debouncer(Generic[_R_co]):
|
|||
@callback
|
||||
def _schedule_timer(self) -> None:
|
||||
"""Schedule a timer."""
|
||||
self._timer_task = self.hass.loop.call_later(self.cooldown, self._on_debounce)
|
||||
if not self._shutdown_requested:
|
||||
self._timer_task = self.hass.loop.call_later(
|
||||
self.cooldown, self._on_debounce
|
||||
)
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
"""Tests for debounce."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import debounce
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from ..common import async_fire_time_changed
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def test_immediate_works(hass: HomeAssistant) -> None:
|
||||
"""Test immediate works."""
|
||||
calls = []
|
||||
debouncer = debounce.Debouncer(
|
||||
hass,
|
||||
None,
|
||||
_LOGGER,
|
||||
cooldown=0.01,
|
||||
immediate=True,
|
||||
function=AsyncMock(side_effect=lambda: calls.append(None)),
|
||||
|
@ -68,7 +74,7 @@ async def test_not_immediate_works(hass: HomeAssistant) -> None:
|
|||
calls = []
|
||||
debouncer = debounce.Debouncer(
|
||||
hass,
|
||||
None,
|
||||
_LOGGER,
|
||||
cooldown=0.01,
|
||||
immediate=False,
|
||||
function=AsyncMock(side_effect=lambda: calls.append(None)),
|
||||
|
@ -123,7 +129,7 @@ async def test_immediate_works_with_function_swapped(hass: HomeAssistant) -> Non
|
|||
|
||||
debouncer = debounce.Debouncer(
|
||||
hass,
|
||||
None,
|
||||
_LOGGER,
|
||||
cooldown=0.01,
|
||||
immediate=True,
|
||||
function=one_function,
|
||||
|
@ -174,3 +180,33 @@ async def test_immediate_works_with_function_swapped(hass: HomeAssistant) -> Non
|
|||
assert debouncer._execute_at_end_of_timer is False
|
||||
debouncer._execute_lock.release()
|
||||
assert debouncer._job.target == debouncer.function
|
||||
|
||||
|
||||
async def test_shutdown(hass: HomeAssistant) -> None:
|
||||
"""Test shutdown."""
|
||||
calls = []
|
||||
future = asyncio.Future()
|
||||
|
||||
async def _func() -> None:
|
||||
await future
|
||||
calls.append(None)
|
||||
|
||||
debouncer = debounce.Debouncer(
|
||||
hass,
|
||||
_LOGGER,
|
||||
cooldown=0.01,
|
||||
immediate=False,
|
||||
function=_func,
|
||||
)
|
||||
|
||||
# Ensure shutdown during a run doesn't create a cooldown timer
|
||||
hass.async_create_task(debouncer.async_call())
|
||||
await asyncio.sleep(0.01)
|
||||
debouncer.async_shutdown()
|
||||
future.set_result(True)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert debouncer._timer_task is None
|
||||
|
||||
with pytest.raises(RuntimeError, match="Debouncer has been shutdown"):
|
||||
await debouncer.async_call()
|
||||
|
|
Loading…
Add table
Reference in a new issue