mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Apply ConfigEntryNotReady improvements to PlatformNotReady (#48665)
* Apply ConfigEntryNotReady improvements to PlatformNotReady - Limit log spam #47201 - Log exception reason #48449 - Prevent startup blockage #48660 * coverage
This commit is contained in:
parent
ecec3c8ab9
commit
b5c679f3d0
@ -9,9 +9,14 @@ from types import ModuleType
|
||||
from typing import TYPE_CHECKING, Callable, Coroutine, Iterable
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import ATTR_RESTORED, DEVICE_DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_RESTORED,
|
||||
DEVICE_DEFAULT_NAME,
|
||||
EVENT_HOMEASSISTANT_STARTED,
|
||||
)
|
||||
from homeassistant.core import (
|
||||
CALLBACK_TYPE,
|
||||
CoreState,
|
||||
HomeAssistant,
|
||||
ServiceCall,
|
||||
callback,
|
||||
@ -215,23 +220,41 @@ class EntityPlatform:
|
||||
hass.config.components.add(full_name)
|
||||
self._setup_complete = True
|
||||
return True
|
||||
except PlatformNotReady:
|
||||
except PlatformNotReady as ex:
|
||||
tries += 1
|
||||
wait_time = min(tries, 6) * PLATFORM_NOT_READY_BASE_WAIT_TIME
|
||||
logger.warning(
|
||||
"Platform %s not ready yet. Retrying in %d seconds.",
|
||||
self.platform_name,
|
||||
wait_time,
|
||||
)
|
||||
message = str(ex)
|
||||
if not message and ex.__cause__:
|
||||
message = str(ex.__cause__)
|
||||
ready_message = f"ready yet: {message}" if message else "ready yet"
|
||||
if tries == 1:
|
||||
logger.warning(
|
||||
"Platform %s not %s; Retrying in background in %d seconds",
|
||||
self.platform_name,
|
||||
ready_message,
|
||||
wait_time,
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
"Platform %s not %s; Retrying in %d seconds",
|
||||
self.platform_name,
|
||||
ready_message,
|
||||
wait_time,
|
||||
)
|
||||
|
||||
async def setup_again(now): # type: ignore[no-untyped-def]
|
||||
async def setup_again(*_): # type: ignore[no-untyped-def]
|
||||
"""Run setup again."""
|
||||
self._async_cancel_retry_setup = None
|
||||
await self._async_setup_platform(async_create_setup_task, tries)
|
||||
|
||||
self._async_cancel_retry_setup = async_call_later(
|
||||
hass, wait_time, setup_again
|
||||
)
|
||||
if hass.state == CoreState.running:
|
||||
self._async_cancel_retry_setup = async_call_later(
|
||||
hass, wait_time, setup_again
|
||||
)
|
||||
else:
|
||||
self._async_cancel_retry_setup = hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_STARTED, setup_again
|
||||
)
|
||||
return False
|
||||
except asyncio.TimeoutError:
|
||||
logger.error(
|
||||
|
@ -6,8 +6,8 @@ from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, PERCENTAGE
|
||||
from homeassistant.core import CoreState, callback
|
||||
from homeassistant.exceptions import HomeAssistantError, PlatformNotReady
|
||||
from homeassistant.helpers import (
|
||||
device_registry as dr,
|
||||
@ -592,6 +592,52 @@ async def test_setup_entry_platform_not_ready(hass, caplog):
|
||||
assert len(mock_call_later.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_setup_entry_platform_not_ready_with_message(hass, caplog):
|
||||
"""Test when an entry is not ready yet that includes a message."""
|
||||
async_setup_entry = Mock(side_effect=PlatformNotReady("lp0 on fire"))
|
||||
platform = MockPlatform(async_setup_entry=async_setup_entry)
|
||||
config_entry = MockConfigEntry()
|
||||
ent_platform = MockEntityPlatform(
|
||||
hass, platform_name=config_entry.domain, platform=platform
|
||||
)
|
||||
|
||||
with patch.object(entity_platform, "async_call_later") as mock_call_later:
|
||||
assert not await ent_platform.async_setup_entry(config_entry)
|
||||
|
||||
full_name = f"{ent_platform.domain}.{config_entry.domain}"
|
||||
assert full_name not in hass.config.components
|
||||
assert len(async_setup_entry.mock_calls) == 1
|
||||
|
||||
assert "Platform test not ready yet" in caplog.text
|
||||
assert "lp0 on fire" in caplog.text
|
||||
assert len(mock_call_later.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_setup_entry_platform_not_ready_from_exception(hass, caplog):
|
||||
"""Test when an entry is not ready yet that includes the causing exception string."""
|
||||
original_exception = HomeAssistantError("The device dropped the connection")
|
||||
platform_exception = PlatformNotReady()
|
||||
platform_exception.__cause__ = original_exception
|
||||
|
||||
async_setup_entry = Mock(side_effect=platform_exception)
|
||||
platform = MockPlatform(async_setup_entry=async_setup_entry)
|
||||
config_entry = MockConfigEntry()
|
||||
ent_platform = MockEntityPlatform(
|
||||
hass, platform_name=config_entry.domain, platform=platform
|
||||
)
|
||||
|
||||
with patch.object(entity_platform, "async_call_later") as mock_call_later:
|
||||
assert not await ent_platform.async_setup_entry(config_entry)
|
||||
|
||||
full_name = f"{ent_platform.domain}.{config_entry.domain}"
|
||||
assert full_name not in hass.config.components
|
||||
assert len(async_setup_entry.mock_calls) == 1
|
||||
|
||||
assert "Platform test not ready yet" in caplog.text
|
||||
assert "The device dropped the connection" in caplog.text
|
||||
assert len(mock_call_later.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_reset_cancels_retry_setup(hass):
|
||||
"""Test that resetting a platform will cancel scheduled a setup retry."""
|
||||
async_setup_entry = Mock(side_effect=PlatformNotReady)
|
||||
@ -614,6 +660,31 @@ async def test_reset_cancels_retry_setup(hass):
|
||||
assert ent_platform._async_cancel_retry_setup is None
|
||||
|
||||
|
||||
async def test_reset_cancels_retry_setup_when_not_started(hass):
|
||||
"""Test that resetting a platform will cancel scheduled a setup retry when not yet started."""
|
||||
hass.state = CoreState.starting
|
||||
async_setup_entry = Mock(side_effect=PlatformNotReady)
|
||||
initial_listeners = hass.bus.async_listeners()[EVENT_HOMEASSISTANT_STARTED]
|
||||
|
||||
platform = MockPlatform(async_setup_entry=async_setup_entry)
|
||||
config_entry = MockConfigEntry()
|
||||
ent_platform = MockEntityPlatform(
|
||||
hass, platform_name=config_entry.domain, platform=platform
|
||||
)
|
||||
|
||||
assert not await ent_platform.async_setup_entry(config_entry)
|
||||
await hass.async_block_till_done()
|
||||
assert (
|
||||
hass.bus.async_listeners()[EVENT_HOMEASSISTANT_STARTED] == initial_listeners + 1
|
||||
)
|
||||
assert ent_platform._async_cancel_retry_setup is not None
|
||||
|
||||
await ent_platform.async_reset()
|
||||
await hass.async_block_till_done()
|
||||
assert hass.bus.async_listeners()[EVENT_HOMEASSISTANT_STARTED] == initial_listeners
|
||||
assert ent_platform._async_cancel_retry_setup is None
|
||||
|
||||
|
||||
async def test_not_fails_with_adding_empty_entities_(hass):
|
||||
"""Test for not fails on empty entities list."""
|
||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
|
Loading…
x
Reference in New Issue
Block a user