Skip to content

Latest commit

 

History

History
906 lines (752 loc) · 33.6 KB

cEP-0014.md

File metadata and controls

906 lines (752 loc) · 33.6 KB

Creating .coafile sections using quickstart

Metadata
cEP 14
Version 1.0
Title Creating .coafile sections using quickstart
Authors Satwik Kansal mailto:satwikkansal@gmail.com
Status Active
Type Process

Abstract

This cEP proposes a framework for coala-quickstart to generate more relevant .coafile sections based on project files and user preferences.

Current flow of coala-quickstart

  • Find all the files from the project directory, check .gitignore for files to ignore.
  • Get the languages used in the project and their percentage from file extensions.
  • Filter bears based on the languages used in the project and curated sets of important bears.
  • Generate a .coafile by creating a section for each language, adding filtered bears corresponding to the language, and prompting the user for non-optional settings.

Objectives

This first objective involves modifying the existing filter_relevant_bears method in the Bears module in coala-quickstart to select and filter bears based on:

  • Languages detected in the project.
  • Linters already used by the project.
  • The current IMPORTANT_BEARS_LIST in Constants.py
  • Information provided by users regarding the capabilities (CAN_DETECT, CAN_FIX labels)
  • Installed bear dependencies

The second objective involves filling in the setting values for .coafile sections.

Process

Categorizing the files from which the information is extracted.

Based on the information they may provide, the target files of InfoExtractor classes can be categorized into following two categories:

  1. Dependency files (Examples: package.json, requirements.txt and Gemfile)
  2. Setting-values file (Examples: .editorconfig, .gitignore and .csslintrc)

Some files might belong both the categories (like Gruntfile.js and Gulpfile.js).

The dependency files can be used to prompt users to activate certain Bears if all the dependencies in Bear's REQUIREMENT field are present in the dependency files. The setting-values files, on the other hand, are used to fill in the settings of the Bears and to detect conflicting configurations in the project.

Both categories of files, taken together, can complement each other to provide a scenario where "A dependency file finds BearA that can be activated, and the settings of BearA are derived by the information extracted from setting-values files" thereby automating the complete section generation process in some cases.

Collecting the meta-data

This section explains the meta-data that is required to be collected for filtering bears and generating sections. The data to be collected at runtime is as follows:

  1. Inverse Mappings of requirement types to the bears.
REQUIREMENTS_META = {
    “requirement_name” : {
        “requirement_type” : NpmRequirement,
        "version" : ">=0.4.2"
        “bears” : [ list, of, bears, wrapping, the, executable],
    }
}

This will be used to match against the ProjectDependencyInfo extracted from the project. In the case of a match, the corresponding bears may be suggested to the user.

  1. Mapping of language and the corresponding Bears available in coala-bears.
{
    "language_1": ["Bear1", "Bear2"],
    "language_2": ["Bear3", "Bear4"]
}

This will be generated at the runtime and will be used to filter bears based on languages used in the project.

  1. Inverse Mappings of the CAN_DETECT and CAN_FIX capabilities metadata provided in the bears to the name of the bears. Example:
CAPABILITIES_MAP = {
    “capability_name” : {
        "lanaguage_name": {
            "DETECT": [bears, that, can, detect, the, capability, for, the, language]
            "FIX": [bears, that, can, fix, the, capability, for, the, language]
        },
    },
}

The users will be prompted to pick the desired capabilities in the beginning via the CLI, relevant bears are selected (with non-overlapping capabilities) and become part of the final generated .coafile. In future, these labels can be replaced or complemented by DETECT_ASPECTS and FIX_ASPECTS to enable users to provide their choices for "aspects" while selecting the bears.

  1. Unified represenation of information extracted from InfoExtractors for project files like .editorconfig, Gruntfile.js, package.json, .csslintrc, etc (see this issue and cEP-0009 for more details)
{
    "info_name": [InfoInstance1, InfoInstance2,]
}

