From 6b4457043d7df73bd5acacbf41cf74ac65a509ab Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 7 Apr 2024 14:08:25 -1000 Subject: [PATCH] Deprecate async_add_hass_job (#115061) --- homeassistant/core.py | 61 +++++++++++++++++-- .../components/homematicip_cloud/test_hap.py | 2 +- tests/test_core.py | 43 +++++++++---- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index 574edf34c9b..113bbf7bf77 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -557,7 +557,7 @@ class HomeAssistant: target = cast(Callable[[*_Ts], Any], target) self.loop.call_soon_threadsafe( functools.partial( - self.async_add_hass_job, HassJob(target), *args, eager_start=True + self._async_add_hass_job, HassJob(target), *args, eager_start=True ) ) @@ -629,7 +629,7 @@ class HomeAssistant: # https://github.com/home-assistant/core/pull/71960 if TYPE_CHECKING: target = cast(Callable[[*_Ts], Coroutine[Any, Any, _R] | _R], target) - return self.async_add_hass_job(HassJob(target), *args, eager_start=eager_start) + return self._async_add_hass_job(HassJob(target), *args, eager_start=eager_start) @overload @callback @@ -664,6 +664,58 @@ class HomeAssistant: If eager_start is True, coroutine functions will be scheduled eagerly. If background is True, the task will created as a background task. + This method must be run in the event loop. + hassjob: HassJob to call. + args: parameters for method to call. + """ + # late import to avoid circular imports + from .helpers import frame # pylint: disable=import-outside-toplevel + + frame.report( + "calls `async_add_hass_job`, which is deprecated and will be removed in Home " + "Assistant 2025.5; Please review " + "https://developers.home-assistant.io/blog/2024/04/07/deprecate_add_hass_job" + " for replacement options", + error_if_core=False, + ) + + return self._async_add_hass_job( + hassjob, *args, eager_start=eager_start, background=background + ) + + @overload + @callback + def _async_add_hass_job( + self, + hassjob: HassJob[..., Coroutine[Any, Any, _R]], + *args: Any, + eager_start: bool = False, + background: bool = False, + ) -> asyncio.Future[_R] | None: ... + + @overload + @callback + def _async_add_hass_job( + self, + hassjob: HassJob[..., Coroutine[Any, Any, _R] | _R], + *args: Any, + eager_start: bool = False, + background: bool = False, + ) -> asyncio.Future[_R] | None: ... + + @callback + def _async_add_hass_job( + self, + hassjob: HassJob[..., Coroutine[Any, Any, _R] | _R], + *args: Any, + eager_start: bool = False, + background: bool = False, + ) -> asyncio.Future[_R] | None: + """Add a HassJob from within the event loop. + + If eager_start is True, coroutine functions will be scheduled eagerly. + If background is True, the task will created as a background task. + This method must be run in the event loop. hassjob: HassJob to call. args: parameters for method to call. @@ -841,7 +893,7 @@ class HomeAssistant: hassjob.target(*args) return None - return self.async_add_hass_job( + return self._async_add_hass_job( hassjob, *args, eager_start=True, background=background ) @@ -1458,7 +1510,8 @@ class EventBus: except Exception: # pylint: disable=broad-except _LOGGER.exception("Error running job: %s", job) else: - self._hass.async_add_hass_job(job, event) + # pylint: disable-next=protected-access + self._hass._async_add_hass_job(job, event) def listen( self, diff --git a/tests/components/homematicip_cloud/test_hap.py b/tests/components/homematicip_cloud/test_hap.py index cddade7cec5..3cb8b7d61e9 100644 --- a/tests/components/homematicip_cloud/test_hap.py +++ b/tests/components/homematicip_cloud/test_hap.py @@ -105,7 +105,7 @@ async def test_hap_setup_connection_error() -> None: ): assert not await hap.async_setup() - assert not hass.async_add_hass_job.mock_calls + assert not hass.async_run_hass_job.mock_calls assert not hass.config_entries.flow.async_init.mock_calls diff --git a/tests/test_core.py b/tests/test_core.py index e9d8d39ce18..a3722b0646d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -93,7 +93,7 @@ async def test_async_add_hass_job_schedule_callback() -> None: hass = MagicMock() job = MagicMock() - ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(ha.callback(job))) + ha.HomeAssistant._async_add_hass_job(hass, ha.HassJob(ha.callback(job))) assert len(hass.loop.call_soon.mock_calls) == 1 assert len(hass.loop.create_task.mock_calls) == 0 assert len(hass.add_job.mock_calls) == 0 @@ -107,7 +107,7 @@ async def test_async_add_hass_job_eager_start_coro_suspends( async def job_that_suspends(): await asyncio.sleep(0) - task = hass.async_add_hass_job( + task = hass._async_add_hass_job( ha.HassJob(ha.callback(job_that_suspends)), eager_start=True ) assert not task.done() @@ -137,7 +137,7 @@ async def test_async_add_hass_job_background(hass: HomeAssistant) -> None: async def job_that_suspends(): await asyncio.sleep(0) - task = hass.async_add_hass_job( + task = hass._async_add_hass_job( ha.HassJob(ha.callback(job_that_suspends)), background=True ) assert not task.done() @@ -167,7 +167,7 @@ async def test_async_add_hass_job_eager_background(hass: HomeAssistant) -> None: async def job_that_suspends(): await asyncio.sleep(0) - task = hass.async_add_hass_job( + task = hass._async_add_hass_job( ha.HassJob(ha.callback(job_that_suspends)), background=True ) assert not task.done() @@ -232,7 +232,7 @@ async def test_async_add_hass_job_coro_named(hass: HomeAssistant) -> None: job = ha.HassJob(mycoro, "named coro") assert "named coro" in str(job) assert job.name == "named coro" - task = ha.HomeAssistant.async_add_hass_job(hass, job) + task = ha.HomeAssistant._async_add_hass_job(hass, job) assert "named coro" in str(task) @@ -245,7 +245,7 @@ async def test_async_add_hass_job_eager_start(hass: HomeAssistant) -> None: job = ha.HassJob(mycoro, "named coro") assert "named coro" in str(job) assert job.name == "named coro" - task = ha.HomeAssistant.async_add_hass_job(hass, job, eager_start=True) + task = ha.HomeAssistant._async_add_hass_job(hass, job, eager_start=True) assert "named coro" in str(task) @@ -255,7 +255,7 @@ async def test_async_add_hass_job_schedule_partial_callback() -> None: job = MagicMock() partial = functools.partial(ha.callback(job)) - ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(partial)) + ha.HomeAssistant._async_add_hass_job(hass, ha.HassJob(partial)) assert len(hass.loop.call_soon.mock_calls) == 1 assert len(hass.loop.create_task.mock_calls) == 0 assert len(hass.add_job.mock_calls) == 0 @@ -268,7 +268,7 @@ async def test_async_add_hass_job_schedule_coroutinefunction() -> None: async def job(): pass - ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(job)) + ha.HomeAssistant._async_add_hass_job(hass, ha.HassJob(job)) assert len(hass.loop.call_soon.mock_calls) == 0 assert len(hass.loop.create_task.mock_calls) == 1 assert len(hass.add_job.mock_calls) == 0 @@ -285,7 +285,7 @@ async def test_async_add_hass_job_schedule_corofunction_eager_start() -> None: "homeassistant.core.create_eager_task", wraps=create_eager_task ) as mock_create_eager_task: hass_job = ha.HassJob(job) - task = ha.HomeAssistant.async_add_hass_job(hass, hass_job, eager_start=True) + task = ha.HomeAssistant._async_add_hass_job(hass, hass_job, eager_start=True) assert len(hass.loop.call_soon.mock_calls) == 0 assert len(hass.add_job.mock_calls) == 0 assert mock_create_eager_task.mock_calls @@ -301,7 +301,7 @@ async def test_async_add_hass_job_schedule_partial_coroutinefunction() -> None: partial = functools.partial(job) - ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(partial)) + ha.HomeAssistant._async_add_hass_job(hass, ha.HassJob(partial)) assert len(hass.loop.call_soon.mock_calls) == 0 assert len(hass.loop.create_task.mock_calls) == 1 assert len(hass.add_job.mock_calls) == 0 @@ -314,7 +314,7 @@ async def test_async_add_job_add_hass_threaded_job_to_pool() -> None: def job(): pass - ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(job)) + ha.HomeAssistant._async_add_hass_job(hass, ha.HassJob(job)) assert len(hass.loop.call_soon.mock_calls) == 0 assert len(hass.loop.create_task.mock_calls) == 0 assert len(hass.loop.run_in_executor.mock_calls) == 2 @@ -383,7 +383,7 @@ async def test_async_run_eager_hass_job_calls_coro_function() -> None: pass ha.HomeAssistant.async_run_hass_job(hass, ha.HassJob(job)) - assert len(hass.async_add_hass_job.mock_calls) == 1 + assert len(hass._async_add_hass_job.mock_calls) == 1 async def test_async_run_hass_job_calls_callback() -> None: @@ -409,7 +409,7 @@ async def test_async_run_hass_job_delegates_non_async() -> None: ha.HomeAssistant.async_run_hass_job(hass, ha.HassJob(job)) assert len(calls) == 0 - assert len(hass.async_add_hass_job.mock_calls) == 1 + assert len(hass._async_add_hass_job.mock_calls) == 1 async def test_async_get_hass_can_be_called(hass: HomeAssistant) -> None: @@ -3236,6 +3236,23 @@ async def test_async_add_job_deprecated( ) in caplog.text +async def test_async_add_hass_job_deprecated( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test async_add_hass_job warns about its deprecation.""" + + async def _test(): + pass + + hass.async_add_hass_job(HassJob(_test)) + assert ( + "Detected code that calls `async_add_hass_job`, which is deprecated " + "and will be removed in Home Assistant 2025.5; Please review " + "https://developers.home-assistant.io/blog/2024/04/07/deprecate_add_hass_job" + " for replacement options" + ) in caplog.text + + async def test_eventbus_lazy_object_creation(hass: HomeAssistant) -> None: """Test we don't create unneeded objects when firing events.""" calls = []