From 7714f807b4d3521dec51591345eb4f81a225fcb9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 May 2024 14:01:59 -1000 Subject: [PATCH] Detect incorrect exception in forwarded platforms (#117754) * Detect incorrect exception in forwarded platforms If an integration raises ConfigEntryError/ConfigEntryAuthFailed/ConfigEntryAuthFailed in a forwarded platform it would affect the state of the config entry and cause it to process unloads and setup retries in while the other platforms continued to setup * Detect incorrect exception in forwarded platforms If an integration raises ConfigEntryError/ConfigEntryAuthFailed/ConfigEntryAuthFailed in a forwarded platform it would affect the state of the config entry and cause it to process unloads and setup retries in while the other platforms continued to setup * Update homeassistant/config_entries.py Co-authored-by: Paulus Schoutsen * adjust * fix --------- Co-authored-by: Paulus Schoutsen --- homeassistant/helpers/entity_platform.py | 18 +++++- tests/test_config_entries.py | 73 ++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 86bf85f17a54b..46f8fe9c6b7d4 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -30,7 +30,13 @@ split_entity_id, valid_entity_id, ) -from homeassistant.exceptions import HomeAssistantError, PlatformNotReady +from homeassistant.exceptions import ( + ConfigEntryAuthFailed, + ConfigEntryError, + ConfigEntryNotReady, + HomeAssistantError, + PlatformNotReady, +) from homeassistant.generated import languages from homeassistant.setup import SetupPhases, async_start_setup from homeassistant.util.async_ import create_eager_task @@ -410,6 +416,16 @@ async def setup_again(*_args: Any) -> None: SLOW_SETUP_MAX_WAIT, ) return False + except (ConfigEntryNotReady, ConfigEntryAuthFailed, ConfigEntryError) as exc: + _LOGGER.error( + "%s raises exception %s in forwarded platform " + "%s; Instead raise %s before calling async_forward_entry_setups", + self.platform_name, + type(exc).__name__, + self.domain, + type(exc).__name__, + ) + return False except Exception: logger.exception( "Error while setting up %s platform for %s", diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index cdce963004ae3..16692e620cb7b 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -5398,3 +5398,76 @@ async def mock_async_setup_entry(hass, entry): await setup_task await reload_task assert setup_calls == 2 + + +@pytest.mark.parametrize( + "exc", + [ + ConfigEntryError, + ConfigEntryAuthFailed, + ConfigEntryNotReady, + ], +) +async def test_raise_wrong_exception_in_forwarded_platform( + hass: HomeAssistant, + manager: config_entries.ConfigEntries, + exc: Exception, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test that we can remove an entry.""" + + async def mock_setup_entry( + hass: HomeAssistant, entry: config_entries.ConfigEntry + ) -> bool: + """Mock setting up entry.""" + await hass.config_entries.async_forward_entry_setups(entry, ["light"]) + return True + + async def mock_unload_entry( + hass: HomeAssistant, entry: config_entries.ConfigEntry + ) -> bool: + """Mock unloading an entry.""" + result = await hass.config_entries.async_unload_platforms(entry, ["light"]) + assert result + return result + + mock_remove_entry = AsyncMock(return_value=None) + + async def mock_setup_entry_platform( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, + ) -> None: + """Mock setting up platform.""" + raise exc + + mock_integration( + hass, + MockModule( + "test", + async_setup_entry=mock_setup_entry, + async_unload_entry=mock_unload_entry, + async_remove_entry=mock_remove_entry, + ), + ) + mock_platform( + hass, "test.light", MockPlatform(async_setup_entry=mock_setup_entry_platform) + ) + mock_platform(hass, "test.config_flow", None) + + entry = MockConfigEntry(domain="test", entry_id="test2") + entry.add_to_manager(manager) + + # Setup entry + await manager.async_setup(entry.entry_id) + await hass.async_block_till_done() + + exc_type_name = type(exc()).__name__ + assert ( + f"test raises exception {exc_type_name} in forwarded platform light;" + in caplog.text + ) + assert ( + f"Instead raise {exc_type_name} before calling async_forward_entry_setups" + in caplog.text + )