mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 07:37:34 +00:00
Deprecate the usage of ContextVar for config_entry in coordinator (#138161)
This commit is contained in:
parent
e8fca19335
commit
9d178ad5f1
@ -84,9 +84,19 @@ class DataUpdateCoordinator(BaseDataUpdateCoordinatorProtocol, Generic[_DataT]):
|
|||||||
self.update_interval = update_interval
|
self.update_interval = update_interval
|
||||||
self._shutdown_requested = False
|
self._shutdown_requested = False
|
||||||
if config_entry is UNDEFINED:
|
if config_entry is UNDEFINED:
|
||||||
|
# late import to avoid circular imports
|
||||||
|
from . import frame # noqa: PLC0415
|
||||||
|
|
||||||
|
# It is not planned to enforce this for custom integrations.
|
||||||
|
# see https://github.com/home-assistant/core/pull/138161#discussion_r1958184241
|
||||||
|
frame.report_usage(
|
||||||
|
"relies on ContextVar, but should pass the config entry explicitly.",
|
||||||
|
core_behavior=frame.ReportBehavior.ERROR,
|
||||||
|
custom_integration_behavior=frame.ReportBehavior.LOG,
|
||||||
|
breaks_in_ha_version="2026.8",
|
||||||
|
)
|
||||||
|
|
||||||
self.config_entry = config_entries.current_entry.get()
|
self.config_entry = config_entries.current_entry.get()
|
||||||
# This should be deprecated once all core integrations are updated
|
|
||||||
# to pass in the config entry explicitly.
|
|
||||||
else:
|
else:
|
||||||
self.config_entry = config_entry
|
self.config_entry = config_entry
|
||||||
self.always_update = always_update
|
self.always_update = always_update
|
||||||
|
@ -19,7 +19,7 @@ from homeassistant.exceptions import (
|
|||||||
ConfigEntryError,
|
ConfigEntryError,
|
||||||
ConfigEntryNotReady,
|
ConfigEntryNotReady,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers import update_coordinator
|
from homeassistant.helpers import frame, update_coordinator
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
@ -165,8 +165,6 @@ async def test_shutdown_on_entry_unload(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test shutdown is requested on entry unload."""
|
"""Test shutdown is requested on entry unload."""
|
||||||
entry = MockConfigEntry()
|
entry = MockConfigEntry()
|
||||||
config_entries.current_entry.set(entry)
|
|
||||||
|
|
||||||
calls = 0
|
calls = 0
|
||||||
|
|
||||||
async def _refresh() -> int:
|
async def _refresh() -> int:
|
||||||
@ -177,6 +175,7 @@ async def test_shutdown_on_entry_unload(
|
|||||||
crd = update_coordinator.DataUpdateCoordinator[int](
|
crd = update_coordinator.DataUpdateCoordinator[int](
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
|
config_entry=entry,
|
||||||
name="test",
|
name="test",
|
||||||
update_method=_refresh,
|
update_method=_refresh,
|
||||||
update_interval=DEFAULT_UPDATE_INTERVAL,
|
update_interval=DEFAULT_UPDATE_INTERVAL,
|
||||||
@ -206,6 +205,7 @@ async def test_shutdown_on_hass_stop(
|
|||||||
crd = update_coordinator.DataUpdateCoordinator[int](
|
crd = update_coordinator.DataUpdateCoordinator[int](
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
|
config_entry=None,
|
||||||
name="test",
|
name="test",
|
||||||
update_method=_refresh,
|
update_method=_refresh,
|
||||||
update_interval=DEFAULT_UPDATE_INTERVAL,
|
update_interval=DEFAULT_UPDATE_INTERVAL,
|
||||||
@ -843,6 +843,7 @@ async def test_timestamp_date_update_coordinator(hass: HomeAssistant) -> None:
|
|||||||
crd = update_coordinator.TimestampDataUpdateCoordinator[int](
|
crd = update_coordinator.TimestampDataUpdateCoordinator[int](
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
|
config_entry=None,
|
||||||
name="test",
|
name="test",
|
||||||
update_method=refresh,
|
update_method=refresh,
|
||||||
update_interval=timedelta(seconds=10),
|
update_interval=timedelta(seconds=10),
|
||||||
@ -865,39 +866,133 @@ async def test_timestamp_date_update_coordinator(hass: HomeAssistant) -> None:
|
|||||||
assert len(last_update_success_times) == 1
|
assert len(last_update_success_times) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_config_entry(hass: HomeAssistant) -> None:
|
@pytest.mark.parametrize(
|
||||||
|
"integration_frame_path", ["homeassistant/components/my_integration"]
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("mock_integration_frame")
|
||||||
|
async def test_config_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
"""Test behavior of coordinator.entry."""
|
"""Test behavior of coordinator.entry."""
|
||||||
entry = MockConfigEntry()
|
entry = MockConfigEntry()
|
||||||
|
|
||||||
# Default without context should be None
|
|
||||||
crd = update_coordinator.DataUpdateCoordinator[int](hass, _LOGGER, name="test")
|
|
||||||
assert crd.config_entry is None
|
|
||||||
|
|
||||||
# Explicit None is OK
|
# Explicit None is OK
|
||||||
crd = update_coordinator.DataUpdateCoordinator[int](
|
crd = update_coordinator.DataUpdateCoordinator[int](
|
||||||
hass, _LOGGER, name="test", config_entry=None
|
hass, _LOGGER, name="test", config_entry=None
|
||||||
)
|
)
|
||||||
assert crd.config_entry is None
|
assert crd.config_entry is None
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
# Explicit entry is OK
|
# Explicit entry is OK
|
||||||
|
caplog.clear()
|
||||||
crd = update_coordinator.DataUpdateCoordinator[int](
|
crd = update_coordinator.DataUpdateCoordinator[int](
|
||||||
hass, _LOGGER, name="test", config_entry=entry
|
hass, _LOGGER, name="test", config_entry=entry
|
||||||
)
|
)
|
||||||
assert crd.config_entry is entry
|
assert crd.config_entry is entry
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
# Explicit entry different from ContextVar not recommended, but should work
|
||||||
|
another_entry = MockConfigEntry()
|
||||||
|
caplog.clear()
|
||||||
|
crd = update_coordinator.DataUpdateCoordinator[int](
|
||||||
|
hass, _LOGGER, name="test", config_entry=another_entry
|
||||||
|
)
|
||||||
|
assert crd.config_entry is another_entry
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
# Default without context should log a warning
|
||||||
|
caplog.clear()
|
||||||
|
crd = update_coordinator.DataUpdateCoordinator[int](hass, _LOGGER, name="test")
|
||||||
|
assert crd.config_entry is None
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar, "
|
||||||
|
"but should pass the config entry explicitly."
|
||||||
|
) in caplog.text
|
||||||
|
|
||||||
|
# Default with context should log a warning
|
||||||
|
caplog.clear()
|
||||||
|
frame._REPORTED_INTEGRATIONS.clear()
|
||||||
|
config_entries.current_entry.set(entry)
|
||||||
|
crd = update_coordinator.DataUpdateCoordinator[int](hass, _LOGGER, name="test")
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar, "
|
||||||
|
"but should pass the config entry explicitly."
|
||||||
|
) in caplog.text
|
||||||
|
assert crd.config_entry is entry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("integration_frame_path", ["custom_components/my_integration"])
|
||||||
|
@pytest.mark.usefixtures("hass", "mock_integration_frame")
|
||||||
|
async def test_config_entry_custom_integration(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test behavior of coordinator.entry for custom integrations."""
|
||||||
|
entry = MockConfigEntry(domain="custom_integration")
|
||||||
|
|
||||||
|
# Default without context should be None
|
||||||
|
crd = update_coordinator.DataUpdateCoordinator[int](hass, _LOGGER, name="test")
|
||||||
|
assert crd.config_entry is None
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
# Explicit None is OK
|
||||||
|
caplog.clear()
|
||||||
|
crd = update_coordinator.DataUpdateCoordinator[int](
|
||||||
|
hass, _LOGGER, name="test", config_entry=None
|
||||||
|
)
|
||||||
|
assert crd.config_entry is None
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
# Explicit entry is OK
|
||||||
|
caplog.clear()
|
||||||
|
crd = update_coordinator.DataUpdateCoordinator[int](
|
||||||
|
hass, _LOGGER, name="test", config_entry=entry
|
||||||
|
)
|
||||||
|
assert crd.config_entry is entry
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
# set ContextVar
|
# set ContextVar
|
||||||
config_entries.current_entry.set(entry)
|
config_entries.current_entry.set(entry)
|
||||||
|
|
||||||
# Default with ContextVar should match the ContextVar
|
# Default with ContextVar should match the ContextVar
|
||||||
|
caplog.clear()
|
||||||
crd = update_coordinator.DataUpdateCoordinator[int](hass, _LOGGER, name="test")
|
crd = update_coordinator.DataUpdateCoordinator[int](hass, _LOGGER, name="test")
|
||||||
assert crd.config_entry is entry
|
assert crd.config_entry is entry
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
# Explicit entry different from ContextVar not recommended, but should work
|
# Explicit entry different from ContextVar not recommended, but should work
|
||||||
another_entry = MockConfigEntry()
|
another_entry = MockConfigEntry()
|
||||||
|
caplog.clear()
|
||||||
crd = update_coordinator.DataUpdateCoordinator[int](
|
crd = update_coordinator.DataUpdateCoordinator[int](
|
||||||
hass, _LOGGER, name="test", config_entry=another_entry
|
hass, _LOGGER, name="test", config_entry=another_entry
|
||||||
)
|
)
|
||||||
assert crd.config_entry is another_entry
|
assert crd.config_entry is another_entry
|
||||||
|
assert (
|
||||||
|
"Detected that integration 'my_integration' relies on ContextVar"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_listener_unsubscribe_releases_coordinator(hass: HomeAssistant) -> None:
|
async def test_listener_unsubscribe_releases_coordinator(hass: HomeAssistant) -> None:
|
||||||
@ -920,7 +1015,7 @@ async def test_listener_unsubscribe_releases_coordinator(hass: HomeAssistant) ->
|
|||||||
self._unsub = None
|
self._unsub = None
|
||||||
|
|
||||||
coordinator = update_coordinator.DataUpdateCoordinator[int](
|
coordinator = update_coordinator.DataUpdateCoordinator[int](
|
||||||
hass, _LOGGER, name="test"
|
hass, _LOGGER, config_entry=None, name="test"
|
||||||
)
|
)
|
||||||
subscriber = Subscriber()
|
subscriber = Subscriber()
|
||||||
subscriber.start_listen(coordinator)
|
subscriber.start_listen(coordinator)
|
||||||
|
@ -4901,6 +4901,7 @@ async def test_setup_raise_entry_error_from_first_coordinator_update(
|
|||||||
hass,
|
hass,
|
||||||
logging.getLogger(__name__),
|
logging.getLogger(__name__),
|
||||||
name="any",
|
name="any",
|
||||||
|
config_entry=entry,
|
||||||
update_method=_async_update_data,
|
update_method=_async_update_data,
|
||||||
update_interval=timedelta(seconds=1000),
|
update_interval=timedelta(seconds=1000),
|
||||||
)
|
)
|
||||||
@ -4941,6 +4942,7 @@ async def test_setup_not_raise_entry_error_from_future_coordinator_update(
|
|||||||
hass,
|
hass,
|
||||||
logging.getLogger(__name__),
|
logging.getLogger(__name__),
|
||||||
name="any",
|
name="any",
|
||||||
|
config_entry=entry,
|
||||||
update_method=_async_update_data,
|
update_method=_async_update_data,
|
||||||
update_interval=timedelta(seconds=1000),
|
update_interval=timedelta(seconds=1000),
|
||||||
)
|
)
|
||||||
@ -5020,6 +5022,7 @@ async def test_setup_raise_auth_failed_from_first_coordinator_update(
|
|||||||
hass,
|
hass,
|
||||||
logging.getLogger(__name__),
|
logging.getLogger(__name__),
|
||||||
name="any",
|
name="any",
|
||||||
|
config_entry=entry,
|
||||||
update_method=_async_update_data,
|
update_method=_async_update_data,
|
||||||
update_interval=timedelta(seconds=1000),
|
update_interval=timedelta(seconds=1000),
|
||||||
)
|
)
|
||||||
@ -5072,6 +5075,7 @@ async def test_setup_raise_auth_failed_from_future_coordinator_update(
|
|||||||
hass,
|
hass,
|
||||||
logging.getLogger(__name__),
|
logging.getLogger(__name__),
|
||||||
name="any",
|
name="any",
|
||||||
|
config_entry=entry,
|
||||||
update_method=_async_update_data,
|
update_method=_async_update_data,
|
||||||
update_interval=timedelta(seconds=1000),
|
update_interval=timedelta(seconds=1000),
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user