From 8b662dc94fecc3638016108326af9696ce6298ad Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 29 May 2023 21:01:47 +0200 Subject: [PATCH] Detect attempt to setup config entry integration via YAML (#93589) --- homeassistant/helpers/config_validation.py | 2 +- homeassistant/setup.py | 14 ++++++ tests/helpers/test_config_validation.py | 8 +++- tests/test_setup.py | 54 ++++++++++++++++++++++ 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 78c63049e5f..6551b7e4709 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -866,7 +866,7 @@ def _deprecated_or_removed( logger_func(warning, *arguments) value = config[key] - if replacement_key: + if replacement_key or option_removed: config.pop(key) else: value = default diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 36b17690d7e..f0a44eaeece 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -222,6 +222,20 @@ async def _async_setup_component( log_error("Invalid config.") return False + # Detect attempt to setup integration which can be setup only from config entry + if ( + domain in processed_config + and not hasattr(component, "async_setup") + and not hasattr(component, "setup") + ): + _LOGGER.error( + ( + "The %s integration does not support YAML setup, please remove it from " + "your configuration" + ), + domain, + ) + start = timer() _LOGGER.info("Setting up %s", domain) with async_start_setup(hass, [domain]): diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index df8e989d672..58082838841 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -1048,7 +1048,10 @@ def test_deprecated_or_removed_logger_with_config_attributes( setattr(config, "__config_file__", file) setattr(config, "__line__", line) - cv.deprecated("mars", replacement_key=replacement_key, default=False)(config) + validated = cv.deprecated("mars", replacement_key=replacement_key, default=False)( + config + ) + assert "mars" not in validated # Removed because a replacement_key is defined assert len(caplog.records) == 1 assert replacement in caplog.text @@ -1063,7 +1066,8 @@ def test_deprecated_or_removed_logger_with_config_attributes( setattr(config, "__config_file__", file) setattr(config, "__line__", line) - cv.removed("mars", default=False, raise_if_present=False)(config) + validated = cv.removed("mars", default=False, raise_if_present=False)(config) + assert "mars" not in validated # Removed because by cv.removed assert len(caplog.records) == 1 assert replacement in caplog.text diff --git a/tests/test_setup.py b/tests/test_setup.py index a624929d8ff..eb4c645ecb1 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -728,3 +728,57 @@ async def test_async_start_setup_platforms(hass: HomeAssistant) -> None: assert "august" not in hass.data[setup.DATA_SETUP_STARTED] assert isinstance(hass.data[setup.DATA_SETUP_TIME]["august"], datetime.timedelta) assert "sensor" not in hass.data[setup.DATA_SETUP_TIME] + + +async def test_setup_config_entry_from_yaml( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test attempting to setup an integration which only supports config_entries.""" + expected_warning = ( + "The test_integration_only_entry integration does not support YAML setup, " + "please remove it from your configuration" + ) + + mock_integration( + hass, + MockModule( + "test_integration_only_entry", + setup=False, + async_setup_entry=AsyncMock(return_value=True), + ), + ) + + assert await setup.async_setup_component(hass, "test_integration_only_entry", {}) + assert expected_warning not in caplog.text + caplog.clear() + hass.data.pop(setup.DATA_SETUP) + hass.config.components.remove("test_integration_only_entry") + + # There should be a warning, but setup should not fail + assert await setup.async_setup_component( + hass, "test_integration_only_entry", {"test_integration_only_entry": None} + ) + assert expected_warning in caplog.text + caplog.clear() + hass.data.pop(setup.DATA_SETUP) + hass.config.components.remove("test_integration_only_entry") + + # There should be a warning, but setup should not fail + assert await setup.async_setup_component( + hass, "test_integration_only_entry", {"test_integration_only_entry": {}} + ) + assert expected_warning in caplog.text + caplog.clear() + hass.data.pop(setup.DATA_SETUP) + hass.config.components.remove("test_integration_only_entry") + + # There should be a warning, but setup should not fail + assert await setup.async_setup_component( + hass, + "test_integration_only_entry", + {"test_integration_only_entry": {"hello": "world"}}, + ) + assert expected_warning in caplog.text + caplog.clear() + hass.data.pop(setup.DATA_SETUP) + hass.config.components.remove("test_integration_only_entry")