def apply_each()

in runtool/runtool/transformations.py [0:0]


def apply_each(node: dict) -> Versions:
    """
    If `$each` is in the node, it means that the node can become
    several different values.

    NOTE::
        nodes which can take multiple values are represented as
        `runtool.datatypes.Versions` objects. Refer to their documentation
        for further information.

    Example, a node which can take the values `1` or `2` or `3` can be generated
    as follows:

    >>> apply_each({"$each":[1,2,3]})
    Versions([1, 2, 3])

    If dictionaries are passed to $each, these will be updated with the
    value of the passed `node` before a Versions object is generated.

    >>> apply_each({"a": 1, "$each": [{"b": 2}, {"b": 3}]})
    Versions([{'b': 2, 'a': 1}, {'b': 3, 'a': 1}])

    It is possible to have an unaltered version of the parent node by
    inserting $None into the values of $each. In the example below,
    apply_each generates two versions of the node, one which is unaltered
    and one which is merged with another dict.

    >>> apply_each({"a": 1, "$each": ["$None", {"b": 2}]})
    Versions([{'a': 1}, {'b': 2, 'a': 1}])

    Below is a more complicated example combining the two examples above:

    >>> apply_each(
    ...     {
    ...         "a": 1,
    ...         "$each": ["$None", {"b": 2, "c": 3}]
    ...     }
    ... )
    Versions([{'a': 1}, {'b': 2, 'c': 3, 'a': 1}])

    Parameters
    ----------
    node
        The node which should have `$each` applied to it.

    Returns
    -------
    runtool.datatypes.Versions
        The versions object representing the different values of the node.
    """
    if not (isinstance(node, dict) and "$each" in node):
        return node

    each = node.pop("$each")
    if not isinstance(each, list):
        raise TypeError(
            f"$each requires a list, not an object of type {type(each)}"
        )

    # Generate versions of the current node
    versions = []
    for item in each:
        if item == "$None":
            # return unaltered node
            # node = {"a": 1, "$each": ["$None"]}
            # ==>
            # {"a": 1}
            versions.append(node)
        elif isinstance(item, dict):
            # merge node with value in $each
            # node = {"a": 1, "$each": [{"b: 2"}]}
            # ==>
            # {"a": 1, "b": 2}
            item.update(node)
            versions.append(item)
        else:
            # any other value overwrites the node if node is
            # otherwise empty.
            # node = {"$each": [2]}
            # ==>
            # 2
            if node:
                node["$each"] = each
                raise TypeError(
                    "Using $each in a non-empty node is only supported"
                    " when using dictionaries or with the $None operator."
                    f" The error occured in:\n{node}"
                )
            versions.append(item)
    return Versions(versions)