Remove options property from OptionFlow (#129890)
* Remove options property from OptionFlow * Update test_config_entries.py * Partial revert of "Remove deprecated property setters in option flows (#129773)" * Partial revert "Use new helper properties in crownstone options flow (#129774)" * Restore onewire init * Restore onvif * Restore roborock * Use deepcopy in onewire * Restore steam_online * Restore initial options property in OptionsFlowWithConfigEntry * re-add options property in SchemaOptionsFlowHandler * Restore test * Cleanuppull/129996/head
parent
53c486ccd1
commit
03d5b18974
|
@ -143,7 +143,7 @@ class CrownstoneConfigFlowHandler(BaseCrownstoneFlowHandler, ConfigFlow, domain=
|
|||
config_entry: ConfigEntry,
|
||||
) -> CrownstoneOptionsFlowHandler:
|
||||
"""Return the Crownstone options."""
|
||||
return CrownstoneOptionsFlowHandler()
|
||||
return CrownstoneOptionsFlowHandler(config_entry)
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the flow."""
|
||||
|
@ -210,9 +210,10 @@ class CrownstoneConfigFlowHandler(BaseCrownstoneFlowHandler, ConfigFlow, domain=
|
|||
class CrownstoneOptionsFlowHandler(BaseCrownstoneFlowHandler, OptionsFlow):
|
||||
"""Handle Crownstone options."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize Crownstone options."""
|
||||
super().__init__(OPTIONS_FLOW, self.async_create_new_entry)
|
||||
self.options = config_entry.options.copy()
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
|
|
|
@ -35,7 +35,7 @@ class DemoConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
config_entry: ConfigEntry,
|
||||
) -> OptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return OptionsFlowHandler()
|
||||
return OptionsFlowHandler(config_entry)
|
||||
|
||||
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Set the config entry up from yaml."""
|
||||
|
@ -45,6 +45,10 @@ class DemoConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
class OptionsFlowHandler(OptionsFlow):
|
||||
"""Handle options."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize options flow."""
|
||||
self.options = dict(config_entry.options)
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
|
|
@ -141,6 +141,10 @@ async def _async_build_schema_with_user_input(
|
|||
class OptionsFlowHandler(OptionsFlow):
|
||||
"""Handle a option flow for homekit."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize options flow."""
|
||||
self.options = dict(config_entry.options)
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
@ -211,4 +215,4 @@ class NmapTrackerConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
@callback
|
||||
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return OptionsFlowHandler()
|
||||
return OptionsFlowHandler(config_entry)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
@ -104,7 +105,7 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
config_entry: ConfigEntry,
|
||||
) -> OnewireOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return OnewireOptionsFlowHandler()
|
||||
return OnewireOptionsFlowHandler(config_entry)
|
||||
|
||||
|
||||
class OnewireOptionsFlowHandler(OptionsFlow):
|
||||
|
@ -125,6 +126,10 @@ class OnewireOptionsFlowHandler(OptionsFlow):
|
|||
current_device: str
|
||||
"""Friendly name of the currently selected device."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize options flow."""
|
||||
self.options = deepcopy(dict(config_entry.options))
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
|
|
@ -109,7 +109,7 @@ class OnvifFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
config_entry: ConfigEntry,
|
||||
) -> OnvifOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return OnvifOptionsFlowHandler()
|
||||
return OnvifOptionsFlowHandler(config_entry)
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the ONVIF config flow."""
|
||||
|
@ -389,6 +389,10 @@ class OnvifFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
class OnvifOptionsFlowHandler(OptionsFlow):
|
||||
"""Handle ONVIF options."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize ONVIF options flow."""
|
||||
self.options = dict(config_entry.options)
|
||||
|
||||
async def async_step_init(self, user_input: None = None) -> ConfigFlowResult:
|
||||
"""Manage the ONVIF options."""
|
||||
return await self.async_step_onvif_devices()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
|
@ -384,6 +385,7 @@ class PlexOptionsFlowHandler(OptionsFlow):
|
|||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize Plex options flow."""
|
||||
self.options = deepcopy(dict(config_entry.options))
|
||||
self.server_id = config_entry.data[CONF_SERVER_IDENTIFIER]
|
||||
|
||||
async def async_step_init(self, user_input: None = None) -> ConfigFlowResult:
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
|
@ -172,12 +173,16 @@ class RoborockFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
config_entry: ConfigEntry,
|
||||
) -> RoborockOptionsFlowHandler:
|
||||
"""Create the options flow."""
|
||||
return RoborockOptionsFlowHandler()
|
||||
return RoborockOptionsFlowHandler(config_entry)
|
||||
|
||||
|
||||
class RoborockOptionsFlowHandler(OptionsFlow):
|
||||
"""Handle an option flow for Roborock."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize options flow."""
|
||||
self.options = deepcopy(dict(config_entry.options))
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
|
|
@ -103,7 +103,7 @@ class SIAConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
config_entry: ConfigEntry,
|
||||
) -> SIAOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return SIAOptionsFlowHandler()
|
||||
return SIAOptionsFlowHandler(config_entry)
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
|
@ -179,8 +179,9 @@ class SIAConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
class SIAOptionsFlowHandler(OptionsFlow):
|
||||
"""Handle SIA options."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize SIA options flow."""
|
||||
self.options = deepcopy(dict(config_entry.options))
|
||||
self.hub: SIAHub | None = None
|
||||
self.accounts_todo: list = []
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
|
@ -121,14 +122,15 @@ class SomfyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
config_entry: ConfigEntry,
|
||||
) -> OptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return OptionsFlowHandler()
|
||||
return OptionsFlowHandler(config_entry)
|
||||
|
||||
|
||||
class OptionsFlowHandler(OptionsFlow):
|
||||
"""Handle a option flow for somfy_mylink."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize options flow."""
|
||||
self.options = deepcopy(dict(config_entry.options))
|
||||
self._target_id: str | None = None
|
||||
|
||||
@callback
|
||||
|
|
|
@ -42,7 +42,7 @@ class SteamFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
config_entry: SteamConfigEntry,
|
||||
) -> SteamOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return SteamOptionsFlowHandler()
|
||||
return SteamOptionsFlowHandler(config_entry)
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
|
@ -121,6 +121,10 @@ def _batch_ids(ids: list[str]) -> Iterator[list[str]]:
|
|||
class SteamOptionsFlowHandler(OptionsFlow):
|
||||
"""Handle Steam client options."""
|
||||
|
||||
def __init__(self, entry: SteamConfigEntry) -> None:
|
||||
"""Initialize options flow."""
|
||||
self.options = dict(entry.options)
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, dict[str, str]] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
|
|
@ -21,7 +21,6 @@ import voluptuous as vol
|
|||
from homeassistant.components import ssdp
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_REAUTH,
|
||||
ConfigEntry,
|
||||
ConfigEntryState,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
|
@ -38,6 +37,7 @@ from homeassistant.core import HomeAssistant, callback
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
|
||||
from . import UnifiConfigEntry
|
||||
from .const import (
|
||||
CONF_ALLOW_BANDWIDTH_SENSORS,
|
||||
CONF_ALLOW_UPTIME_SENSORS,
|
||||
|
@ -78,10 +78,10 @@ class UnifiFlowHandler(ConfigFlow, domain=UNIFI_DOMAIN):
|
|||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: UnifiConfigEntry,
|
||||
) -> UnifiOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return UnifiOptionsFlowHandler()
|
||||
return UnifiOptionsFlowHandler(config_entry)
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the UniFi Network flow."""
|
||||
|
@ -247,6 +247,10 @@ class UnifiOptionsFlowHandler(OptionsFlow):
|
|||
|
||||
hub: UnifiHub
|
||||
|
||||
def __init__(self, config_entry: UnifiConfigEntry) -> None:
|
||||
"""Initialize UniFi Network options flow."""
|
||||
self.options = dict(config_entry.options)
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
|
|
@ -3060,7 +3060,6 @@ class OptionsFlowManager(
|
|||
class OptionsFlow(ConfigEntryBaseFlow):
|
||||
"""Base class for config options flows."""
|
||||
|
||||
_options: dict[str, Any]
|
||||
handler: str
|
||||
|
||||
_config_entry: ConfigEntry
|
||||
|
@ -3127,28 +3126,6 @@ class OptionsFlow(ConfigEntryBaseFlow):
|
|||
)
|
||||
self._config_entry = value
|
||||
|
||||
@property
|
||||
def options(self) -> dict[str, Any]:
|
||||
"""Return a mutable copy of the config entry options.
|
||||
|
||||
Please note that this is not available inside `__init__` method, and
|
||||
can only be referenced after initialisation.
|
||||
"""
|
||||
if not hasattr(self, "_options"):
|
||||
self._options = deepcopy(dict(self.config_entry.options))
|
||||
return self._options
|
||||
|
||||
@options.setter
|
||||
def options(self, value: dict[str, Any]) -> None:
|
||||
"""Set the options value."""
|
||||
report(
|
||||
"sets option flow options explicitly, which is deprecated "
|
||||
"and will stop working in 2025.12",
|
||||
error_if_integration=False,
|
||||
error_if_core=True,
|
||||
)
|
||||
self._options = value
|
||||
|
||||
|
||||
class OptionsFlowWithConfigEntry(OptionsFlow):
|
||||
"""Base class for options flows with config entry and options."""
|
||||
|
@ -3164,6 +3141,11 @@ class OptionsFlowWithConfigEntry(OptionsFlow):
|
|||
error_if_core=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def options(self) -> dict[str, Any]:
|
||||
"""Return a mutable copy of the config entry options."""
|
||||
return self._options
|
||||
|
||||
|
||||
class EntityRegistryDisabledHandler:
|
||||
"""Handler when entities related to config entries updated disabled_by."""
|
||||
|
|
|
@ -421,8 +421,6 @@ class SchemaOptionsFlowHandler(OptionsFlow):
|
|||
options, which is the union of stored options and user input from the options
|
||||
flow steps.
|
||||
"""
|
||||
# Although `self.options` is most likely unused, it is safer to keep both
|
||||
# `self.options` and `self._common_handler.options` referring to the same object
|
||||
self._options = copy.deepcopy(dict(config_entry.options))
|
||||
self._common_handler = SchemaCommonFlowHandler(self, options_flow, self.options)
|
||||
self._async_options_flow_finished = async_options_flow_finished
|
||||
|
@ -437,6 +435,11 @@ class SchemaOptionsFlowHandler(OptionsFlow):
|
|||
if async_setup_preview:
|
||||
setattr(self, "async_setup_preview", async_setup_preview)
|
||||
|
||||
@property
|
||||
def options(self) -> dict[str, Any]:
|
||||
"""Return a mutable copy of the config entry options."""
|
||||
return self._options
|
||||
|
||||
@staticmethod
|
||||
def _async_step(
|
||||
step_id: str,
|
||||
|
|
|
@ -5066,31 +5066,6 @@ async def test_options_flow_with_config_entry(caplog: pytest.LogCaptureFixture)
|
|||
assert entry.options == {"sub_dict": {"1": "one"}, "sub_list": ["one"]}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_integration_frame")
|
||||
@patch.object(frame, "_REPORTED_INTEGRATIONS", set())
|
||||
async def test_options_flow_options_not_mutated(hass: HomeAssistant) -> None:
|
||||
"""Test that OptionsFlow doesn't mutate entry options."""
|
||||
entry = MockConfigEntry(
|
||||
domain="test",
|
||||
data={"first": True},
|
||||
options={"sub_dict": {"1": "one"}, "sub_list": ["one"]},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
options_flow = config_entries.OptionsFlow()
|
||||
options_flow.handler = entry.entry_id
|
||||
options_flow.hass = hass
|
||||
|
||||
options_flow.options["sub_dict"]["2"] = "two"
|
||||
options_flow._options["sub_list"].append("two")
|
||||
|
||||
assert options_flow._options == {
|
||||
"sub_dict": {"1": "one", "2": "two"},
|
||||
"sub_list": ["one", "two"],
|
||||
}
|
||||
assert entry.options == {"sub_dict": {"1": "one"}, "sub_list": ["one"]}
|
||||
|
||||
|
||||
async def test_initializing_flows_canceled_on_shutdown(
|
||||
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||
) -> None:
|
||||
|
@ -7466,6 +7441,7 @@ async def test_options_flow_config_entry(
|
|||
|
||||
|
||||
@pytest.mark.usefixtures("mock_integration_frame")
|
||||
@patch.object(frame, "_REPORTED_INTEGRATIONS", set())
|
||||
async def test_options_flow_deprecated_config_entry_setter(
|
||||
hass: HomeAssistant,
|
||||
manager: config_entries.ConfigEntries,
|
||||
|
@ -7493,10 +7469,7 @@ async def test_options_flow_deprecated_config_entry_setter(
|
|||
|
||||
def __init__(self, entry) -> None:
|
||||
"""Test initialisation."""
|
||||
with patch.object(frame, "_REPORTED_INTEGRATIONS", set()):
|
||||
self.config_entry = entry
|
||||
with patch.object(frame, "_REPORTED_INTEGRATIONS", set()):
|
||||
self.options = entry.options
|
||||
self.config_entry = entry
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Test user step."""
|
||||
|
@ -7525,10 +7498,6 @@ async def test_options_flow_deprecated_config_entry_setter(
|
|||
"Detected that integration 'hue' sets option flow config_entry explicitly, "
|
||||
"which is deprecated and will stop working in 2025.12" in caplog.text
|
||||
)
|
||||
assert (
|
||||
"Detected that integration 'hue' sets option flow options explicitly, "
|
||||
"which is deprecated and will stop working in 2025.12" in caplog.text
|
||||
)
|
||||
|
||||
|
||||
async def test_add_description_placeholder_automatically(
|
||||
|
|
Loading…
Reference in New Issue