From a2aa0e608ddfd68f91de9a965078d5480dda728d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 17 Sep 2022 12:52:28 -0500 Subject: [PATCH] Add a helpful message to the config_entries.OperationNotAllowed exception (#78631) We only expect this exception to be raised as a result of an implementation problem. When it is raised during production it is currently hard to trace down why its happening See #75835 --- homeassistant/config_entries.py | 10 ++++++++-- tests/test_config_entries.py | 16 +++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index f37efb6c6275e2..7e9f3b1248d334 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -1024,7 +1024,10 @@ async def async_setup(self, entry_id: str) -> bool: raise UnknownEntry if entry.state is not ConfigEntryState.NOT_LOADED: - raise OperationNotAllowed + raise OperationNotAllowed( + f"The config entry {entry.title} ({entry.domain}) with entry_id {entry.entry_id}" + f" cannot be setup because is already loaded in the {entry.state} state" + ) # Setup Component if not set up yet if entry.domain in self.hass.config.components: @@ -1046,7 +1049,10 @@ async def async_unload(self, entry_id: str) -> bool: raise UnknownEntry if not entry.state.recoverable: - raise OperationNotAllowed + raise OperationNotAllowed( + f"The config entry {entry.title} ({entry.domain}) with entry_id " + f"{entry.entry_id} cannot be unloaded because it is not in a recoverable state ({entry.state})" + ) return await entry.async_unload(self.hass) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index b923e37b636f23..d2d4ffe1134f6c 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -1141,7 +1141,7 @@ async def test_entry_setup_invalid_state(hass, manager, state): MockModule("comp", async_setup=mock_setup, async_setup_entry=mock_setup_entry), ) - with pytest.raises(config_entries.OperationNotAllowed): + with pytest.raises(config_entries.OperationNotAllowed, match=str(state)): assert await manager.async_setup(entry.entry_id) assert len(mock_setup.mock_calls) == 0 @@ -1201,7 +1201,7 @@ async def test_entry_unload_invalid_state(hass, manager, state): mock_integration(hass, MockModule("comp", async_unload_entry=async_unload_entry)) - with pytest.raises(config_entries.OperationNotAllowed): + with pytest.raises(config_entries.OperationNotAllowed, match=str(state)): assert await manager.async_unload(entry.entry_id) assert len(async_unload_entry.mock_calls) == 0 @@ -1296,7 +1296,7 @@ async def test_entry_reload_error(hass, manager, state): ), ) - with pytest.raises(config_entries.OperationNotAllowed): + with pytest.raises(config_entries.OperationNotAllowed, match=str(state)): assert await manager.async_reload(entry.entry_id) assert len(async_unload_entry.mock_calls) == 0 @@ -1370,7 +1370,10 @@ async def test_entry_disable_without_reload_support(hass, manager): assert entry.state is config_entries.ConfigEntryState.FAILED_UNLOAD # Enable - with pytest.raises(config_entries.OperationNotAllowed): + with pytest.raises( + config_entries.OperationNotAllowed, + match=str(config_entries.ConfigEntryState.FAILED_UNLOAD), + ): await manager.async_set_disabled_by(entry.entry_id, None) assert len(async_setup.mock_calls) == 0 assert len(async_setup_entry.mock_calls) == 0 @@ -3270,7 +3273,10 @@ async def test_disallow_entry_reload_with_setup_in_progresss(hass, manager): ) entry.add_to_hass(hass) - with pytest.raises(config_entries.OperationNotAllowed): + with pytest.raises( + config_entries.OperationNotAllowed, + match=str(config_entries.ConfigEntryState.SETUP_IN_PROGRESS), + ): assert await manager.async_reload(entry.entry_id) assert entry.state is config_entries.ConfigEntryState.SETUP_IN_PROGRESS