Make integration setup optional (#48381)

This commit is contained in:
Franck Nijhof 2021-03-29 22:53:47 +02:00 committed by GitHub
parent dda9f957b6
commit 52475c108f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 39 additions and 77 deletions

View File

@ -8,7 +8,6 @@ from aiohttp.client_exceptions import ClientConnectorError
from async_timeout import timeout
from homeassistant.const import CONF_API_KEY
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@ -26,12 +25,6 @@ _LOGGER = logging.getLogger(__name__)
PLATFORMS = ["sensor", "weather"]
async def async_setup(hass: HomeAssistant, config: Config) -> bool:
"""Set up configured AccuWeather."""
hass.data.setdefault(DOMAIN, {})
return True
async def async_setup_entry(hass, config_entry) -> bool:
"""Set up AccuWeather as config entry."""
api_key = config_entry.data[CONF_API_KEY]
@ -52,7 +45,7 @@ async def async_setup_entry(hass, config_entry) -> bool:
undo_listener = config_entry.add_update_listener(update_listener)
hass.data[DOMAIN][config_entry.entry_id] = {
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = {
COORDINATOR: coordinator,
UNDO_UPDATE_LISTENER: undo_listener,
}

View File

@ -34,7 +34,6 @@ from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType
_LOGGER = logging.getLogger(__name__)
@ -49,11 +48,6 @@ SERVICE_REFRESH_SCHEMA = vol.Schema(
PLATFORMS = ["sensor", "switch"]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the AdGuard Home components."""
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up AdGuard Home from a config entry."""
session = async_get_clientsession(hass, entry.data[CONF_VERIFY_SSL])

View File

@ -38,11 +38,6 @@ _LOGGER = logging.getLogger(__name__)
PLATFORMS = ["sensor"]
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the AirNow component."""
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up AirNow from a config entry."""
api_key = entry.data[CONF_API_KEY]

View File

@ -20,11 +20,6 @@ CONFIG_SCHEMA = vol.Schema(
)
async def async_setup(hass, config):
"""Old way of setting up deCONZ integrations."""
return True
async def async_setup_entry(hass, config_entry):
"""Set up a deCONZ bridge for a config entry.

View File

@ -9,16 +9,10 @@ from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType
from .const import DATA_ELGATO_CLIENT, DOMAIN
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Elgato Key Light components."""
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Elgato Key Light from a config entry."""
session = async_get_clientsession(hass)

View File

@ -42,7 +42,7 @@ from homeassistant.helpers.json import JSONEncoder
from homeassistant.helpers.service import async_set_service_schema
from homeassistant.helpers.storage import Store
from homeassistant.helpers.template import Template
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from homeassistant.helpers.typing import HomeAssistantType
# Import config flow so that it's added to the registry
from .entry_data import RuntimeEntryData
@ -56,11 +56,6 @@ STORAGE_VERSION = 1
CONFIG_SCHEMA = vol.Schema({}, extra=vol.ALLOW_EXTRA)
async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
"""Stub to allow setting up this component."""
return True
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool:
"""Set up the esphome component."""
hass.data.setdefault(DOMAIN, {})

View File

@ -24,7 +24,6 @@ from homeassistant.helpers.device_registry import (
EVENT_DEVICE_REGISTRY_UPDATED,
async_entries_for_config_entry,
)
from homeassistant.helpers.typing import HomeAssistantType
from . import device_automation, discovery
from .const import (
@ -38,11 +37,6 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass: HomeAssistantType, config: dict):
"""Set up the Tasmota component."""
return True
async def async_setup_entry(hass, entry):
"""Set up Tasmota from a config entry."""
websocket_api.async_register_command(hass, websocket_remove_device)

View File

@ -16,7 +16,6 @@ from homeassistant.const import ATTR_NAME, CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
@ -37,11 +36,6 @@ PLATFORMS = (LIGHT_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN)
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the WLED components."""
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up WLED from a config entry."""

View File

@ -169,7 +169,7 @@ async def _async_setup_component(
return False
if integration.disabled:
log_error(f"dependency is disabled - {integration.disabled}")
log_error(f"Dependency is disabled - {integration.disabled}")
return False
# Validate all dependencies exist and there are no circular dependencies
@ -219,6 +219,8 @@ async def _async_setup_component(
SLOW_SETUP_WARNING,
)
task = None
result = True
try:
if hasattr(component, "async_setup"):
task = component.async_setup(hass, processed_config) # type: ignore
@ -228,13 +230,14 @@ async def _async_setup_component(
task = hass.loop.run_in_executor(
None, component.setup, hass, processed_config # type: ignore
)
else:
log_error("No setup function defined.")
elif not hasattr(component, "async_setup_entry"):
log_error("No setup or config entry setup function defined.")
hass.data[DATA_SETUP_STARTED].pop(domain)
return False
async with hass.timeout.async_timeout(SLOW_SETUP_MAX_WAIT, domain):
result = await task
if task:
async with hass.timeout.async_timeout(SLOW_SETUP_MAX_WAIT, domain):
result = await task
except asyncio.TimeoutError:
_LOGGER.error(
"Setup of %s is taking longer than %s seconds."

View File

@ -2,7 +2,6 @@
from __future__ import annotations
import asyncio
from typing import Any
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -14,11 +13,6 @@ from .const import DOMAIN
PLATFORMS = ["light"]
async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool:
"""Set up the NEW_NAME component."""
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up NEW_NAME from a config entry."""
# TODO Store an API object for your platforms to access

View File

@ -20,8 +20,6 @@ async def test_form(hass: HomeAssistant) -> None:
"homeassistant.components.NEW_DOMAIN.config_flow.PlaceholderHub.authenticate",
return_value=True,
), patch(
"homeassistant.components.NEW_DOMAIN.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.NEW_DOMAIN.async_setup_entry",
return_value=True,
) as mock_setup_entry:
@ -42,7 +40,6 @@ async def test_form(hass: HomeAssistant) -> None:
"username": "test-username",
"password": "test-password",
}
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1

View File

@ -2,7 +2,6 @@
from __future__ import annotations
import asyncio
from typing import Any
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -14,11 +13,6 @@ from .const import DOMAIN
PLATFORMS = ["light"]
async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool:
"""Set up the NEW_NAME component."""
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up NEW_NAME from a config entry."""
# TODO Store an API object for your platforms to access

View File

@ -566,7 +566,7 @@ class MockModule:
if platform_schema_base is not None:
self.PLATFORM_SCHEMA_BASE = platform_schema_base
if setup is not None:
if setup:
# We run this in executor, wrap it in function
self.setup = lambda *args: setup(*args)

View File

@ -75,9 +75,7 @@ async def test_form(hass):
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {}
with patch("pyairnow.WebServiceAPI._get", return_value=MOCK_RESPONSE,), patch(
"homeassistant.components.airnow.async_setup", return_value=True
) as mock_setup, patch(
with patch("pyairnow.WebServiceAPI._get", return_value=MOCK_RESPONSE), patch(
"homeassistant.components.airnow.async_setup_entry",
return_value=True,
) as mock_setup_entry:
@ -90,7 +88,6 @@ async def test_form(hass):
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result2["data"] == CONFIG
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1

View File

@ -539,8 +539,6 @@ async def test_flow_hassio_discovery(hass):
assert result["description_placeholders"] == {"addon": "Mock Addon"}
with patch(
"homeassistant.components.deconz.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.deconz.async_setup_entry",
return_value=True,
) as mock_setup_entry:
@ -555,7 +553,6 @@ async def test_flow_hassio_discovery(hass):
CONF_PORT: 80,
CONF_API_KEY: API_KEY,
}
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1

View File

@ -3,7 +3,7 @@
import asyncio
import os
import threading
from unittest.mock import Mock, patch
from unittest.mock import AsyncMock, Mock, patch
import pytest
import voluptuous as vol
@ -618,3 +618,29 @@ async def test_async_get_loaded_integrations(hass):
"myintegration",
"device_tracker",
}
async def test_integration_no_setup(hass, caplog):
"""Test we fail integration setup without setup functions."""
mock_integration(
hass,
MockModule("test_integration_without_setup", setup=False),
)
result = await setup.async_setup_component(
hass, "test_integration_without_setup", {}
)
assert not result
assert "No setup or config entry setup function defined" in caplog.text
async def test_integration_only_setup_entry(hass):
"""Test we have an integration with only a setup entry method."""
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", {})