Remove assumption in ConfigEntryItems about unique unique_id (#127399)

This commit is contained in:
Erik Montnemery 2024-10-03 22:27:15 +02:00 committed by GitHub
parent 80582a128a
commit 48a07d531c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 6 deletions

View File

@ -1600,7 +1600,7 @@ class ConfigEntryItems(UserDict[str, ConfigEntry]):
super().__init__()
self._hass = hass
self._domain_index: dict[str, list[ConfigEntry]] = {}
self._domain_unique_id_index: dict[str, dict[str, ConfigEntry]] = {}
self._domain_unique_id_index: dict[str, dict[str, list[ConfigEntry]]] = {}
def values(self) -> ValuesView[ConfigEntry]:
"""Return the underlying values to avoid __iter__ overhead."""
@ -1643,9 +1643,9 @@ class ConfigEntryItems(UserDict[str, ConfigEntry]):
report_issue,
)
self._domain_unique_id_index.setdefault(entry.domain, {})[
unique_id_hash
] = entry
self._domain_unique_id_index.setdefault(entry.domain, {}).setdefault(
unique_id_hash, []
).append(entry)
def _unindex_entry(self, entry_id: str) -> None:
"""Unindex an entry."""
@ -1658,7 +1658,9 @@ class ConfigEntryItems(UserDict[str, ConfigEntry]):
# Check type first to avoid expensive isinstance call
if type(unique_id) is not str and not isinstance(unique_id, Hashable): # noqa: E721
unique_id = str(entry.unique_id) # type: ignore[unreachable]
del self._domain_unique_id_index[domain][unique_id]
self._domain_unique_id_index[domain][unique_id].remove(entry)
if not self._domain_unique_id_index[domain][unique_id]:
del self._domain_unique_id_index[domain][unique_id]
if not self._domain_unique_id_index[domain]:
del self._domain_unique_id_index[domain]
@ -1690,7 +1692,10 @@ class ConfigEntryItems(UserDict[str, ConfigEntry]):
# Check type first to avoid expensive isinstance call
if type(unique_id) is not str and not isinstance(unique_id, Hashable): # noqa: E721
unique_id = str(unique_id) # type: ignore[unreachable]
return self._domain_unique_id_index.get(domain, {}).get(unique_id)
entries = self._domain_unique_id_index.get(domain, {}).get(unique_id)
if not entries:
return None
return entries[0]
class ConfigEntryStore(storage.Store[dict[str, list[dict[str, Any]]]]):

View File

@ -513,6 +513,41 @@ async def test_remove_entry(
assert not entity_entry_list
async def test_remove_entry_non_unique_unique_id(
hass: HomeAssistant,
manager: config_entries.ConfigEntries,
entity_registry: er.EntityRegistry,
) -> None:
"""Test that we can remove entry with colliding unique_id."""
entry_1 = MockConfigEntry(
domain="test_other", entry_id="test1", unique_id="not_unique"
)
entry_1.add_to_manager(manager)
entry_2 = MockConfigEntry(
domain="test_other", entry_id="test2", unique_id="not_unique"
)
entry_2.add_to_manager(manager)
entry_3 = MockConfigEntry(
domain="test_other", entry_id="test3", unique_id="not_unique"
)
entry_3.add_to_manager(manager)
# Check all config entries exist
assert manager.async_entry_ids() == [
"test1",
"test2",
"test3",
]
# Remove entries
assert await manager.async_remove("test1") == {"require_restart": False}
await hass.async_block_till_done()
assert await manager.async_remove("test2") == {"require_restart": False}
await hass.async_block_till_done()
assert await manager.async_remove("test3") == {"require_restart": False}
await hass.async_block_till_done()
async def test_remove_entry_cancels_reauth(
hass: HomeAssistant,
manager: config_entries.ConfigEntries,