Example #1
0
def get_bib_info(*infos, logger=None):
    """
    Gathers and returns the descriptions and bibliographic sources for the components
    mentioned in ``infos``.

    ``infos`` can be input dictionaries or single component names.
    """
    if not logger:
        logger_setup()
        logger = get_logger("bib")
    used_components, component_infos = get_used_components(*infos,
                                                           return_infos=True)
    descs: InfoDict = {}
    bibs: InfoDict = {}
    used_components = get_used_components(*infos)
    for kind, components in used_components.items():
        if kind is None:
            continue  # we will deal with bare component names later, to avoid repetition
        descs[kind], bibs[kind] = {}, {}
        for component in components:
            try:
                descs[kind][component] = get_desc_component(
                    component, kind, component_infos[component])
                bibs[kind][component] = get_bib_component(component, kind)
            except ComponentNotFoundError:
                sugg = similar_internal_class_names(component)
                logger.error(
                    f"Could not identify component '{component}'. "
                    f"Did you mean any of the following? {sugg} (mind capitalization!)"
                )
                continue
    # Deal with bare component names
    for component in used_components.get(None, []):
        try:
            cls = get_component_class(component)
        except ComponentNotFoundError:
            sugg = similar_internal_class_names(component)
            logger.error(
                f"Could not identify component '{component}'. "
                f"Did you mean any of the following? {sugg} (mind capitalization!)"
            )
            continue
        kind = cls.get_kind()
        if kind not in descs:
            descs[kind], bibs[kind] = {}, {}
        if kind in descs and component in descs[kind]:
            continue  # avoid repetition
        descs[kind][component] = get_desc_component(cls, kind)
        bibs[kind][component] = get_bib_component(cls, kind)
    descs["cobaya"] = {"cobaya": cobaya_desc}
    bibs["cobaya"] = {"cobaya": cobaya_bib}
    return descs, bibs
Example #2
0
def get_bib_info(*infos):
    used_components, component_infos = get_used_components(*infos, return_infos=True)
    descs = {}
    bibs = {}
    for kind, components in get_used_components(*infos).items():
        descs[kind], bibs[kind] = {}, {}
        for component in components:
            descs[kind][component] = get_desc_component(
                component, kind, component_infos[component])
            bibs[kind][component] = get_bib_component(component, kind)
    descs["cobaya"] = {"cobaya": cobaya_desc}
    bibs["cobaya"] = {"cobaya": cobaya_bib}
    return descs, bibs
Example #3
0
def create_docker_image(filenames, MPI_version=None):
    log.info("Creating Docker image...")
    if not MPI_version:
        MPI_version = "3.2"
        # log.warning("You have not specified an MPICH version. "
        #          "It is strongly encouraged to request the one installed in your cluster,"
        #          " using '--mpi-version X.Y'. Defaulting to MPICH v%s.", MPI_version)
    dc = get_docker_client()
    components = yaml_dump(
        get_used_components(*[load_input(f) for f in filenames])).strip()
    echos_reqs = "RUN " + " && \\ \n    ".join([
        r'echo "%s" >> %s' % (block, requirements_file_path)
        for block in components.split("\n")
    ])
    echos_help = "RUN " + " && \\ \n    ".join([
        r'echo "%s" >> %s' % (line, help_file_path)
        for line in image_help("docker").split("\n")
    ])
    recipe = r"""
    FROM cobaya/base_mpich_%s:latest
    %s
    RUN cobaya-install %s --%s %s --just-code --force ### NEEDS PYTHON UPDATE! --no-progress-bars
    %s
    CMD ["cat", "%s"]
    """ % (MPI_version, echos_reqs, requirements_file_path, _packages_path_arg,
           _packages_path, echos_help, help_file_path)
    image_name = "cobaya:" + uuid.uuid4().hex[:6]
    with StringIO(recipe) as stream:
        dc.images.build(fileobj=stream, tag=image_name)
    log.info(
        "Docker image '%s' created! "
        "Do 'docker save %s | gzip > some_name.tar.gz'"
        "to save it to the current folder.", image_name, image_name)
Example #4
0
def get_bib_info(*infos):
    blocks_text = {"Cobaya": cobaya_bib}
    for kind, components in get_used_components(*infos).items():
        for component in components:
            blocks_text["%s:%s" % (kind, component)] = get_bib_component(
                component, kind)
    return blocks_text
