def _recursive_merge(conf: Dict[str, Any], package: Dict[str, Any]) -> Union[bool, str]: """Merge package into conf, recursively.""" error: Union[bool, str] = False for key, pack_conf in package.items(): if isinstance(pack_conf, dict): if not pack_conf: continue conf[key] = conf.get(key, OrderedDict()) error = _recursive_merge(conf=conf[key], package=pack_conf) elif isinstance(pack_conf, list): conf[key] = cv.remove_falsy( cv.ensure_list(conf.get(key)) + cv.ensure_list(pack_conf) ) else: if conf.get(key) is not None: return key conf[key] = pack_conf return error
def test_remove_falsy(): """Test remove falsy.""" assert cv.remove_falsy([0, None, 1, "1", {}, [], ""]) == [1, "1"]
async def merge_packages_config( hass: HomeAssistant, config: Dict, packages: Dict[str, Any], _log_pkg_error: Callable = _log_pkg_error, ) -> Dict: """Merge packages into the top-level configuration. Mutate config.""" # pylint: disable=too-many-nested-blocks PACKAGES_CONFIG_SCHEMA(packages) for pack_name, pack_conf in packages.items(): for comp_name, comp_conf in pack_conf.items(): if comp_name == CONF_CORE: continue # If component name is given with a trailing description, remove it # when looking for component domain = comp_name.split(" ")[0] try: integration = await async_get_integration_with_requirements( hass, domain ) component = integration.get_component() except (IntegrationNotFound, RequirementsNotFound, ImportError) as ex: _log_pkg_error(pack_name, comp_name, config, str(ex)) continue merge_list = hasattr(component, "PLATFORM_SCHEMA") if not merge_list and hasattr(component, "CONFIG_SCHEMA"): merge_type, _ = _identify_config_schema(component) merge_list = merge_type == "list" if merge_list: config[comp_name] = cv.remove_falsy( cv.ensure_list(config.get(comp_name)) + cv.ensure_list(comp_conf) ) continue if comp_conf is None: comp_conf = OrderedDict() if not isinstance(comp_conf, dict): _log_pkg_error( pack_name, comp_name, config, "cannot be merged. Expected a dict." ) continue if comp_name not in config or config[comp_name] is None: config[comp_name] = OrderedDict() if not isinstance(config[comp_name], dict): _log_pkg_error( pack_name, comp_name, config, "cannot be merged. Dict expected in main config.", ) continue error = _recursive_merge(conf=config[comp_name], package=comp_conf) if error: _log_pkg_error( pack_name, comp_name, config, f"has duplicate key '{error}'" ) return config
async def merge_packages_config( hass: HomeAssistant, config: dict, packages: dict[str, Any], _log_pkg_error: Callable = _log_pkg_error, ) -> dict: """Merge packages into the top-level configuration. Mutate config.""" PACKAGES_CONFIG_SCHEMA(packages) for pack_name, pack_conf in packages.items(): for comp_name, comp_conf in pack_conf.items(): if comp_name == CONF_CORE: continue # If component name is given with a trailing description, remove it # when looking for component domain = comp_name.split(" ")[0] try: integration = await async_get_integration_with_requirements( hass, domain) component = integration.get_component() except INTEGRATION_LOAD_EXCEPTIONS as ex: _log_pkg_error(pack_name, comp_name, config, str(ex)) continue try: config_platform: ModuleType | None = integration.get_platform( "config") # Test if config platform has a config validator if not hasattr(config_platform, "async_validate_config"): config_platform = None except ImportError: config_platform = None merge_list = False # If integration has a custom config validator, it needs to provide a hint. if config_platform is not None: merge_list = config_platform.PACKAGE_MERGE_HINT == "list" # type: ignore[attr-defined] if not merge_list: merge_list = hasattr(component, "PLATFORM_SCHEMA") if not merge_list and hasattr(component, "CONFIG_SCHEMA"): merge_list = _identify_config_schema(component) == "list" if merge_list: config[comp_name] = cv.remove_falsy( cv.ensure_list(config.get(comp_name)) + cv.ensure_list(comp_conf)) continue if comp_conf is None: comp_conf = OrderedDict() if not isinstance(comp_conf, dict): _log_pkg_error(pack_name, comp_name, config, "cannot be merged. Expected a dict.") continue if comp_name not in config or config[comp_name] is None: config[comp_name] = OrderedDict() if not isinstance(config[comp_name], dict): _log_pkg_error( pack_name, comp_name, config, "cannot be merged. Dict expected in main config.", ) continue error = _recursive_merge(conf=config[comp_name], package=comp_conf) if error: _log_pkg_error(pack_name, comp_name, config, f"has duplicate key '{error}'") return config