The Info instances encapsulate the extracted information from project files. The various kinds of Info classes will be utilized on case-by-case basis. Example, IgnorePathsInfo may be used to exclude files from analysis right from the beginning, ProjectDependencyInfo might be used to match against the REQUIREMENTS_META discussed in point 1, StyleInfo may provide values to fill inside the generated .coafile sections.

  1. Mechanism to extract all the non-optional settings of a Bear at the runtime.

Filtering and selecting bears from collected metadata

This section discusses the step-by-step procedure of selecting relevant bears and then creating .coafile sections from them.

  1. Identify all the languages used in the project.

  2. Ask users to select capabilities based on possible CAN_DETECT and CAN_FIX label values. Store them in a list desired_capabilities like:

desired_capabilities = ["capability1", "capabilty2"]

The "FIX" capabilites are stricter than "DETECT" capabilities. If a Bear has a capability to fix some aspect, it is obvious that it has capability to detect it. This fact will be taken into account while filtering bears by capabilities at a later step. The desired_capabilities list may also be pre-populated with some important capabilities (on similar lines as the existing IMPORTANT_BEARS_LIST)

  1. Generate a dictionary named candidate_bears of the form
candidate_bears = {
    "detected_language_1": {"BearA", "BearB"},
    "detected_language_2": {"BearB", "BearD"},
    "all": {"BearE", "BearF", "BearG"}
}

It is possible to have overlap among the lists in candidate_bears which will be resolved at later stage.

  1. Initialize empty dictionary named to_propose_bears.

  2. Initialize a dictionary named selected_bears to IMPORTANT_BEARS_LIST.

  3. Given the metadata mentioned in the previous section is available, filter the candidate_bears lists as:

    1. Remove those bears from candidate_bears which do not contain any of the capability in desired_capabilites set.
    2. Among the remaining bears in candidate_bears, if some ProjectDependenyInfo extracted from the project matches the REQUIREMENTS field of bear (matching might also involve checking the compatibility of semvers), move the tuple (Info, Bear) to to_propose_bears.
  4. For every bear in to_propose_bears:

    1. If there's enough extracted information from the project to fill in all the non-optional settings of the Bear, move the Bear to selected_bears list.
    2. Else, prompt the user on CLI with the evidence (the source of Info instance in the tuple) to enable the bear. If the user agrees, move the bear to selected_bears, otherwise discard the bear.
  5. For each of the remaining bears in candidate_bears, eliminate bears having capabilities already satisfied in selected_bears.

  6. Among the remaining bears in candidate_bears, in the case of conflicts in capabilities among the bears in the list:

    1. Eliminate bears having no unique capabilities among the other bears present in the list.
    2. Give preference to the bears already having dependencies installed (using the check_prerequisites field of the bears) and eliminate the respective conflicting bears.
    3. Add the remaining bears to selected_bears list.
  7. For every non-optional setting of each bear selected_bears object,

    1. If the setting's value exists in extracted information, do nothing.
    2. Else,
      1. If the quickstart is being executed in interactive mode, prompt the user for the setting value.
      2. If non-interactive mode, discard the bear.
  8. Finally, create sections from the selected_bears list using the collected information values from user inputs and project files.

In future, more constraints can be added to further narrow down the list of selected bears by giving users more flexibility in terms of options like max_bears, max_bears_per_language, exclude_languages, etc.

Mapping extracted information to setting values.

To automatically fill in some of the setting values in .coafile sections, the Info classes need to be mapped to settings values of the Bears. These scope of applicability of these Info instances may vary from "bear-specific", to "section" to "global". The scope for a mapping may be defined based on the:

  • Source of the Info instance
  • Value of the Info instance
  • Class of the Info instance
  • Any other attribute encapsulated in the Info instance

The scope of the information can be represented by the InfoScope class as follows:

class InfoScope:
    def __init__(self,
                 level,
                 sections=[],
                 bears=[],
                 allowed_sources=[],
                 allowed_extractors=[]):
        """
        A class representing scope of applicability of an
        ``Info`` instance.

        :param level:              Broad-level scope, possible values are
                                   "global", "section", and "bear".
        :param sections:           list of section names, considered only for
                                   the ``level`` "section" and "bear".
        :param bears:              list of bear names, considered only for the
                                   "bear" ``level``.
        :param allowed_sources:    list containing names of the sources of
                                   ``Info`` classes which fall within this
                                   scope, empty list will means the ``Info``
                                   instance is applicable for all the sources.
        :param allowed_extractors: list of allowed ``InfoExtractor`` derived
                                   classes for the scope.
        """
        self.level = level
        if level=="sections":
            self.sections = sections
        elif level=="bears":
            self.sections = sections
            self.bears = bears
        self.allowed_sources = allowed_sources
        self.allowed_extractors = allowed_ extractors

    def check_belongs_to_scope(self,
                               section_name,
                               bear_name):
        """
        Checks if the given section_name and bear_name
        belong to the InfoScope or not.
        """
        if self.level=="global":
            return True
        elif self.level=="sections":
            if section_name in self.sections:
                return True
        elif self.level=="sections":
            if (section_name in self.sections and
                bear_name in self.bears):
                return True
        return False

    def check_is_applicable_information(self, info):
        """
        Checks if the given ``Info`` instance contains
        information applicable to the InfoScope of not.
        """
        if (info.source in self.allowed_sources or
            is_instance(info.extractor, self.allowed_extractors)):
            return True
        return False

The InfoScope instances serve following two purposes while trying to fill an extracted information value to a setting based on mapping:

  1. Check if the mapping if applicable for a given bear and a given section.
  2. Check if the Info instance is valid based on its extraction source and the extractor class.

The mappings will be defined similar to the following way:

INFO_SETTING_MAPS = {
    "setting_key_1": [
            {
                "scope": InfoScope(level="global"),
                "info_kind": SomeInfoClass,
                "mapper_function": function_to_map_value_in_info_to_setting_value
            },
            {
                "scope": InfoScope(level="bear",
                                   bears=["SomeBear"]),
                "info_kind": AnotherInfoClass,
                "mapper_function": function_to_map_value_in_info_to_setting_value
            },
        ],

    "setting_key_2": [
            {
                "scope": InfoScope(level="sections",
                                   sections=["python"]),
                "info_kind": InfoClassA,
                "mapper_function": function_to_map_value_in_info_to_setting_value
            },
        ],

    "ignore": [
            {
                "scope": InfoScope(level="global"),
                "info_kind": IgnorePathsInfo,
                "mapper_function": lambda x: x if is_valid_glob(x)
            }
    ]
}

The mappings will be stored in a module named InfoMappings.py in coala-quickstart inside the info_extraction package.

Filling-in the values of bear settings

An attempt to autofill the setting's value can be made before prompting the user to fill the values. This can be done as follows:

def autofill_value_if_possible(self,
                               setting_key,
                               section,
                               bear,
                               extracted_information):
    """
    For the given setting configurations, checks if there is a possiblity of filling it's value from the extracted information, and returns the values if they are applicable.
    """
    if INFO_SETTING_MAPS.get(setting_name):
        for mapping in INFO_SETTING_MAPS[setting_key]:
            scope = mapping["scope"]
            if (scope.check_belongs_to_scope(
                    section, bear)):
                # look for the values in extracted information
                # from all the ``InfoExtractor`` instances.
                values = extracted_information.get(mapping["info_kind"])
                for val in values:
                    if scope.check_is_applicable_information(val):
                        yield val
    return None

Detecting inconsistencies in project configurations

Another interesting application of the unified representation of extracted-information is detecting the inconsistencies among different project configurations. The intra-scope conflicts due to different Info from different sources can be brought up to users consideration. The inter-scope conflicts can be fixed either based on level-priority (global < section < bear) or by prompting users to select their preferred value.

The inconsistencies can be detected as