Example #5
0
def create_singularity_image(filenames, MPI_version=None):
    log.info("Creating Singularity image...")
    if not MPI_version:
        MPI_version = "2.1.1"
        # log.warning("You have not specified an OpenMPI version. "
        #          "It is strongly encouraged to request the one installed in your cluster,"
        #          " using '--mpi-version X.Y.Z'. Defaulting to OpenMPI v%s.", MPI_version)
    components = yaml_dump(
        get_used_components(*[load_input(f) for f in filenames])).strip()
    echos_reqs = "\n    " + "\n    ".join([""] + [
        'echo "%s" >> %s' % (block, requirements_file_path)
        for block in components.split("\n")
    ])
    recipe = (
        dedent("""
        Bootstrap: docker
        From: cobaya/base_openmpi_%s:latest\n
        %%post\n""" % MPI_version) + dedent(echos_reqs) + dedent("""
        export CONTAINED=TRUE
        cobaya-install %s --%s %s --just-code --force ### --no-progress-bars
        mkdir $COBAYA_PRODUCTS

        %%help

        %s
        """ % (
            requirements_file_path,
            # TODO: this looks wrong?
            packages_path_input,
            os.path.join(packages_path_arg, packages_path_input, data_path),
            "\n        ".join(image_help("singularity").split("\n")[1:]))))
    with NamedTemporaryFile(delete=False) as recipe_file:
        recipe_file.write(recipe.encode('utf-8'))
        recipe_file_name = recipe_file.name
    image_name = "cobaya_" + uuid.uuid4().hex[:6] + ".simg"
    process_build = Popen(
        ["singularity", "build", image_name, recipe_file_name],
        stdout=PIPE,
        stderr=PIPE)
    out, err = process_build.communicate()
    if process_build.returncode:
        log.info(out)
        log.info(err)
        raise LoggedError(log,
                          "Image creation failed! See error message above.")
    log.info("Singularity image '%s' created!", image_name)
Example #6
0
def install(*infos, **kwargs):
    if not log.root.handlers:
        logger_setup()
    path = kwargs.get("path")
    if not path:
        path = resolve_packages_path(infos)
    if not path:
        raise LoggedError(
            log,
            "No 'path' argument given, and none could be found in input infos "
            "(as %r), the %r env variable or the config file. "
            "Maybe specify one via a command line argument '-%s [...]'?",
            _packages_path, _packages_path_env, _packages_path_arg[0])
    abspath = os.path.abspath(path)
    log.info("Installing external packages at '%s'", abspath)
    kwargs_install = {
        "force": kwargs.get("force", False),
        "no_progress_bars": kwargs.get("no_progress_bars")
    }
    for what in (_code, _data):
        kwargs_install[what] = kwargs.get(what, True)
        spath = os.path.join(abspath, what)
        if kwargs_install[what] and not os.path.exists(spath):
            try:
                os.makedirs(spath)
            except OSError:
                raise LoggedError(
                    log,
                    "Could not create the desired installation folder '%s'",
                    spath)
    failed_components = []
    skip_keywords = set(kwargs.get("skip", []) or [])
    skip_keywords_env = set(
        os.environ.get(_install_skip_env, "").replace(",",
                                                      " ").lower().split())
    skip_keywords = skip_keywords.union(skip_keywords_env)
    for kind, components in get_used_components(*infos).items():
        for component in components:
            print()
            print(create_banner(kind + ":" + component, symbol="=", length=80))
            print()
            if _skip_helper(component.lower(), skip_keywords,
                            skip_keywords_env, log):
                continue
            info = (next(
                info for info in infos
                if component in info.get(kind, {}))[kind][component]) or {}
            if isinstance(info, str) or _external in info:
                log.warning(
                    "Component '%s' is a custom function. "
                    "Nothing to do.", component)
                continue
            try:
                imported_class = get_class(component,
                                           kind,
                                           component_path=info.pop(
                                               _component_path, None))
            except ImportError as e:
                log.error("Component '%s' not recognized. [%s]." %
                          (component, e))
                failed_components += ["%s:%s" % (kind, component)]
                continue
            else:
                if _skip_helper(imported_class.__name__.lower(), skip_keywords,
                                skip_keywords_env, log):
                    continue
            is_installed = getattr(imported_class, "is_installed", None)
            if is_installed is None:
                log.info("%s.%s is a fully built-in component: nothing to do.",
                         kind, imported_class.__name__)
                continue
            if is_installed(path=abspath, **kwargs_install):
                log.info("External component already installed.")
                if kwargs.get("just_check", False):
                    continue
                if kwargs_install["force"]:
                    log.info("Forcing re-installation, as requested.")
                else:
                    log.info("Doing nothing.")
                    continue
            else:
                if kwargs.get("just_check", False):
                    log.info("NOT INSTALLED!")
                    continue
            try:
                install_this = getattr(imported_class, "install", None)
                success = install_this(path=abspath, **kwargs_install)
            except KeyboardInterrupt:
                raise
            except:
                traceback.print_exception(*sys.exc_info(), file=sys.stdout)
                log.error(
                    "An unknown error occurred. Delete the external packages "
                    "folder %r and try again. "
                    "Please, notify the developers if this error persists.",
                    abspath)
                success = False
            if success:
                log.info("Successfully installed!")
            else:
                log.error(
                    "Installation failed! Look at the error messages above. "
                    "Solve them and try again, or, if you are unable to solve, "
                    "install the packages required by this component manually."
                )
                failed_components += ["%s:%s" % (kind, component)]
                continue
            # test installation
            if not is_installed(path=abspath, **kwargs_install):
                log.error(
                    "Installation apparently worked, "
                    "but the subsequent installation test failed! "
                    "Look at the error messages above. "
                    "Solve them and try again, or, if you are unable to solve, "
                    "install the packages required by this component manually."
                )
                failed_components += ["%s:%s" % (kind, component)]
    if failed_components:
        bullet = "\n - "
        raise LoggedError(
            log,
            "The installation (or installation test) of some component(s) has "
            "failed: %s\nCheck output of the installer of each component above "
            "for precise error info.\n",
            bullet + bullet.join(failed_components))
    # Set the installation path in the global config file
    if not kwargs.get("no_set_global", False) and not kwargs.get(
            "just_check", False):
        write_packages_path_in_config_file(abspath)
        log.info(
            "The installation path has been written in the global config file."
        )
