From cb1b45d31ab85edec72caf3fee77bba3c9c6e425 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 29 Mar 2021 00:25:40 -1000 Subject: [PATCH] Log the reason a config entry failed to setup (#48449) If we pass a string to ConfigEntryNotReady or raise it from another exception we now log the string passed or the string generated by the original exception. With #47201 this makes it easy for developers to still show the reason why setup failed without having to worry about log spam from additional attempts by rasing ConfigEntryNotReady from the original exception. --- homeassistant/config_entries.py | 12 +++++++++--- tests/test_config_entries.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 849e3dcfd14..2ffce00f62f 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -252,21 +252,27 @@ class ConfigEntry: "%s.async_setup_entry did not return boolean", integration.domain ) result = False - except ConfigEntryNotReady: + except ConfigEntryNotReady as ex: self.state = ENTRY_STATE_SETUP_RETRY wait_time = 2 ** min(tries, 4) * 5 tries += 1 + message = str(ex) + if not message and ex.__cause__: + message = str(ex.__cause__) + ready_message = f"ready yet: {message}" if message else "ready yet" if tries == 1: _LOGGER.warning( - "Config entry '%s' for %s integration not ready yet. Retrying in background", + "Config entry '%s' for %s integration not %s; Retrying in background", self.title, self.domain, + ready_message, ) else: _LOGGER.debug( - "Config entry '%s' for %s integration not ready yet. Retrying in %d seconds", + "Config entry '%s' for %s integration not %s; Retrying in %d seconds", self.title, self.domain, + ready_message, wait_time, ) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index b12601220c3..af1f7ba2ec4 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -795,7 +795,9 @@ async def test_setup_raise_not_ready(hass, caplog): """Test a setup raising not ready.""" entry = MockConfigEntry(title="test_title", domain="test") - mock_setup_entry = AsyncMock(side_effect=ConfigEntryNotReady) + mock_setup_entry = AsyncMock( + side_effect=ConfigEntryNotReady("The internet connection is offline") + ) mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) mock_entity_platform(hass, "config_flow.test", None) @@ -803,7 +805,10 @@ async def test_setup_raise_not_ready(hass, caplog): await entry.async_setup(hass) assert len(mock_call.mock_calls) == 1 - assert "Config entry 'test_title' for test integration not ready yet" in caplog.text + assert ( + "Config entry 'test_title' for test integration not ready yet: The internet connection is offline" + in caplog.text + ) p_hass, p_wait_time, p_setup = mock_call.mock_calls[0][1] assert p_hass is hass @@ -817,6 +822,28 @@ async def test_setup_raise_not_ready(hass, caplog): assert entry.state == config_entries.ENTRY_STATE_LOADED +async def test_setup_raise_not_ready_from_exception(hass, caplog): + """Test a setup raising not ready from another exception.""" + entry = MockConfigEntry(title="test_title", domain="test") + + original_exception = HomeAssistantError("The device dropped the connection") + config_entry_exception = ConfigEntryNotReady() + config_entry_exception.__cause__ = original_exception + + mock_setup_entry = AsyncMock(side_effect=config_entry_exception) + mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + with patch("homeassistant.helpers.event.async_call_later") as mock_call: + await entry.async_setup(hass) + + assert len(mock_call.mock_calls) == 1 + assert ( + "Config entry 'test_title' for test integration not ready yet: The device dropped the connection" + in caplog.text + ) + + async def test_setup_retrying_during_unload(hass): """Test if we unload an entry that is in retry mode.""" entry = MockConfigEntry(domain="test")