to_fill_values = list(autofill_value_if_possible(some_setting,
                                                 some_section,
                                                 some_bear))
if len(to_fill_values) > 1:
    # warn the user about inconsistency.

The inconsistencies need to be resolved before using the Info values to fill in the settings of .coafile sections. As a simple solution, user can be prompted like

> We found conflicting options in your file_a and your file_b, which should assume dominance?
1. file_a
2. file_b
3. override both and specify your value

Overall effect on coala-quickstart's CLI and outputs

After the bears are filtered, and relevant information has been extracted out of the projects, the users are notified, a more tailored version of coafile is written. The per-language sections are generated like before, but with some additional bears included based on relevant information discovered from project and user's preference (CLI input). The users are also prompted for the values like before, but an attempt is made to autofill these values before prompting the users. If there are multiple possible values to autofill, such inconsistencies are brought to user's consideration allowing him to pick any one or them or override all of them.

Running mode options for quickstart

  • Robust mode (activate all the bears)
  • Non-interactive mode (choose the best .coafile without user interference)
    • With default capabilities. (default option for non-interactive modes)
    • Without any predefined capabilities.
  • Interactive mode (prompt for setting values)
    • With user provided capabilities. (default option for interactive mode)
    • With default capabilities.
    • Without capabilities.

There will be a filter_by_capabilties option which will be prompted to users in normal mode. In non-interactive mode, a command line flag --no-default-capabilities can be passed to disable filtering by default_capabilities. In case the filter_by_capabilties option is disabled, steps 8,9 and 10 in "Filtering and selecting bears from collected meta-data" section will be skipped.

A possible set of deault capabilties can be:

  • Syntax
  • Formatting
  • Documentation
  • Spelling
  • Code Simplification
  • Smell
  • Redundancy
An example flow for the interactive mode
  1. The user runs coala-quickstart and specifies project directories.
  2. Languages are identified in the project.
  3. All the InfoExtractor classes are instantiated, and the extracted information is aggregated and stored.
  4. The user is asked if he wants to select bears based on capabilities.
  5. The filtering and selection of bears take place as described in sections above.
  6. After the bears are selected for every language present in the project, coala-quickstart tries automatically to fill the setting values for the bears, failing which, the user is prompted for the value. In case some inconsistency is encountered here, it is brought to user's consideration.
  7. The per-language sections are generated, and final .coafile is written.
An example flow for the non-interactive mode
  1. The user runs coala-quickstart and specifies project directories.
  2. Languages are identified in the project.
  3. All the InfoExtractor classes are instantiated, and the extracted information is aggregated and stored.
  4. The filtering and selection of bears take place as described in sections above.
  5. After the bears are selected for every language present in the project, coala-quickstart tries automatically to fill the setting values for the bears, failing which, the bear is discarded. In case some inconsistency is encountered here, an attempt is made to resolve it based on scope, failing which, the bear may be discarded, or any one of the possible value is filled in with a warning comment.
  6. The per-language sections are generated, and final .coafile is written.

Code Samples

Collecting extracted information from project

from coala_quickstart.info_extractors.GruntfileInfoExtractor import (
    GruntfileInfoExtractor)
from coala_quickstart.info_extractors.EditorconfigInfoExtractor import (
    EditorconfigInfoExtractor)
from coala_quickstart.info_extractors.PackageJSONInfoExtractor import (
    PackageJSONInfoExtractor)
from coala_quickstart.info_extractors.PackageJSONInfoExtractor import (
    GemfileInfoExtractor)


def collect_info(project_dir):
    gruntfile_info = GruntfileInfoExtractor(
        ["Gruntfile.js"], project_dir).extract_information()

    editorconfig_info = EditorconfigInfoExtractor(
        [".editorconfig"], project_dir).extract_information()

    package_json_info = PackageJSONInfoExtractor(
        ["package.json"], project_dir).extract_information()

    gemfile_info = GemfileInfoExtractor(
        ["Gemfile"], project_dir).extract_information()

    extracted_info = aggregate_info(
        gruntfile_info, editorconfig_info, package_json_info, gemfile_info)

    return extracted_info