Example #7
0
def makeGrid(batchPath,
             settingName=None,
             settings=None,
             read_only=False,
             interactive=False,
             install_reqs_at=None,
             install_reqs_force=None):
    print("Generating grid...")
    batchPath = os.path.abspath(batchPath) + os.sep
    if not settings:
        if not settingName:
            raise NotImplementedError(
                "Re-using previous batch is work in progress...")
        #            if not pathIsGrid(batchPath):
        #                raise Exception('Need to give name of setting file if batchPath/config '
        #                                'does not exist')
        #            read_only = True
        #            sys.path.insert(0, batchPath + 'config')
        #            settings = __import__(IniFile(batchPath + 'config/config.ini').params['setting_file'].replace('.py', ''))
        elif os.path.splitext(settingName)[-1].lower() in _yaml_extensions:
            settings = yaml_load_file(settingName)
        else:
            raise NotImplementedError(
                "Using a python script is work in progress...")
            # In this case, info-as-dict would be passed
            # settings = __import__(settingName, fromlist=['dummy'])
    batch = batchjob.BatchJob(batchPath)
    # batch.skip = settings.get("skip", False)
    batch.makeItems(settings, messages=not read_only)
    if read_only:
        for jobItem in [b for b in batch.jobItems]:
            if not jobItem.chainExists():
                batch.jobItems.remove(jobItem)
        batch.save()
        print('OK, configured grid with %u existing chains' %
              (len(batch.jobItems)))
        return batch
    else:
        batch.makeDirectories(setting_file=None)
        batch.save()
    infos = {}
    components_used = {}
    # Default info
    defaults = copy.deepcopy(settings)
    grid_definition = defaults.pop("grid")
    models_definitions = grid_definition["models"]
    datasets_definitions = grid_definition["datasets"]
    for jobItem in batch.items(wantSubItems=False):
        # Model info
        jobItem.makeChainPath()
        try:
            model_info = copy.deepcopy(models_definitions[jobItem.param_set]
                                       or {})
        except KeyError:
            raise ValueError("Model '%s' must be defined." % jobItem.param_set)
        model_info = merge_info(defaults, model_info)
        # Dataset info
        try:
            dataset_info = copy.deepcopy(
                datasets_definitions[jobItem.data_set.tag])
        except KeyError:
            raise ValueError("Data set '%s' must be defined." %
                             jobItem.data_set.tag)
        # Combined info
        combined_info = merge_info(defaults, model_info, dataset_info)
        if "preset" in combined_info:
            preset = combined_info.pop("preset")
            combined_info = merge_info(create_input(**preset), combined_info)
        combined_info[_output_prefix] = jobItem.chainRoot
        # Requisites
        components_used = get_used_components(components_used, combined_info)
        if install_reqs_at:
            combined_info[_packages_path] = os.path.abspath(install_reqs_at)
        # Save the info (we will write it after installation:
        # we need to install to add auto covmats
        if jobItem.param_set not in infos:
            infos[jobItem.param_set] = {}
        infos[jobItem.param_set][jobItem.data_set.tag] = combined_info
    # Installing requisites
    if install_reqs_at:
        print("Installing required code and data for the grid.")
        from cobaya.log import logger_setup
        logger_setup()
        install_reqs(components_used,
                     path=install_reqs_at,
                     force=install_reqs_force)
    print("Adding covmats (if necessary) and writing input files")
    for jobItem in batch.items(wantSubItems=False):
        info = infos[jobItem.param_set][jobItem.data_set.tag]
        # Covariance matrices
        # We try to find them now, instead of at run time, to check if correctly selected
        try:
            sampler = list(info[kinds.sampler])[0]
        except KeyError:
            raise ValueError("No sampler has been chosen")
        if sampler == "mcmc" and info[kinds.sampler][sampler].get(
                "covmat", "auto"):
            packages_path = install_reqs_at or info.get(_packages_path, None)
            if not packages_path:
                raise ValueError(
                    "Cannot assign automatic covariance matrices because no "
                    "external packages path has been defined.")
            # Need updated info for covmats: includes renames
            updated_info = update_info(info)
            # Ideally, we use slow+sampled parameters to look for the covariance matrix
            # but since for that we'd need to initialise a model, we approximate that set
            # as theory+sampled
            from itertools import chain
            like_params = set(
                chain(*[
                    list(like[_params])
                    for like in updated_info[kinds.likelihood].values()
                ]))
            params_info = {
                p: v
                for p, v in updated_info[_params].items()
                if is_sampled_param(v) and p not in like_params
            }
            best_covmat = _get_best_covmat(os.path.abspath(packages_path),
                                           params_info,
                                           updated_info[kinds.likelihood])
            info[kinds.sampler][sampler]["covmat"] = os.path.join(
                best_covmat["folder"], best_covmat["name"])
        # Write the info for this job
        # Allow overwrite since often will want to regenerate grid with tweaks
        yaml_dump_file(jobItem.iniFile(),
                       sort_cosmetic(info),
                       error_if_exists=False)

        # Non-translated old code
        # if not start_at_bestfit:
        #     setMinimize(jobItem, ini)
        #     variant = '_minimize'
        #     ini.saveFile(jobItem.iniFile(variant))
        ## NOT IMPLEMENTED: start at best fit
        ##        ini.params['start_at_bestfit'] = start_at_bestfit
        # ---
        # for deffile in settings.defaults:
        #    ini.defaults.append(batch.commonPath + deffile)
        # if hasattr(settings, 'override_defaults'):
        #    ini.defaults = [batch.commonPath + deffile for deffile in settings.override_defaults] + ini.defaults
        # ---
        # # add ini files for importance sampling runs
        # for imp in jobItem.importanceJobs():
        #     if getattr(imp, 'importanceFilter', None): continue
        #     if batch.hasName(imp.name.replace('_post', '')):
        #         raise Exception('importance sampling something you already have?')
        #     for minimize in (False, True):
        #         if minimize and not getattr(imp, 'want_minimize', True): continue
        #         ini = IniFile()
        #         updateIniParams(ini, imp.importanceSettings, batch.commonPath)
        #         if cosmomcAction == 0 and not minimize:
        #             for deffile in settings.importanceDefaults:
        #                 ini.defaults.append(batch.commonPath + deffile)
        #             ini.params['redo_outroot'] = imp.chainRoot
        #             ini.params['action'] = 1
        #         else:
        #             ini.params['file_root'] = imp.chainRoot
        #         if minimize:
        #             setMinimize(jobItem, ini)
        #             variant = '_minimize'
        #         else:
        #             variant = ''
        #         ini.defaults.append(jobItem.iniFile())
        #         ini.saveFile(imp.iniFile(variant))
        #         if cosmomcAction != 0: break

    if not interactive:
        return batch
    print('Done... to run do: cobaya-grid-run %s' % batchPath)
