diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 5cffe992c0d..5c026064c28 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -1436,12 +1436,18 @@ class _TrackPointUTCTime: """Initialize track job.""" loop = self.hass.loop self._cancel_callback = loop.call_at( - loop.time() + self.expected_fire_timestamp - time.time(), self._run_action + loop.time() + self.expected_fire_timestamp - time.time(), self ) @callback - def _run_action(self) -> None: - """Call the action.""" + def __call__(self) -> None: + """Call the action. + + We implement this as __call__ so when debug logging logs the object + it shows the name of the job. This is especially helpful when asyncio + debug logging is enabled as we can see the name of the job that is + being called that is blocking the event loop. + """ # Depending on the available clock support (including timer hardware # and the OS kernel) it can happen that we fire a little bit too early # as measured by utcnow(). That is bad when callbacks have assumptions @@ -1450,7 +1456,7 @@ class _TrackPointUTCTime: if (delta := (self.expected_fire_timestamp - time_tracker_timestamp())) > 0: _LOGGER.debug("Called %f seconds too early, rearming", delta) loop = self.hass.loop - self._cancel_callback = loop.call_at(loop.time() + delta, self._run_action) + self._cancel_callback = loop.call_at(loop.time() + delta, self) return self.hass.async_run_hass_job(self.job, self.utc_point_in_time) diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index 07228abcc2c..a6fad968eac 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -4819,3 +4819,21 @@ async def test_track_state_change_deprecated( "of `async_track_state_change_event` which is deprecated and " "will be removed in Home Assistant 2025.5. Please report this issue." ) in caplog.text + + +async def test_track_point_in_time_repr( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test track point in time.""" + + @ha.callback + def _raise_exception(_): + raise RuntimeError("something happened and its poorly described") + + async_track_point_in_utc_time(hass, _raise_exception, dt_util.utcnow()) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) + + assert "Exception in callback _TrackPointUTCTime" in caplog.text + assert "._raise_exception" in caplog.text + await hass.async_block_till_done(wait_background_tasks=True) diff --git a/tests/ignore_uncaught_exceptions.py b/tests/ignore_uncaught_exceptions.py index 3be2093057b..aaf6cbe3efe 100644 --- a/tests/ignore_uncaught_exceptions.py +++ b/tests/ignore_uncaught_exceptions.py @@ -7,6 +7,12 @@ IGNORE_UNCAUGHT_EXCEPTIONS = [ "tests.test_runner", "test_unhandled_exception_traceback", ), + ( + # This test explicitly throws an uncaught exception + # and should not be removed. + "tests.helpers.test_event", + "test_track_point_in_time_repr", + ), ( "test_homeassistant_bridge", "test_homeassistant_bridge_fan_setup",