def aggregate_info(infoextractors):
    """
    Aggregates inforamtion extracted from multiple ``InfoExtractor``
    instances to one dictionary.
    """
    result = {}
    for ie in infoextractors:
        # the fname key level can be removed from the current implementation
        # of the way information is stored as file name is already stored
        # in source attribute of ``Info`` classes.
        for fname, extracted_info in ie.items():
            for info_name, info_instances in extracted_info.items():
                if result.get(info_name):
                    result[info_name] += info_instances
                else:
                    result[info_name] = info_instances
    return result

Collecting all the meta-data

from collections import defaultdict

from pyprint.NullPrinter import NullPrinter

from coalib.settings.ConfigurationGathering import get_filtered_bears
from coalib.output.printers.LogPrinter import LogPrinter
from coalib.misc.DictUtilities import inverse_dicts


used_languages = ["languages", "used", "in", "project"]


def get_all_bears(languages):
    """
    Collects all the bears corresponding to the provided languages and
    returns them as a list.
    :param languages: list of languages.
    :return:          list of all the bears that can be suitable for
                      the given list of languages.
    """
    local_bears, global_bears = get_filtered_bears(
        languages, LogPrinter(NullPrinter()), None)
    all_bears = inverse_dicts(local_bears, global_bears).keys()
    return list(all_bears)


def get_all_bears_by_lang(languages):
    """
    Return a dict with language names as keys and the list of suitable
    bears as values.
    """
    all_bears_by_lang = {
            lang: set(inverse_dicts(*get_filtered_bears(
                [lang], LogPrinter(NullPrinter()), None)).keys())
            for lang in languages
    }
    return all_bears_by_lang


def generate_capabilties_map(bears_by_lang):
    """
    Generates a dictionary of capabilities, languages and the corresponding bears from the given ``bears_by_lang`` dict.

    :param bears_by_lang: dict with language names as keys
                          and the list of bears as values.
    :returns:             dict of the form
                          {
                            "language": {
                                "detect": [list, of, bears]
                                "fix": [list, of, bears]
                            }
                          }
    """

    def nested_dict():
        return defaultdict(dict)
    capabilities_meta = defaultdict(nested_dict)

    # collectiong the capabilities meta-data
    for lang, bears in bears_by_lang.items():
        can_detect_meta = inverse_dicts(
            *[{bear: list(bear.CAN_DETECT)} for bear in bears])
        can_fix_meta = inverse_dicts(
            *[{bear: list(bear.CAN_FIX)} for bear in bears])

        for capability, bears in can_detect_meta.items():
            capabilities_meta[capability][lang]["DETECT"] = bears

        for capability, bears in can_fix_meta.items():
            capabilities_meta[capability][lang]["FIX"] = bears
    return capabilities_map


def generate_requirements_map(bears):
    """
    For the given list of bears, returns a dict of the form
    ```
    {
        “requirement_name” : {
            “requirement_type” : NpmRequirement,
            "version" : ">=0.4.2"
            “bears” : [ list, of, bears, wrapping, the, executable],
        }
    }
    ```
    """
    requirements_meta = {}
    for bear in bears:
        for req in bear.REQUIREMENTS:
            to_add = {
                "bear": bear,
                "version": req.version,
                "type": req.type
            }
            if requirements_meta.get(req.package):
                requirements_meta[req.package].append(to_add)
            else:
                requirements_meta[req.package] = [to_add]
    return requirements_meta

Filtering and selecting bears

import copy


from coalib.settings.ConfigurationGathering import get_filtered_bears
from coala_quickstart.Constants import IMPORTANT_BEAR_LIST
from coala_quickstart.InfoExtraction.InfoMappings import INFO_MAPPINGS