Example #8
0
def install(*infos, **kwargs):
    debug = kwargs.get(_debug)
    if not log.root.handlers:
        logger_setup()
    path = kwargs.get("path")
    if not path:
        path = resolve_packages_path(infos)
    if not path:
        raise LoggedError(
            log, "No 'path' argument given, and none could be found in input infos "
                 "(as %r), the %r env variable or the config file. "
                 "Maybe specify one via a command line argument '-%s [...]'?",
            _packages_path, _packages_path_env, _packages_path_arg[0])
    abspath = os.path.abspath(path)
    log.info("Installing external packages at '%s'", abspath)
    kwargs_install = {"force": kwargs.get("force", False),
                      "no_progress_bars": kwargs.get("no_progress_bars")}
    for what in (_code, _data):
        kwargs_install[what] = kwargs.get(what, True)
        spath = os.path.join(abspath, what)
        if kwargs_install[what] and not os.path.exists(spath):
            try:
                os.makedirs(spath)
            except OSError:
                raise LoggedError(
                    log, "Could not create the desired installation folder '%s'", spath)
    failed_components = []
    skip_keywords_arg = set(kwargs.get("skip", []) or [])
    # NB: if passed with quotes as `--skip "a b"`, it's interpreted as a single key
    skip_keywords_arg = set(chain(*[word.split() for word in skip_keywords_arg]))
    skip_keywords_env = set(
        os.environ.get(_install_skip_env, "").replace(",", " ").lower().split())
    skip_keywords = skip_keywords_arg.union(skip_keywords_env)
    for kind, components in get_used_components(*infos).items():
        for component in components:
            print()
            print(create_banner(kind + ":" + component,
                                symbol=_banner_symbol, length=_banner_length), end="")
            print()
            if _skip_helper(component.lower(), skip_keywords, skip_keywords_env, log):
                continue
            info = (next(info for info in infos if component in
                         info.get(kind, {}))[kind][component]) or {}
            if isinstance(info, str) or _external in info:
                log.warning("Component '%s' is a custom function. "
                            "Nothing to do.", component)
                continue
            try:
                imported_class = get_class(component, kind,
                                           component_path=info.pop(_component_path, None))
            except ImportError as excpt:
                log.error("Component '%s' not recognized. [%s].", component, excpt)
                failed_components += ["%s:%s" % (kind, component)]
                continue
            else:
                if _skip_helper(imported_class.__name__.lower(), skip_keywords,
                                skip_keywords_env, log):
                    continue
            is_compatible = getattr(imported_class, "is_compatible", lambda: True)()
            if not is_compatible:
                log.info(
                    "Skipping %r because it is not compatible with your OS.", component)
                continue
            log.info("Checking if dependencies have already been installed...")
            is_installed = getattr(imported_class, "is_installed", None)
            if is_installed is None:
                log.info("%s.%s is a fully built-in component: nothing to do.",
                         kind, imported_class.__name__)
                continue
            install_path = abspath
            get_path = getattr(imported_class, "get_path", None)
            if get_path:
                install_path = get_path(install_path)
            has_been_installed = False
            if not debug:
                logging.disable(logging.ERROR)
            if kwargs.get("skip_global"):
                has_been_installed = is_installed(path="global", **kwargs_install)
            if not has_been_installed:
                has_been_installed = is_installed(path=install_path, **kwargs_install)
            if not debug:
                logging.disable(logging.NOTSET)
            if has_been_installed:
                log.info("External dependencies for this component already installed.")
                if kwargs.get(_test_run, False):
                    continue
                if kwargs_install["force"] and not kwargs.get("skip_global"):
                    log.info("Forcing re-installation, as requested.")
                else:
                    log.info("Doing nothing.")
                    continue
            else:
                log.info("Installation check failed!")
                if not debug:
                    log.info(
                        "(If you expected this to be already installed, re-run "
                        "`cobaya-install` with --debug to get more verbose output.)")
                if kwargs.get(_test_run, False):
                    continue
                log.info("Installing...")
            try:
                install_this = getattr(imported_class, "install", None)
                success = install_this(path=abspath, **kwargs_install)
            except KeyboardInterrupt:
                raise
            except:
                traceback.print_exception(*sys.exc_info(), file=sys.stdout)
                log.error("An unknown error occurred. Delete the external packages "
                          "folder %r and try again. "
                          "Please, notify the developers if this error persists.",
                          abspath)
                success = False
            if success:
                log.info("Successfully installed! Let's check it...")
            else:
                log.error("Installation failed! Look at the error messages above. "
                          "Solve them and try again, or, if you are unable to solve, "
                          "install the packages required by this component manually.")
                failed_components += ["%s:%s" % (kind, component)]
                continue
            # test installation
            if not debug:
                logging.disable(logging.ERROR)
            successfully_installed = is_installed(path=install_path, **kwargs_install)
            if not debug:
                logging.disable(logging.NOTSET)
            if not successfully_installed:
                log.error("Installation apparently worked, "
                          "but the subsequent installation test failed! "
                          "Look at the error messages above, or re-run with --debug "
                          "for more more verbose output. "
                          "Try to solve the issues and try again, or, if you are unable "
                          "to solve them, install the packages required by this "
                          "component manually.")
                failed_components += ["%s:%s" % (kind, component)]
            else:
                log.info("Installation check successful.")
    print()
    print(create_banner(" * Summary * ",
                        symbol=_banner_symbol, length=_banner_length), end="")
    print()
    if failed_components:
        bullet = "\n - "
        raise LoggedError(
            log, "The installation (or installation test) of some component(s) has "
                 "failed: %s\nCheck output of the installer of each component above "
                 "for precise error info.\n",
            bullet + bullet.join(failed_components))
    log.info("All requested components' dependencies correctly installed.")
    # Set the installation path in the global config file
    if not kwargs.get("no_set_global", False) and not kwargs.get(_test_run, False):
        write_packages_path_in_config_file(abspath)
        log.info("The installation path has been written into the global config file: %s",
                 os.path.join(get_config_path(), _packages_path_config_file))
