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:
G Johansson 2024-03-15 23:15:36 +01:00 committed by GitHub
parent 77a94ea515
commit e8de1a7031
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 42 additions and 89 deletions

View file

@ -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):

View file

@ -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")

View file

@ -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

View file

@ -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",

View file

@ -219,16 +219,6 @@ async def test_form_reauth_auth(
) )
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with patch(
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_bootstrap",
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( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={ context={
@ -244,6 +234,10 @@ async def test_form_reauth_auth(
"name": "Mock Title", "name": "Mock Title",
} }
with patch(
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_bootstrap",
side_effect=NotAuthorized,
):
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(

View file

@ -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)

View file

@ -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,