def filter_relevant_bears(used_languages,
                          desired_capabilities,
                          extracted_info,
                          arg_parser=None):
    """
    Filter bears based on used languages in the project and
    the desired bear capabilities.
    """
    all_bears_by_lang = {
        lang: set(inverse_dicts(*get_filtered_bears([lang],
                                                    log_printer,
                                                    arg_parser)).keys())
        for lang in used_languages
    }

    selected_bears = {}
    candidate_bears = copy.copy(all_bears_by_lang)
    to_propose_bears = {}

    # Initialize selected_bears with IMPORTANT_BEAR_LIST
    for lang in candidate_bears:
        if lang in IMPORTANT_BEAR_LIST:
            selected_bears[lang] = [
                bear for bear in IMPORTANT_BEAR_LIST[lang]
                if bear.name in candidate_bears[lang]]
        candidate_bears[lang] = [bear for bear in candidate_bears[lang]
                                 if bear not in selected_bears[lang]]

    # Filter bears by desired_capabilities
    capable_candidates = {}
    for lang, lang_bears in candidate_bears.items():
        # Eliminate bears which doesn't contain the desired capabilites
        capable_bears = get_bears_with_given_capabilities(
            lang_bears, desired_capabilties)
        capable_candidates[lang] = capable_bears

    # Use project_dependency_info to shortlist bears to be proposed.
    project_dependency_info = extracted_info["ProjectDependencyInfo"]
    for lang, lang_bears in capable_candidates.items():
        matching_dep_bears = get_bears_with_matching_dependencies(
            lang_bears, project_dependency_info)
        # Remove these bears from capable_candidates list
        # and move to to_propose_bears list
        to_propose_bears[lang] = set(matching_dep_bears)
        bears_to_remove = [match[0] for match in matching_dep_bears]
        capable_candidates[lang] = [bear for bear in capable_candidates[lang]
                                    if bear not in bears_to_remove]

    # Proposing users to select a bear based on some extracted information
    for lang, lang_bears in to_propose_bears.items():
        for bear, associated_info in lang_bears:
            # get the non-optional settings of the bears
            settings = get_non_optional_settings(bear)
            if not settings:
                # no non-optional setting, select it right away!
                selected_bears[lang].add(bear)
            else:
                user_input_reqd = False
                for setting in settings:
                   if not is_autofill_possible(
                            extracted_info, setting.key, lang, bear):
                        user_input_reqd = True

                if user_input_reqd:
                    # Ask user to activate the bear
                    if (not arguments.non_interactive and
                        prompt_to_activate(bear, associated_info)):
                        # select the bear!
                        selected_bears[lang].add(bear)
                else:
                    # All the non-optional settings can be filled automatically
                    selected_bears[lang].add(bear)

    # capabilities satisfied till now
    satisfied_capabilities = get_bear_capabilties(selected_bears)
    remaining_capabilities = [cap for cap in desired_capabilities
                              if cap not in satisfied_capabilities]
    capable_bears = get_bears_with_given_capabilities(
        capable_bears, remaining_capabilties)

    # optional, remove conflicting capabilties and
    # find a satisfying combination containing minimum
    # number of bears.
    capable_bears = remove_bears_with_conflicting_capabilties(
        capable_bears)

    # Add the remaining bears to selected_bears
    for lang, lang_bears in capable_bears.items():
        selected_bears[lang] += lang_bears

    # Put language independent bears into "All" category
    selected_bears = {lang: selected_bears[lang] - all_bears_by_lang[lang],
                      for lang, _ in selected_bears.items()}

    return selected_bears

def get_bears_with_given_capabilities(bears, capabilties):
    """
    Returns a list of bears which contain at least one on the
    capability in ``capabilities`` list.
    """
    result = []
    for bear in bears:
        can_detect_caps = [c for c in list(bear.CAN_DETECT)]
        can_fix_caps = [c for c in list(bear.CAN_FIX)]
        for cap, cap_type in capabilities:
            if cap in can_fix_caps:
                result.append(bear)
            elif cap in can_detect_caps and cap_type=="DETECT":
                result.append(bear)
    return result