Example #9
0
def install(*infos, **kwargs):
    """
    Installs the external packages required by the components mentioned in ``infos``.

    ``infos`` can be input dictionaries or single component names.

    :param force: force re-installation of apparently installed packages (default:
       ``False``).
    :param test: just check whether components are installed  (default: ``False``).
    :param upgrade: force upgrade of obsolete components (default: ``False``).
    :param skip: keywords of components that will be skipped during installation.
    :param skip_global: skip installation of already-available Python modules (default:
       ``False``).
    :param debug: produce verbose debug output  (default: ``False``).
    :param code: set to ``False`` to skip code packages (default: ``True``).
    :param data: set to ``False`` to skip data packages (default: ``True``).
    :param no_progress_bars: no progress bars shown; use when output is saved into a text
       file (e.g. when running on a cluster) (default: ``False``).
    :param no_set_global: do not store the installation path for later runs (default:
       ``False``).
    """
    debug = kwargs.get("debug", False)
    logger = kwargs.get("logger")
    if not logger:
        logger_setup(debug=debug)
        logger = get_logger("install")
    path = kwargs.get("path")
    infos_not_single_names = [
        info for info in infos if isinstance(info, Mapping)
    ]
    if not path:
        path = resolve_packages_path(*infos_not_single_names)
    if not path:
        raise LoggedError(logger, (
            "No 'path' argument given, and none could be found in input infos "
            "(as %r), the %r env variable or the config file. "
            "Maybe specify one via a command line argument '-%s [...]'?"),
                          packages_path_input, packages_path_env,
                          packages_path_arg[0])
    # General install path for all dependencies
    general_abspath = os.path.abspath(path)
    logger.info("Installing external packages at '%s'", general_abspath)
    # Set the installation path in the global config file
    if not kwargs.get("no_set_global", False) and not kwargs.get(
            "test", False):
        write_packages_path_in_config_file(general_abspath)
        logger.info(
            "The installation path has been written into the global config file: %s",
            os.path.join(get_config_path(), packages_path_config_file))
    kwargs_install = {
        "force": kwargs.get("force", False),
        "no_progress_bars": kwargs.get("no_progress_bars")
    }
    for what in (code_path, data_path):
        kwargs_install[what] = kwargs.get(what, True)
        spath = os.path.join(general_abspath, what)
        if kwargs_install[what] and not os.path.exists(spath):
            try:
                os.makedirs(spath)
            except OSError:
                raise LoggedError(
                    logger,
                    f"Could not create the desired installation folder '{spath}'"
                )
    # To check e.g. for a version upgrade, it needs to reload the component class and
    # all relevant imported modules: the implementation of `is_installed` for each
    # class is expected to always reload external modules if passed `reload=True`
    # (should be False by default to avoid deleting objects unnecessarily).
    kwargs_is_installed = {"reload": True}
    unknown_components = []  # could not be identified
    failed_components = []  # general errors
    obsolete_components = []  # older or unknown version already installed
    skip_keywords_arg = set(kwargs.get("skip", []) or [])
    # NB: if passed with quotes as `--skip "a b"`, it's interpreted as a single key
    skip_keywords_arg = set(
        chain(*[word.split() for word in skip_keywords_arg]))
    skip_keywords_env = set(
        os.environ.get(install_skip_env, "").replace(",", " ").lower().split())
    skip_keywords = skip_keywords_arg.union(skip_keywords_env)
    # Combine all requested components and install them
    # NB: components mentioned by name may be repeated with those given in dict infos.
    #     That's OK, because the install check will skip them in the 2nd pass
    used_components, components_infos = get_used_components(*infos,
                                                            return_infos=True)
    for kind, components in used_components.items():
        for component in components:
            name_w_kind = (kind + ":" if kind else "") + component
            print()
            print(create_banner(name_w_kind,
                                symbol=_banner_symbol,
                                length=_banner_length),
                  end="")
            print()
            if _skip_helper(component.lower(), skip_keywords,
                            skip_keywords_env, logger):
                continue
            info = components_infos[component]
            if isinstance(info, str) or "external" in info:
                logger.info(
                    f"Component '{name_w_kind}' is a custom function. Nothing to do."
                )
                continue
            try:
                class_name = (info or {}).get("class")
                if class_name:
                    logger.info(
                        f"Class to be installed for this component: {class_name}"
                    )
                imported_class = get_component_class(component,
                                                     kind=kind,
                                                     component_path=info.pop(
                                                         "python_path", None),
                                                     class_name=class_name,
                                                     logger=logger)
                # Update the name if the kind was unknown
                if not kind:
                    name_w_kind = imported_class.get_kind() + ":" + component
            except ComponentNotFoundError:
                logger.error(
                    f"Component '{name_w_kind}' could not be identified. Skipping."
                )
                unknown_components += [name_w_kind]
                continue
            except Exception:
                traceback.print_exception(*sys.exc_info(), file=sys.stdout)
                logger.error(
                    f"An error occurred when loading '{name_w_kind}'. Skipping."
                )
                failed_components += [name_w_kind]
                continue
            else:
                if _skip_helper(imported_class.__name__.lower(), skip_keywords,
                                skip_keywords_env, logger):
                    continue
            is_compatible = getattr(imported_class, "is_compatible",
                                    lambda: True)()
            if not is_compatible:
                logger.error(f"Skipping '{name_w_kind}' "
                             "because it is not compatible with your OS.")
                failed_components += [name_w_kind]
                continue
            logger.info(
                "Checking if dependencies have already been installed...")
            is_installed = getattr(imported_class, "is_installed", None)
            if is_installed is None:
                logger.info(
                    f"Component '{name_w_kind}' is a fully built-in component: "
                    "nothing to do.")
                continue
            this_component_install_path = general_abspath
            get_path = getattr(imported_class, "get_path", None)
            if get_path:
                this_component_install_path = get_path(
                    this_component_install_path)
            # Check previous installations and their versions
            has_been_installed = False
            is_old_version_msg = None
            with NoLogging(None if debug else logging.ERROR):
                try:
                    if kwargs.get("skip_global"):
                        has_been_installed = is_installed(
                            path="global",
                            **kwargs_install,
                            **kwargs_is_installed)
                    if not has_been_installed:
                        has_been_installed = is_installed(
                            path=this_component_install_path,
                            **kwargs_install,
                            **kwargs_is_installed)
                except VersionCheckError as excpt:
                    is_old_version_msg = str(excpt)
            if has_been_installed:  # no VersionCheckError was raised
                logger.info(
                    "External dependencies for this component already installed."
                )
                if kwargs.get("test", False):
                    continue
                if kwargs_install["force"] and not kwargs.get("skip_global"):
                    logger.info("Forcing re-installation, as requested.")
                else:
                    logger.info("Doing nothing.")
                    continue
            elif is_old_version_msg:
                logger.info(f"Version check failed: {is_old_version_msg}")
                obsolete_components += [name_w_kind]
                if kwargs.get("test", False):
                    continue
                if not kwargs.get("upgrade", False) and not kwargs.get(
                        "force", False):
                    logger.info("Skipping because '--upgrade' not requested.")
                    continue
            else:
                logger.info("Check found no existing installation")
                if not debug:
                    logger.info(
                        "(If you expected this to be already installed, re-run "
                        "`cobaya-install` with --debug to get more verbose output.)"
                    )
                if kwargs.get("test", False):
                    # We are only testing whether it was installed, so consider it failed
                    failed_components += [name_w_kind]
                    continue
            # Do the install
            logger.info("Installing...")
            try:
                install_this = getattr(imported_class, "install", None)
                success = install_this(path=general_abspath, **kwargs_install)
            except Exception:
                traceback.print_exception(*sys.exc_info(), file=sys.stdout)
                logger.error(
                    "An unknown error occurred. Delete the external packages "
                    "folder %r and try again. "
                    "Please, notify the developers if this error persists.",
                    general_abspath)
                success = False
            if success:
                logger.info("Successfully installed! Let's check it...")
            else:
                logger.error(
                    "Installation failed! Look at the error messages above. "
                    "Solve them and try again, or, if you are unable to solve them, "
                    "install the packages required by this component manually."
                )
                failed_components += [name_w_kind]
                continue
            # Test installation
            reloaded_class = get_component_class(component,
                                                 kind=kind,
                                                 component_path=info.pop(
                                                     "python_path", None),
                                                 class_name=class_name,
                                                 logger=logger)
            reloaded_is_installed = getattr(reloaded_class, "is_installed",
                                            None)
            with NoLogging(None if debug else logging.ERROR):
                try:
                    successfully_installed = reloaded_is_installed(
                        path=this_component_install_path,
                        **kwargs_install,
                        **kwargs_is_installed)
                except Exception:
                    traceback.print_exception(*sys.exc_info(), file=sys.stdout)
                    successfully_installed = False
            if not successfully_installed:
                logger.error(
                    "Installation apparently worked, "
                    "but the subsequent installation test failed! "
                    "This does not always mean that there was an actual error, "
                    "and is sometimes fixed simply by running the installer "
                    "again. If not, look closely at the error messages above, "
                    "or re-run with --debug for more more verbose output. "
                    "If you are unable to fix the issues above, "
                    "try installing the packages required by this "
                    "component manually.")
                failed_components += [name_w_kind]
            else:
                logger.info("Installation check successful.")
    print()
    print(create_banner(" * Summary * ",
                        symbol=_banner_symbol,
                        length=_banner_length),
          end="")
    print()
    bullet = "\n - "
    if unknown_components:
        suggestions_dict = {
            name: similar_internal_class_names(name)
            for name in unknown_components
        }
        suggestions_msg = \
            bullet + bullet.join(
                f"{name}: did you mean any of the following? {sugg} "
                "(mind capitalization!)" for name, sugg in suggestions_dict.items())
        raise LoggedError(logger, (
            "The following components could not be identified and were skipped:"
            f"{suggestions_msg}"))
    if failed_components:
        raise LoggedError(logger, (
            "The installation (or installation test) of some component(s) has "
            "failed: %s\nCheck output of the installer of each component above "
            "for precise error info.\n"),
                          bullet + bullet.join(failed_components))
    if obsolete_components:
        raise LoggedError(logger, (
            "The following packages are obsolete. Re-run with `--upgrade` option"
            " (not upgrading by default to preserve possible user changes): %s"
        ), bullet + bullet.join(obsolete_components))
    if not unknown_components and not failed_components and not obsolete_components:
        logger.info(
            "All requested components' dependencies correctly installed at "
            f"{general_abspath}")