def test_recursive_apply_merging_versions_dict(): compare_recursive_apply( node={ "a": Versions([1, 2]), "b": Versions([3, 4]) }, expected=Versions([ { "a": 1, "b": 3 }, { "a": 1, "b": 4 }, { "a": 2, "b": 3 }, { "a": 2, "b": 4 }, ]), fn=lambda x: x, )
def test_recursive_apply_merging_versions_list_in_list(): compare_recursive_apply( node=[ [Versions([1, 2]), Versions([3, 4])], [Versions([5, 6]), Versions([7, 8])], ], expected=Versions([ [[1, 3], [5, 7]], [[1, 3], [5, 8]], [[1, 3], [6, 7]], [[1, 3], [6, 8]], [[1, 4], [5, 7]], [[1, 4], [5, 8]], [[1, 4], [6, 7]], [[1, 4], [6, 8]], [[2, 3], [5, 7]], [[2, 3], [5, 8]], [[2, 3], [6, 7]], [[2, 3], [6, 8]], [[2, 4], [5, 7]], [[2, 4], [5, 8]], [[2, 4], [6, 7]], [[2, 4], [6, 8]], ]), fn=lambda x: x, )
def test_recursive_apply_versions(): compare_recursive_apply( node={"my_list": [ { "hello": "there" }, { "a": { "version": [1, 2] } }, ]}, expected=Versions([ { "my_list": [{ "hello": "there" }, { "a": 1 }] }, { "my_list": [{ "hello": "there" }, { "a": 2 }] }, ]), )
def test_recursive_apply_merging_versions_with_function(): compare_recursive_apply( node={ "my_list": [ { "hello": "there" }, { "a": { "version": [1, 2] } }, { "b": { "version": [3, 4] } }, ] }, expected=Versions([ { "my_list": [{ "hello": "there" }, { "a": 1 }, { "b": 3 }] }, { "my_list": [{ "hello": "there" }, { "a": 1 }, { "b": 4 }] }, { "my_list": [{ "hello": "there" }, { "a": 2 }, { "b": 3 }] }, { "my_list": [{ "hello": "there" }, { "a": 2 }, { "b": 4 }] }, ]), )
def transform(node: dict): """ Converts node to a version object if the node has a key "versions" else it multiplies the node by 2 if the node has key "double" """ if "version" in node: return Versions(node["version"]) elif "double" in node: return 2 * node["double"] return node
def test_recursive_apply_merging_versions_dict_in_dict(): compare_recursive_apply( node={ "a": { "b": Versions([1, 2]) }, "c": Versions([2, 3]) }, expected=Versions([ { "a": { "b": 1 }, "c": 2 }, { "a": { "b": 1 }, "c": 3 }, { "a": { "b": 2 }, "c": 2 }, { "a": { "b": 2 }, "c": 3 }, ]), fn=lambda x: x, )
def test_apply_each_with_none(): compare_apply_each( node={ "c": "dummy", "$each": [ "$None", { "a": 150, "b": 64 }, ], }, expected=Versions([ { "c": "dummy" }, { "a": 150, "b": 64, "c": "dummy" }, ]), )
def test_all_in_one(): compare( { "dataset": DATASET, "datasets": [DATASET], "algorithm": ALGORITHM, "algorithms": [ALGORITHM], "experiment": {"algorithm": ALGORITHM, "dataset": DATASET}, "experiments": [{"algorithm": ALGORITHM, "dataset": DATASET}], }, { "dataset": Versions([Dataset(DATASET)]), "datasets": Versions([Datasets([Dataset(DATASET)])]), "algorithm": Versions([Algorithm(ALGORITHM)]), "algorithms": Versions([Algorithms([Algorithm(ALGORITHM)])]), "experiment": Versions([EXPERIMENT]), "experiments": Versions([Experiments([EXPERIMENT])]), }, )
def test_recursive_apply_merging_versions_list(): compare_recursive_apply( node=[Versions([1, 2]), Versions([3, 4])], expected=Versions([[1, 3], [1, 4], [2, 3], [2, 4]]), fn=lambda x: x, )
def test_recursive_apply_merging_versions_simple(): compare_recursive_apply( node=[Versions([1, 2])], expected=Versions([[1], [2]]), fn=lambda x: x, )
def test_recursive_apply_merging_versions_list_in_dict(): compare_recursive_apply( node={ "a": [Versions([1, 2]), Versions([3, 4])], "b": [Versions([5, 6]), Versions([7, 8])], }, expected=Versions([ { "a": [1, 3], "b": [5, 7] }, { "a": [1, 3], "b": [5, 8] }, { "a": [1, 3], "b": [6, 7] }, { "a": [1, 3], "b": [6, 8] }, { "a": [1, 4], "b": [5, 7] }, { "a": [1, 4], "b": [5, 8] }, { "a": [1, 4], "b": [6, 7] }, { "a": [1, 4], "b": [6, 8] }, { "a": [2, 3], "b": [5, 7] }, { "a": [2, 3], "b": [5, 8] }, { "a": [2, 3], "b": [6, 7] }, { "a": [2, 3], "b": [6, 8] }, { "a": [2, 4], "b": [5, 7] }, { "a": [2, 4], "b": [5, 8] }, { "a": [2, 4], "b": [6, 7] }, { "a": [2, 4], "b": [6, 8] }, ]), fn=lambda x: x, )
def test_datasets_identification(): compare( {"ds": [DATASET]}, {"ds": Versions([Datasets([Dataset(DATASET)])])}, )
def test_algorithms_identification(): compare( {"algo": [ALGORITHM]}, {"algo": Versions([Algorithms([Algorithm(ALGORITHM)])])}, )
def test_algorithm_identification(): compare( {"algorithm": ALGORITHM}, {"algorithm": Versions([Algorithm(ALGORITHM)])}, )
def test_experiments_identification(): compare( {"my_experiments": [EXPERIMENT]}, {"my_experiments": Versions([Experiments([EXPERIMENT])])}, )
def test_experiment_identification(): compare( {"my_experiment": {"algorithm": ALGORITHM, "dataset": DATASET}}, {"my_experiment": Versions([EXPERIMENT])}, )
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)
def test_apply_each_simple(): compare_apply_each( node={"$each": [1, 2, 3]}, expected=Versions([1, 2, 3]), )