def remove_bears_with_conflicting_capabilties(bears_by_lang):
    """
    Eliminate bears having no unique capabilities among the other bears present in the list.
    Gives preference to:
    - The bears already having dependencies installed.
    - Bears that can fix the capability rather that just detecting it.
    """
    result = {}
    for lang, bears in bears_by_lang.items():
        lang_result = set()
        capabilties_map = generate_capabilties_map({lang: bears})
        for cap in capabilties.map.keys():
            # bears that can fix the ``cap`` capabilitiy
            fix_bears = capabilties_map[cap][lang]["fix"]
            if fix_bears:
                for bear in fix_bears:
                    if not bear.check_prerequisites():
                        # The dependecies for bear are already installed,
                        # so select it.
                        lang_result.add(bear)
                        break
                # None of the bear has it's dependency installed, select
                # a random bear.
                lang_result.add(random.choice(fix_bears))
                break
            # There were no bears to fix the capability
            detect_bears = capabilties_map[cap][lang]["detect"]
            if detect_bears:
                for bear in detect_bears:
                    if not bear.check_prerequisites():
                        lang_result.add(bear)
                        break
                lang_result.add(random.choice(detect_bears))
                break
        result[lang] = lang_result
    return result

def is_autofill_possible(self,
                         extracted_info,
                         setting_name,
                         language,
                         bear):
    """
    Checks if it is possible to autofill the setting values.
    """
    if INFO_SETTING_MAPS.get(setting_name):
        for mapping in INFO_SETTING_MAPS[setting_key]:
            scope = mapping["scope"]
            if (scope.check_belongs_to_scope(
                    section=language, bear=bear)):
                values = extracted_information.get(mapping["info_kind"])
                for val in values:
                    if scope.check_is_applicable_information(val):
                        return True
    return False

def get_non_optional_settings(bear):
    """
    Return a list of non-optional settings for a given bear.
    """
    # Already implemented in quickstart
    pass

def get_bears_with_matching_dependencies(
        bears, dependency_info):
    """
    Matches the `REQUIREMENTS` filed of bears against a list
    of ``ProjectDependencyInfo`` instances.

    Return a list of the tuples of the form
    (bear, ProjectDependencyInfoInstance)
    """
    result = []
    requirements_map = generate_requirements_map(bears)
    for req, req_info in requirements_map.items():
        for dep in dependency_info:
            # Check if names of requirements match
            if dep.value == req:
                if (are_compatible_versions(dep.version, req_info["version"])
                        and are_compatible_req_types(dep.type, req_info["type"])):
                    # Everyhing is fine, it's a match!
                    result.append((req_info["bear"], dep))
    return result

def are_compatible_versions(semver1, semver2):
    """
    :returns:
        True if semver1 is latest or matches semvers 2,
        False otherwise
    """
    pass

def are_compatible_req_types(req_type_1, req_type_2):
    """
    Checks if req_type_1 is a type of req_type_2.
    """
    return isinstance(req_type_1, req_type_2)

Generating .coafile sections

selected_bears = filter_relevant_bears(
    used_languages, desired_capabilities, extracted_info, arg_parser=None)

for lang, lang_bears in selected_bears:
    generate_section(lang, lang_bears, extracted_info)

def generate_section(section_name,
                     section_bears,
                     arguments,
                     extracted_info):
    settings = get_non_optional_settings(section_bears)
    for setting_name, associated_bears in settings:
        to_fill_values = autofill_value_if_possible(
            extracted_info,
            section_name,
            associated_bears)
        setting_value = None
        if len(to_fill_values) > 1:
            # warn the user about inconsistency.
            # ask user for correct value
        elif len(to_fill_values)==1:
            setting_value = to_fill_values[0]
        else:
            if arguments.non_interactive:
                continue
            setting_value = ask_for_setting_value(
                setting, associated_bears)
        # store setting values
    # create sections finally