Move thread safety check in issue_registry sooner (#116899)

This commit is contained in:
J. Nick Koston 2024-05-05 20:32:55 -05:00 committed by GitHub
parent 2a4686e1b7
commit 5d5f311898
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 76 additions and 5 deletions

View file

@ -132,7 +132,7 @@ class IssueRegistry(BaseRegistry):
translation_placeholders: dict[str, str] | None = None,
) -> IssueEntry:
"""Get issue. Create if it doesn't exist."""
self.hass.verify_event_loop_thread("async_get_or_create")
if (issue := self.async_get_issue(domain, issue_id)) is None:
issue = IssueEntry(
active=True,
@ -152,7 +152,7 @@ class IssueRegistry(BaseRegistry):
)
self.issues[(domain, issue_id)] = issue
self.async_schedule_save()
self.hass.bus.async_fire(
self.hass.bus.async_fire_internal(
EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED,
{"action": "create", "domain": domain, "issue_id": issue_id},
)
@ -174,7 +174,7 @@ class IssueRegistry(BaseRegistry):
if replacement != issue:
issue = self.issues[(domain, issue_id)] = replacement
self.async_schedule_save()
self.hass.bus.async_fire(
self.hass.bus.async_fire_internal(
EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED,
{"action": "update", "domain": domain, "issue_id": issue_id},
)
@ -184,11 +184,12 @@ class IssueRegistry(BaseRegistry):
@callback
def async_delete(self, domain: str, issue_id: str) -> None:
"""Delete issue."""
self.hass.verify_event_loop_thread("async_delete")
if self.issues.pop((domain, issue_id), None) is None:
return
self.async_schedule_save()
self.hass.bus.async_fire(
self.hass.bus.async_fire_internal(
EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED,
{"action": "remove", "domain": domain, "issue_id": issue_id},
)
@ -196,6 +197,7 @@ class IssueRegistry(BaseRegistry):
@callback
def async_ignore(self, domain: str, issue_id: str, ignore: bool) -> IssueEntry:
"""Ignore issue."""
self.hass.verify_event_loop_thread("async_ignore")
old = self.issues[(domain, issue_id)]
dismissed_version = ha_version if ignore else None
if old.dismissed_version == dismissed_version:
@ -207,7 +209,7 @@ class IssueRegistry(BaseRegistry):
)
self.async_schedule_save()
self.hass.bus.async_fire(
self.hass.bus.async_fire_internal(
EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED,
{"action": "update", "domain": domain, "issue_id": issue_id},
)

View file

@ -1,5 +1,6 @@
"""Test the repairs websocket API."""
from functools import partial
from typing import Any
import pytest
@ -358,3 +359,71 @@ async def test_migration_1_1(hass: HomeAssistant, hass_storage: dict[str, Any])
registry: ir.IssueRegistry = hass.data[ir.DATA_REGISTRY]
assert len(registry.issues) == 2
async def test_get_or_create_thread_safety(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test call async_get_or_create_from a thread."""
with pytest.raises(
RuntimeError,
match="Detected code that calls async_get_or_create from a thread. Please report this issue.",
):
await hass.async_add_executor_job(
partial(
ir.async_create_issue,
hass,
"any",
"any",
is_fixable=True,
severity="error",
translation_key="any",
)
)
async def test_async_delete_issue_thread_safety(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test call async_delete_issue from a thread."""
ir.async_create_issue(
hass,
"any",
"any",
is_fixable=True,
severity="error",
translation_key="any",
)
with pytest.raises(
RuntimeError,
match="Detected code that calls async_delete from a thread. Please report this issue.",
):
await hass.async_add_executor_job(
ir.async_delete_issue,
hass,
"any",
"any",
)
async def test_async_ignore_issue_thread_safety(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test call async_ignore_issue from a thread."""
ir.async_create_issue(
hass,
"any",
"any",
is_fixable=True,
severity="error",
translation_key="any",
)
with pytest.raises(
RuntimeError,
match="Detected code that calls async_ignore from a thread. Please report this issue.",
):
await hass.async_add_executor_job(
ir.async_ignore_issue, hass, "any", "any", True
)