Revert "Avoid pre-importing config_flows if the integration does not … (#113553)
Revert "Avoid pre-importing config_flows if the integration does not support …"
This reverts commit 9940f51b95
.
This commit is contained in:
parent
77a94ea515
commit
e8de1a7031
7 changed files with 42 additions and 89 deletions
|
@ -349,9 +349,6 @@ class ConfigEntry:
|
||||||
# Supports remove device
|
# Supports remove device
|
||||||
self.supports_remove_device: bool | None = None
|
self.supports_remove_device: bool | None = None
|
||||||
|
|
||||||
# Supports migrate
|
|
||||||
self.supports_migrate: bool | None = None
|
|
||||||
|
|
||||||
# Supports options
|
# Supports options
|
||||||
self._supports_options: bool | None = None
|
self._supports_options: bool | None = None
|
||||||
|
|
||||||
|
@ -494,7 +491,6 @@ class ConfigEntry:
|
||||||
self.supports_remove_device = await support_remove_from_device(
|
self.supports_remove_device = await support_remove_from_device(
|
||||||
hass, self.domain
|
hass, self.domain
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
component = await integration.async_get_component()
|
component = await integration.async_get_component()
|
||||||
except ImportError as err:
|
except ImportError as err:
|
||||||
|
@ -510,12 +506,7 @@ class ConfigEntry:
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.supports_migrate is None:
|
if domain_is_integration:
|
||||||
self.supports_migrate = hasattr(component, "async_migrate_entry")
|
|
||||||
|
|
||||||
if domain_is_integration and self.supports_migrate:
|
|
||||||
# Avoid loading the config_flow module unless we need to check
|
|
||||||
# the version to see if we need to migrate
|
|
||||||
try:
|
try:
|
||||||
await integration.async_get_platforms(("config_flow",))
|
await integration.async_get_platforms(("config_flow",))
|
||||||
except ImportError as err:
|
except ImportError as err:
|
||||||
|
@ -796,7 +787,11 @@ class ConfigEntry:
|
||||||
if same_major_version and self.minor_version == handler.MINOR_VERSION:
|
if same_major_version and self.minor_version == handler.MINOR_VERSION:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not self.supports_migrate:
|
if not (integration := self._integration_for_domain):
|
||||||
|
integration = await loader.async_get_integration(hass, self.domain)
|
||||||
|
component = await integration.async_get_component()
|
||||||
|
supports_migrate = hasattr(component, "async_migrate_entry")
|
||||||
|
if not supports_migrate:
|
||||||
if same_major_version:
|
if same_major_version:
|
||||||
return True
|
return True
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
|
@ -806,11 +801,6 @@ class ConfigEntry:
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not (integration := self._integration_for_domain):
|
|
||||||
integration = await loader.async_get_integration(hass, self.domain)
|
|
||||||
|
|
||||||
component = await integration.async_get_component()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = await component.async_migrate_entry(hass, self)
|
result = await component.async_migrate_entry(hass, self)
|
||||||
if not isinstance(result, bool):
|
if not isinstance(result, bool):
|
||||||
|
|
|
@ -961,7 +961,10 @@ class Integration:
|
||||||
|
|
||||||
# Some integrations fail on import because they call functions incorrectly.
|
# Some integrations fail on import because they call functions incorrectly.
|
||||||
# So we do it before validating config to catch these errors.
|
# So we do it before validating config to catch these errors.
|
||||||
load_executor = self.import_executor and self.pkg_path not in sys.modules
|
load_executor = self.import_executor and (
|
||||||
|
self.pkg_path not in sys.modules
|
||||||
|
or (self.config_flow and f"{self.pkg_path}.config_flow" not in sys.modules)
|
||||||
|
)
|
||||||
if not load_executor:
|
if not load_executor:
|
||||||
comp = self._get_component()
|
comp = self._get_component()
|
||||||
if debug:
|
if debug:
|
||||||
|
@ -1048,12 +1051,6 @@ class Integration:
|
||||||
|
|
||||||
if preload_platforms:
|
if preload_platforms:
|
||||||
for platform_name in self.platforms_exists(self._platforms_to_preload):
|
for platform_name in self.platforms_exists(self._platforms_to_preload):
|
||||||
if (
|
|
||||||
platform_name == "config_flow"
|
|
||||||
and not async_config_flow_needs_preload(cache[domain])
|
|
||||||
):
|
|
||||||
continue
|
|
||||||
|
|
||||||
with suppress(ImportError):
|
with suppress(ImportError):
|
||||||
self.get_platform(platform_name)
|
self.get_platform(platform_name)
|
||||||
|
|
||||||
|
@ -1687,15 +1684,3 @@ def async_suggest_report_issue(
|
||||||
)
|
)
|
||||||
|
|
||||||
return f"create a bug report at {issue_tracker}"
|
return f"create a bug report at {issue_tracker}"
|
||||||
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_config_flow_needs_preload(component: ComponentProtocol) -> bool:
|
|
||||||
"""Test if a config_flow for a component needs to be preloaded.
|
|
||||||
|
|
||||||
Currently we need to preload a the config flow if the integration
|
|
||||||
has a config flow and the component has an async_migrate_entry method
|
|
||||||
because it means that config_entries will always have to load
|
|
||||||
it to check if it needs to be migrated.
|
|
||||||
"""
|
|
||||||
return hasattr(component, "async_migrate_entry")
|
|
||||||
|
|
|
@ -67,7 +67,6 @@ async def test_invalid_auth(
|
||||||
|
|
||||||
mock_config_entry.add_to_hass(hass)
|
mock_config_entry.add_to_hass(hass)
|
||||||
assert not await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
assert not await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
flows = hass.config_entries.flow.async_progress()
|
flows = hass.config_entries.flow.async_progress()
|
||||||
assert len(flows) == 1
|
assert len(flows) == 1
|
||||||
|
|
|
@ -105,9 +105,7 @@ async def test_migrate_config_entry(hass: HomeAssistant) -> None:
|
||||||
assert entry.version == 1
|
assert entry.version == 1
|
||||||
assert not entry.unique_id
|
assert not entry.unique_id
|
||||||
|
|
||||||
with patch("homeassistant.components.sonarr.async_setup_entry", return_value=True):
|
await entry.async_migrate(hass)
|
||||||
await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert entry.data == {
|
assert entry.data == {
|
||||||
CONF_API_KEY: "MOCK_API_KEY",
|
CONF_API_KEY: "MOCK_API_KEY",
|
||||||
|
|
|
@ -219,31 +219,25 @@ async def test_form_reauth_auth(
|
||||||
)
|
)
|
||||||
mock_config.add_to_hass(hass)
|
mock_config.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={
|
||||||
|
"source": config_entries.SOURCE_REAUTH,
|
||||||
|
"entry_id": mock_config.entry_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert result["type"] == FlowResultType.FORM
|
||||||
|
assert not result["errors"]
|
||||||
|
flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN)
|
||||||
|
assert flows[0]["context"]["title_placeholders"] == {
|
||||||
|
"ip_address": "1.1.1.1",
|
||||||
|
"name": "Mock Title",
|
||||||
|
}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_bootstrap",
|
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_bootstrap",
|
||||||
side_effect=NotAuthorized,
|
side_effect=NotAuthorized,
|
||||||
), patch(
|
|
||||||
"homeassistant.components.unifiprotect.async_setup",
|
|
||||||
return_value=True,
|
|
||||||
) as mock_setup, patch(
|
|
||||||
"homeassistant.components.unifiprotect.async_setup_entry",
|
|
||||||
return_value=True,
|
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN,
|
|
||||||
context={
|
|
||||||
"source": config_entries.SOURCE_REAUTH,
|
|
||||||
"entry_id": mock_config.entry_id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert result["type"] == FlowResultType.FORM
|
|
||||||
assert not result["errors"]
|
|
||||||
flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN)
|
|
||||||
assert flows[0]["context"]["title_placeholders"] == {
|
|
||||||
"ip_address": "1.1.1.1",
|
|
||||||
"name": "Mock Title",
|
|
||||||
}
|
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
|
@ -263,10 +257,7 @@ async def test_form_reauth_auth(
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.unifiprotect.async_setup",
|
"homeassistant.components.unifiprotect.async_setup",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_setup, patch(
|
) as mock_setup:
|
||||||
"homeassistant.components.unifiprotect.async_setup_entry",
|
|
||||||
return_value=True,
|
|
||||||
):
|
|
||||||
result3 = await hass.config_entries.flow.async_configure(
|
result3 = await hass.config_entries.flow.async_configure(
|
||||||
result2["flow_id"],
|
result2["flow_id"],
|
||||||
{
|
{
|
||||||
|
@ -777,20 +768,6 @@ async def test_discovered_by_unifi_discovery_direct_connect_on_different_interfa
|
||||||
)
|
)
|
||||||
mock_config.add_to_hass(hass)
|
mock_config.add_to_hass(hass)
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.unifiprotect.async_setup_entry",
|
|
||||||
return_value=True,
|
|
||||||
) as mock_setup_entry, patch(
|
|
||||||
"homeassistant.components.unifiprotect.async_setup",
|
|
||||||
return_value=True,
|
|
||||||
) as mock_setup:
|
|
||||||
await hass.config_entries.async_setup(mock_config.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert mock_config.state == config_entries.ConfigEntryState.LOADED
|
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
|
||||||
assert len(mock_setup.mock_calls) == 1
|
|
||||||
|
|
||||||
other_ip_dict = UNIFI_DISCOVERY_DICT.copy()
|
other_ip_dict = UNIFI_DISCOVERY_DICT.copy()
|
||||||
other_ip_dict["source_ip"] = "127.0.0.2"
|
other_ip_dict["source_ip"] = "127.0.0.2"
|
||||||
other_ip_dict["direct_connect_domain"] = "nomatchsameip.ui.direct"
|
other_ip_dict["direct_connect_domain"] = "nomatchsameip.ui.direct"
|
||||||
|
@ -846,7 +823,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_on_different_interfa
|
||||||
"verify_ssl": True,
|
"verify_ssl": True,
|
||||||
}
|
}
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
assert len(mock_setup.mock_calls) == 0
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_discovered_by_unifi_discovery_direct_connect_on_different_interface_resolver_no_result(
|
async def test_discovered_by_unifi_discovery_direct_connect_on_different_interface_resolver_no_result(
|
||||||
|
|
|
@ -117,7 +117,6 @@ async def test_call_setup_entry(hass: HomeAssistant) -> None:
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
assert entry.state is config_entries.ConfigEntryState.LOADED
|
assert entry.state is config_entries.ConfigEntryState.LOADED
|
||||||
assert entry.supports_unload
|
assert entry.supports_unload
|
||||||
assert entry.supports_migrate
|
|
||||||
|
|
||||||
|
|
||||||
async def test_call_setup_entry_without_reload_support(hass: HomeAssistant) -> None:
|
async def test_call_setup_entry_without_reload_support(hass: HomeAssistant) -> None:
|
||||||
|
@ -147,7 +146,6 @@ async def test_call_setup_entry_without_reload_support(hass: HomeAssistant) -> N
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
assert entry.state is config_entries.ConfigEntryState.LOADED
|
assert entry.state is config_entries.ConfigEntryState.LOADED
|
||||||
assert not entry.supports_unload
|
assert not entry.supports_unload
|
||||||
assert entry.supports_migrate
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("major_version", "minor_version"), [(2, 1), (1, 2), (2, 2)])
|
@pytest.mark.parametrize(("major_version", "minor_version"), [(2, 1), (1, 2), (2, 2)])
|
||||||
|
@ -291,7 +289,6 @@ async def test_call_async_migrate_entry_failure_not_supported(
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
assert not entry.supports_unload
|
assert not entry.supports_unload
|
||||||
entry.supports_migrate = True
|
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(return_value=True)
|
mock_setup_entry = AsyncMock(return_value=True)
|
||||||
|
|
||||||
|
|
|
@ -1204,21 +1204,31 @@ async def test_async_get_component_loads_loop_if_already_in_sys_modules(
|
||||||
assert "test_package_loaded_executor" not in hass.config.components
|
assert "test_package_loaded_executor" not in hass.config.components
|
||||||
assert "test_package_loaded_executor.config_flow" not in hass.config.components
|
assert "test_package_loaded_executor.config_flow" not in hass.config.components
|
||||||
|
|
||||||
module_mock = object()
|
config_flow_module_name = f"{integration.pkg_path}.config_flow"
|
||||||
|
module_mock = MagicMock(__file__="__init__.py")
|
||||||
|
config_flow_module_mock = MagicMock(__file__="config_flow.py")
|
||||||
|
|
||||||
def import_module(name: str) -> Any:
|
def import_module(name: str) -> Any:
|
||||||
if name == integration.pkg_path:
|
if name == integration.pkg_path:
|
||||||
return module_mock
|
return module_mock
|
||||||
|
if name == config_flow_module_name:
|
||||||
|
return config_flow_module_mock
|
||||||
raise ImportError
|
raise ImportError
|
||||||
|
|
||||||
|
modules_without_config_flow = {
|
||||||
|
k: v for k, v in sys.modules.items() if k != config_flow_module_name
|
||||||
|
}
|
||||||
with patch.dict(
|
with patch.dict(
|
||||||
"sys.modules",
|
"sys.modules",
|
||||||
{**sys.modules, integration.pkg_path: module_mock},
|
{**modules_without_config_flow, integration.pkg_path: module_mock},
|
||||||
clear=True,
|
clear=True,
|
||||||
), patch("homeassistant.loader.importlib.import_module", import_module):
|
), patch("homeassistant.loader.importlib.import_module", import_module):
|
||||||
module = await integration.async_get_component()
|
module = await integration.async_get_component()
|
||||||
|
|
||||||
assert "loaded_executor=False" in caplog.text
|
# The config flow is missing so we should load
|
||||||
|
# in the executor
|
||||||
|
assert "loaded_executor=True" in caplog.text
|
||||||
|
assert "loaded_executor=False" not in caplog.text
|
||||||
assert module is module_mock
|
assert module is module_mock
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
|
|
||||||
|
@ -1226,6 +1236,7 @@ async def test_async_get_component_loads_loop_if_already_in_sys_modules(
|
||||||
"sys.modules",
|
"sys.modules",
|
||||||
{
|
{
|
||||||
integration.pkg_path: module_mock,
|
integration.pkg_path: module_mock,
|
||||||
|
config_flow_module_name: config_flow_module_mock,
|
||||||
},
|
},
|
||||||
), patch("homeassistant.loader.importlib.import_module", import_module):
|
), patch("homeassistant.loader.importlib.import_module", import_module):
|
||||||
module = await integration.async_get_component()
|
module = await integration.async_get_component()
|
||||||
|
@ -1235,10 +1246,6 @@ async def test_async_get_component_loads_loop_if_already_in_sys_modules(
|
||||||
assert "loaded_executor" not in caplog.text
|
assert "loaded_executor" not in caplog.text
|
||||||
assert module is module_mock
|
assert module is module_mock
|
||||||
|
|
||||||
# The integration does not implement async_migrate_entry so it
|
|
||||||
# should not be preloaded
|
|
||||||
assert integration.get_platform_cached("config_flow") is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_get_component_concurrent_loads(
|
async def test_async_get_component_concurrent_loads(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|
Loading…
Add table
Reference in a new issue