Avoid falling back to event loop import on ModuleNotFound (#115404)

This commit is contained in:
J. Nick Koston 2024-04-11 16:32:47 -10:00 committed by GitHub
parent 28bdbec14e
commit fb5fc136e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 94 additions and 0 deletions

View File

@ -976,6 +976,8 @@ class Integration:
comp = await self.hass.async_add_import_executor_job(
self._get_component, True
)
except ModuleNotFoundError:
raise
except ImportError as ex:
load_executor = False
_LOGGER.debug(
@ -1115,6 +1117,8 @@ class Integration:
self._load_platforms, platform_names
)
)
except ModuleNotFoundError:
raise
except ImportError as ex:
_LOGGER.debug(
"Failed to import %s platforms %s in executor",

View File

@ -1471,6 +1471,50 @@ async def test_async_get_component_deadlock_fallback(
assert module is module_mock
async def test_async_get_component_deadlock_fallback_module_not_found(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Verify async_get_component fallback behavior.
Ensure that fallback is not triggered on ModuleNotFoundError.
"""
executor_import_integration = _get_test_integration(
hass, "executor_import", True, import_executor=True
)
assert executor_import_integration.import_executor is True
module_mock = MagicMock(__file__="__init__.py")
import_attempts = 0
def mock_import(module: str, *args: Any, **kwargs: Any) -> Any:
nonlocal import_attempts
if module == "homeassistant.components.executor_import":
import_attempts += 1
if import_attempts == 1:
raise ModuleNotFoundError(
"homeassistant.components.executor_import not found",
name="homeassistant.components.executor_import",
)
return module_mock
assert "homeassistant.components.executor_import" not in sys.modules
assert "custom_components.executor_import" not in sys.modules
with (
patch("homeassistant.loader.importlib.import_module", mock_import),
pytest.raises(
ModuleNotFoundError, match="homeassistant.components.executor_import"
),
):
await executor_import_integration.async_get_component()
# We should not have tried to fall back to the event loop import
assert "loaded_executor=False" not in caplog.text
assert "homeassistant.components.executor_import" not in sys.modules
assert "custom_components.executor_import" not in sys.modules
assert import_attempts == 1
async def test_async_get_component_raises_after_import_failure(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
@ -1551,6 +1595,52 @@ async def test_async_get_platform_deadlock_fallback(
assert module is module_mock
async def test_async_get_platform_deadlock_fallback_module_not_found(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Verify async_get_platform fallback behavior.
Ensure that fallback is not triggered on ModuleNotFoundError.
"""
executor_import_integration = _get_test_integration(
hass, "executor_import", True, import_executor=True
)
assert executor_import_integration.import_executor is True
module_mock = MagicMock()
import_attempts = 0
def mock_import(module: str, *args: Any, **kwargs: Any) -> Any:
nonlocal import_attempts
if module == "homeassistant.components.executor_import.config_flow":
import_attempts += 1
if import_attempts == 1:
raise ModuleNotFoundError(
"Not found homeassistant.components.executor_import.config_flow",
name="homeassistant.components.executor_import.config_flow",
)
return module_mock
assert "homeassistant.components.executor_import" not in sys.modules
assert "custom_components.executor_import" not in sys.modules
with (
patch("homeassistant.loader.importlib.import_module", mock_import),
pytest.raises(
ModuleNotFoundError,
match="homeassistant.components.executor_import.config_flow",
),
):
await executor_import_integration.async_get_platform("config_flow")
# We should not have tried to fall back to the event loop import
assert "executor=['config_flow']" in caplog.text
assert "loop=['config_flow']" not in caplog.text
assert "homeassistant.components.executor_import" not in sys.modules
assert "custom_components.executor_import" not in sys.modules
assert import_attempts == 1
async def test_async_get_platform_raises_after_import_failure(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None: