Adjust thread safety check messages to point to developer docs (#117392)

This commit is contained in:
J. Nick Koston 2024-05-14 22:20:31 +09:00 committed by GitHub
parent 77de1b2331
commit 7871e9279b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 57 additions and 50 deletions

View File

@ -1955,7 +1955,7 @@ class ConfigEntries:
if entry.entry_id not in self._entries:
raise UnknownEntry(entry.entry_id)
self.hass.verify_event_loop_thread("async_update_entry")
self.hass.verify_event_loop_thread("hass.config_entries.async_update_entry")
changed = False
_setter = object.__setattr__

View File

@ -439,7 +439,10 @@ class HomeAssistant:
# frame is a circular import, so we import it here
frame.report(
f"calls {what} from a thread",
f"calls {what} from a thread. "
"For more information, see "
"https://developers.home-assistant.io/docs/asyncio_thread_safety/"
f"#{what.replace('.', '')}",
error_if_core=True,
error_if_integration=True,
)
@ -802,7 +805,7 @@ class HomeAssistant:
# check with a check for the `hass.config.debug` flag being set as
# long term we don't want to be checking this in production
# environments since it is a performance hit.
self.verify_event_loop_thread("async_create_task")
self.verify_event_loop_thread("hass.async_create_task")
return self.async_create_task_internal(target, name, eager_start)
@callback
@ -1493,7 +1496,7 @@ class EventBus:
This method must be run in the event loop.
"""
_verify_event_type_length_or_raise(event_type)
self._hass.verify_event_loop_thread("async_fire")
self._hass.verify_event_loop_thread("hass.bus.async_fire")
return self.async_fire_internal(
event_type, event_data, origin, context, time_fired
)
@ -2506,7 +2509,7 @@ class ServiceRegistry:
This method must be run in the event loop.
"""
self._hass.verify_event_loop_thread("async_register")
self._hass.verify_event_loop_thread("hass.services.async_register")
self._async_register(
domain, service, service_func, schema, supports_response, job_type
)
@ -2565,7 +2568,7 @@ class ServiceRegistry:
This method must be run in the event loop.
"""
self._hass.verify_event_loop_thread("async_remove")
self._hass.verify_event_loop_thread("hass.services.async_remove")
self._async_remove(domain, service)
@callback

View File

@ -204,7 +204,7 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
picture: str | None = None,
) -> AreaEntry:
"""Create a new area."""
self.hass.verify_event_loop_thread("async_create")
self.hass.verify_event_loop_thread("area_registry.async_create")
normalized_name = normalize_name(name)
if self.async_get_area_by_name(name):
@ -233,7 +233,7 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
@callback
def async_delete(self, area_id: str) -> None:
"""Delete area."""
self.hass.verify_event_loop_thread("async_delete")
self.hass.verify_event_loop_thread("area_registry.async_delete")
device_registry = dr.async_get(self.hass)
entity_registry = er.async_get(self.hass)
device_registry.async_clear_area_id(area_id)
@ -314,7 +314,7 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
if not new_values:
return old
self.hass.verify_event_loop_thread("_async_update")
self.hass.verify_event_loop_thread("area_registry.async_update")
new = self.areas[area_id] = dataclasses.replace(old, **new_values) # type: ignore[arg-type]
self.async_schedule_save()

View File

@ -98,7 +98,7 @@ class CategoryRegistry(BaseRegistry[CategoryRegistryStoreData]):
icon: str | None = None,
) -> CategoryEntry:
"""Create a new category."""
self.hass.verify_event_loop_thread("async_create")
self.hass.verify_event_loop_thread("category_registry.async_create")
self._async_ensure_name_is_available(scope, name)
category = CategoryEntry(
icon=icon,
@ -122,7 +122,7 @@ class CategoryRegistry(BaseRegistry[CategoryRegistryStoreData]):
@callback
def async_delete(self, *, scope: str, category_id: str) -> None:
"""Delete category."""
self.hass.verify_event_loop_thread("async_delete")
self.hass.verify_event_loop_thread("category_registry.async_delete")
del self.categories[scope][category_id]
self.hass.bus.async_fire_internal(
EVENT_CATEGORY_REGISTRY_UPDATED,
@ -157,7 +157,7 @@ class CategoryRegistry(BaseRegistry[CategoryRegistryStoreData]):
if not changes:
return old
self.hass.verify_event_loop_thread("async_update")
self.hass.verify_event_loop_thread("category_registry.async_update")
new = self.categories[scope][category_id] = dataclasses.replace(old, **changes) # type: ignore[arg-type]
self.async_schedule_save()

View File

@ -906,7 +906,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
if not new_values:
return old
self.hass.verify_event_loop_thread("async_update_device")
self.hass.verify_event_loop_thread("device_registry.async_update_device")
new = attr.evolve(old, **new_values)
self.devices[device_id] = new
@ -933,7 +933,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
@callback
def async_remove_device(self, device_id: str) -> None:
"""Remove a device from the device registry."""
self.hass.verify_event_loop_thread("async_remove_device")
self.hass.verify_event_loop_thread("device_registry.async_remove_device")
device = self.devices.pop(device_id)
self.deleted_devices[device_id] = DeletedDeviceEntry(
config_entries=device.config_entries,

View File

@ -821,7 +821,7 @@ class EntityRegistry(BaseRegistry):
unit_of_measurement=unit_of_measurement,
)
self.hass.verify_event_loop_thread("async_get_or_create")
self.hass.verify_event_loop_thread("entity_registry.async_get_or_create")
_validate_item(
self.hass,
domain,
@ -894,7 +894,7 @@ class EntityRegistry(BaseRegistry):
@callback
def async_remove(self, entity_id: str) -> None:
"""Remove an entity from registry."""
self.hass.verify_event_loop_thread("async_remove")
self.hass.verify_event_loop_thread("entity_registry.async_remove")
entity = self.entities.pop(entity_id)
config_entry_id = entity.config_entry_id
key = (entity.domain, entity.platform, entity.unique_id)
@ -1089,7 +1089,7 @@ class EntityRegistry(BaseRegistry):
if not new_values:
return old
self.hass.verify_event_loop_thread("_async_update_entity")
self.hass.verify_event_loop_thread("entity_registry.async_update_entity")
new = self.entities[entity_id] = attr.evolve(old, **new_values)

View File

@ -121,7 +121,7 @@ class FloorRegistry(BaseRegistry[FloorRegistryStoreData]):
level: int | None = None,
) -> FloorEntry:
"""Create a new floor."""
self.hass.verify_event_loop_thread("async_create")
self.hass.verify_event_loop_thread("floor_registry.async_create")
if floor := self.async_get_floor_by_name(name):
raise ValueError(
f"The name {name} ({floor.normalized_name}) is already in use"
@ -152,7 +152,7 @@ class FloorRegistry(BaseRegistry[FloorRegistryStoreData]):
@callback
def async_delete(self, floor_id: str) -> None:
"""Delete floor."""
self.hass.verify_event_loop_thread("async_delete")
self.hass.verify_event_loop_thread("floor_registry.async_delete")
del self.floors[floor_id]
self.hass.bus.async_fire_internal(
EVENT_FLOOR_REGISTRY_UPDATED,
@ -191,7 +191,7 @@ class FloorRegistry(BaseRegistry[FloorRegistryStoreData]):
if not changes:
return old
self.hass.verify_event_loop_thread("async_update")
self.hass.verify_event_loop_thread("floor_registry.async_update")
new = self.floors[floor_id] = dataclasses.replace(old, **changes) # type: ignore[arg-type]
self.async_schedule_save()

View File

@ -144,7 +144,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")
self.hass.verify_event_loop_thread("issue_registry.async_get_or_create")
if (issue := self.async_get_issue(domain, issue_id)) is None:
issue = IssueEntry(
active=True,
@ -204,7 +204,7 @@ class IssueRegistry(BaseRegistry):
@callback
def async_delete(self, domain: str, issue_id: str) -> None:
"""Delete issue."""
self.hass.verify_event_loop_thread("async_delete")
self.hass.verify_event_loop_thread("issue_registry.async_delete")
if self.issues.pop((domain, issue_id), None) is None:
return
@ -221,7 +221,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")
self.hass.verify_event_loop_thread("issue_registry.async_ignore")
old = self.issues[(domain, issue_id)]
dismissed_version = ha_version if ignore else None
if old.dismissed_version == dismissed_version:

View File

@ -121,7 +121,7 @@ class LabelRegistry(BaseRegistry[LabelRegistryStoreData]):
description: str | None = None,
) -> LabelEntry:
"""Create a new label."""
self.hass.verify_event_loop_thread("async_create")
self.hass.verify_event_loop_thread("label_registry.async_create")
if label := self.async_get_label_by_name(name):
raise ValueError(
f"The name {name} ({label.normalized_name}) is already in use"
@ -152,7 +152,7 @@ class LabelRegistry(BaseRegistry[LabelRegistryStoreData]):
@callback
def async_delete(self, label_id: str) -> None:
"""Delete label."""
self.hass.verify_event_loop_thread("async_delete")
self.hass.verify_event_loop_thread("label_registry.async_delete")
del self.labels[label_id]
self.hass.bus.async_fire_internal(
EVENT_LABEL_REGISTRY_UPDATED,
@ -192,7 +192,7 @@ class LabelRegistry(BaseRegistry[LabelRegistryStoreData]):
if not changes:
return old
self.hass.verify_event_loop_thread("async_update")
self.hass.verify_event_loop_thread("label_registry.async_update")
new = self.labels[label_id] = dataclasses.replace(old, **changes) # type: ignore[arg-type]
self.async_schedule_save()

View File

@ -500,7 +500,7 @@ async def test_async_get_or_create_thread_checks(
"""We raise when trying to create in the wrong thread."""
with pytest.raises(
RuntimeError,
match="Detected code that calls async_create from a thread. Please report this issue.",
match="Detected code that calls area_registry.async_create from a thread.",
):
await hass.async_add_executor_job(area_registry.async_create, "Mock1")
@ -512,7 +512,7 @@ async def test_async_update_thread_checks(
area = area_registry.async_create("Mock1")
with pytest.raises(
RuntimeError,
match="Detected code that calls _async_update from a thread. Please report this issue.",
match="Detected code that calls area_registry.async_update from a thread.",
):
await hass.async_add_executor_job(
partial(area_registry.async_update, area.id, name="Mock2")
@ -526,6 +526,6 @@ async def test_async_delete_thread_checks(
area = area_registry.async_create("Mock1")
with pytest.raises(
RuntimeError,
match="Detected code that calls async_delete from a thread. Please report this issue.",
match="Detected code that calls area_registry.async_delete from a thread.",
):
await hass.async_add_executor_job(area_registry.async_delete, area.id)

View File

@ -403,7 +403,7 @@ async def test_async_create_thread_safety(
"""Test async_create raises when called from wrong thread."""
with pytest.raises(
RuntimeError,
match="Detected code that calls async_create from a thread. Please report this issue.",
match="Detected code that calls category_registry.async_create from a thread.",
):
await hass.async_add_executor_job(
partial(category_registry.async_create, name="any", scope="any")
@ -418,7 +418,7 @@ async def test_async_delete_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_delete from a thread. Please report this issue.",
match="Detected code that calls category_registry.async_delete from a thread.",
):
await hass.async_add_executor_job(
partial(
@ -437,7 +437,7 @@ async def test_async_update_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_update from a thread. Please report this issue.",
match="Detected code that calls category_registry.async_update from a thread.",
):
await hass.async_add_executor_job(
partial(

View File

@ -2485,7 +2485,7 @@ async def test_async_get_or_create_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_update_device from a thread. Please report this issue.",
match="Detected code that calls device_registry.async_update_device from a thread.",
):
await hass.async_add_executor_job(
partial(
@ -2515,7 +2515,7 @@ async def test_async_remove_device_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_remove_device from a thread. Please report this issue.",
match="Detected code that calls device_registry.async_remove_device from a thread.",
):
await hass.async_add_executor_job(
device_registry.async_remove_device, device.id

View File

@ -1984,7 +1984,7 @@ async def test_get_or_create_thread_safety(
"""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.",
match="Detected code that calls entity_registry.async_get_or_create from a thread.",
):
await hass.async_add_executor_job(
entity_registry.async_get_or_create, "light", "hue", "1234"
@ -1998,7 +1998,7 @@ async def test_async_update_entity_thread_safety(
entry = entity_registry.async_get_or_create("light", "hue", "1234")
with pytest.raises(
RuntimeError,
match="Detected code that calls _async_update_entity from a thread. Please report this issue.",
match="Detected code that calls entity_registry.async_update_entity from a thread.",
):
await hass.async_add_executor_job(
partial(
@ -2016,6 +2016,6 @@ async def test_async_remove_thread_safety(
entry = entity_registry.async_get_or_create("light", "hue", "1234")
with pytest.raises(
RuntimeError,
match="Detected code that calls async_remove from a thread. Please report this issue.",
match="Detected code that calls entity_registry.async_remove from a thread.",
):
await hass.async_add_executor_job(entity_registry.async_remove, entry.entity_id)

View File

@ -367,7 +367,7 @@ async def test_async_create_thread_safety(
"""Test async_create raises when called from wrong thread."""
with pytest.raises(
RuntimeError,
match="Detected code that calls async_create from a thread. Please report this issue.",
match="Detected code that calls floor_registry.async_create from a thread.",
):
await hass.async_add_executor_job(floor_registry.async_create, "any")
@ -381,7 +381,7 @@ async def test_async_delete_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_delete from a thread. Please report this issue.",
match="Detected code that calls floor_registry.async_delete from a thread.",
):
await hass.async_add_executor_job(floor_registry.async_delete, any_floor)
@ -395,7 +395,7 @@ async def test_async_update_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_update from a thread. Please report this issue.",
match="Detected code that calls floor_registry.async_update from a thread.",
):
await hass.async_add_executor_job(
partial(floor_registry.async_update, any_floor.floor_id, name="new name")

View File

@ -367,7 +367,7 @@ async def test_get_or_create_thread_safety(
"""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.",
match="Detected code that calls issue_registry.async_get_or_create from a thread.",
):
await hass.async_add_executor_job(
partial(
@ -397,7 +397,7 @@ async def test_async_delete_issue_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_delete from a thread. Please report this issue.",
match="Detected code that calls issue_registry.async_delete from a thread.",
):
await hass.async_add_executor_job(
ir.async_delete_issue,
@ -422,7 +422,7 @@ async def test_async_ignore_issue_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_ignore from a thread. Please report this issue.",
match="Detected code that calls issue_registry.async_ignore from a thread.",
):
await hass.async_add_executor_job(
ir.async_ignore_issue, hass, "any", "any", True

View File

@ -464,7 +464,7 @@ async def test_async_create_thread_safety(
"""Test async_create raises when called from wrong thread."""
with pytest.raises(
RuntimeError,
match="Detected code that calls async_create from a thread. Please report this issue.",
match="Detected code that calls label_registry.async_create from a thread.",
):
await hass.async_add_executor_job(label_registry.async_create, "any")
@ -478,7 +478,7 @@ async def test_async_delete_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_delete from a thread. Please report this issue.",
match="Detected code that calls label_registry.async_delete from a thread.",
):
await hass.async_add_executor_job(label_registry.async_delete, any_label)
@ -492,7 +492,7 @@ async def test_async_update_thread_safety(
with pytest.raises(
RuntimeError,
match="Detected code that calls async_update from a thread. Please report this issue.",
match="Detected code that calls label_registry.async_update from a thread.",
):
await hass.async_add_executor_job(
partial(label_registry.async_update, any_label.label_id, name="new name")

View File

@ -3442,7 +3442,8 @@ async def test_async_fire_thread_safety(hass: HomeAssistant) -> None:
events = async_capture_events(hass, "test_event")
hass.bus.async_fire("test_event")
with pytest.raises(
RuntimeError, match="Detected code that calls async_fire from a thread."
RuntimeError,
match="Detected code that calls hass.bus.async_fire from a thread.",
):
await hass.async_add_executor_job(hass.bus.async_fire, "test_event")
@ -3452,7 +3453,8 @@ async def test_async_fire_thread_safety(hass: HomeAssistant) -> None:
async def test_async_register_thread_safety(hass: HomeAssistant) -> None:
"""Test async_register thread safety."""
with pytest.raises(
RuntimeError, match="Detected code that calls async_register from a thread."
RuntimeError,
match="Detected code that calls hass.services.async_register from a thread.",
):
await hass.async_add_executor_job(
hass.services.async_register,
@ -3465,7 +3467,8 @@ async def test_async_register_thread_safety(hass: HomeAssistant) -> None:
async def test_async_remove_thread_safety(hass: HomeAssistant) -> None:
"""Test async_remove thread safety."""
with pytest.raises(
RuntimeError, match="Detected code that calls async_remove from a thread."
RuntimeError,
match="Detected code that calls hass.services.async_remove from a thread.",
):
await hass.async_add_executor_job(
hass.services.async_remove, "test_domain", "test_service"
@ -3479,6 +3482,7 @@ async def test_async_create_task_thread_safety(hass: HomeAssistant) -> None:
pass
with pytest.raises(
RuntimeError, match="Detected code that calls async_create_task from a thread."
RuntimeError,
match="Detected code that calls hass.async_create_task from a thread.",
):
await hass.async_add_executor_job(hass.async_create_task, _any_coro)