diff --git a/tests/test_merger_mergerconfig.py b/tests/test_merger_mergerconfig.py index 96aaf93..a40196f 100644 --- a/tests/test_merger_mergerconfig.py +++ b/tests/test_merger_mergerconfig.py @@ -20,6 +20,7 @@ create_temp_yaml_file ) + class Test_merger_MergerConfig(): """Tests for the MergerConfig class.""" @@ -207,6 +208,83 @@ def test_hash_merge_mode_ini_rule_overrides_cli( assert mc.hash_merge_mode( NodeCoords(node, parent, parentref)) == mode + @pytest.mark.parametrize("ini_rule, override_rule, mode", [ + ("left", "right", HashMergeOpts.RIGHT), + ("right", "deep", HashMergeOpts.DEEP), + ("deep", "left", HashMergeOpts.LEFT), + ]) + def test_hash_merge_mode_override_rule_overrides_ini_rule( + self, quiet_logger, tmp_path_factory, ini_rule, override_rule, mode + ): + config_file = create_temp_yaml_file(tmp_path_factory, """ + [rules] + /hash = {} + """.format(ini_rule)) + lhs_yaml_file = create_temp_yaml_file(tmp_path_factory, """--- + hash: + lhs_exclusive: lhs value 1 + merge_targets: + subkey: lhs value 2 + subarray: + - one + - two + array_of_hashes: + - name: LHS Record 1 + id: 1 + prop: LHS value AoH 1 + - name: LHS Record 2 + id: 2 + prop: LHS value AoH 2 + """) + lhs_yaml = get_yaml_editor() + (lhs_data, lhs_loaded) = get_yaml_data(lhs_yaml, quiet_logger, lhs_yaml_file) + + mc = MergerConfig(quiet_logger, SimpleNamespace(config=config_file), rules={"/hash": override_rule}) + mc.prepare(lhs_data) + + node = lhs_data["hash"] + parent = lhs_data + parentref = "hash" + + assert mc.hash_merge_mode( + NodeCoords(node, parent, parentref)) == mode + + @pytest.mark.parametrize("arg_rule, override_rule, mode", [ + ("left", "right", HashMergeOpts.RIGHT), + ("right", "deep", HashMergeOpts.DEEP), + ("deep", "left", HashMergeOpts.LEFT), + ]) + def test_hash_merge_mode_override_rule_overrides_arg_rule( + self, quiet_logger, tmp_path_factory, arg_rule, override_rule, mode + ): + lhs_yaml_file = create_temp_yaml_file(tmp_path_factory, """--- + hash: + lhs_exclusive: lhs value 1 + merge_targets: + subkey: lhs value 2 + subarray: + - one + - two + array_of_hashes: + - name: LHS Record 1 + id: 1 + prop: LHS value AoH 1 + - name: LHS Record 2 + id: 2 + prop: LHS value AoH 2 + """) + lhs_yaml = get_yaml_editor() + (lhs_data, lhs_loaded) = get_yaml_data(lhs_yaml, quiet_logger, lhs_yaml_file) + + mc = MergerConfig(quiet_logger, SimpleNamespace(hashes=arg_rule), rules={"/hash": override_rule}) + mc.prepare(lhs_data) + + node = lhs_data["hash"] + parent = lhs_data + parentref = "hash" + + assert mc.hash_merge_mode( + NodeCoords(node, parent, parentref)) == mode ### # array_merge_mode @@ -311,6 +389,93 @@ def test_array_merge_mode_ini_rule_overrides_cli( assert mc.array_merge_mode( NodeCoords(node, parent, parentref)) == mode + @pytest.mark.parametrize("ini_rule, override_rule, mode", [ + ("left", "right", ArrayMergeOpts.RIGHT), + ("right", "unique", ArrayMergeOpts.UNIQUE), + ("unique", "all", ArrayMergeOpts.ALL), + ("all", "left", ArrayMergeOpts.LEFT), + ]) + def test_array_merge_mode_override_rule_overrides_ini_rule( + self, quiet_logger, tmp_path_factory, ini_rule, override_rule, mode + ): + config_file = create_temp_yaml_file(tmp_path_factory, """ + [rules] + /hash/merge_targets/subarray = {} + """.format(ini_rule)) + lhs_yaml_file = create_temp_yaml_file(tmp_path_factory, """--- + hash: + lhs_exclusive: lhs value 1 + merge_targets: + subkey: lhs value 2 + subarray: + - one + - two + array_of_hashes: + - name: LHS Record 1 + id: 1 + prop: LHS value AoH 1 + - name: LHS Record 2 + id: 2 + prop: LHS value AoH 2 + """) + lhs_yaml = get_yaml_editor() + (lhs_data, lhs_loaded) = get_yaml_data(lhs_yaml, quiet_logger, lhs_yaml_file) + + mc = MergerConfig( + quiet_logger, + SimpleNamespace(config=config_file), + rules={"/hash/merge_targets/subarray": override_rule} + ) + mc.prepare(lhs_data) + + node = lhs_data["hash"]["merge_targets"]["subarray"] + parent = lhs_data["hash"]["merge_targets"] + parentref = "subarray" + + assert mc.array_merge_mode( + NodeCoords(node, parent, parentref)) == mode + + @pytest.mark.parametrize("arg_rule, override_rule, mode", [ + ("left", "right", ArrayMergeOpts.RIGHT), + ("right", "unique", ArrayMergeOpts.UNIQUE), + ("unique", "all", ArrayMergeOpts.ALL), + ("all", "left", ArrayMergeOpts.LEFT), + ]) + def test_array_merge_mode_override_rule_overrides_arg_rule( + self, quiet_logger, tmp_path_factory, arg_rule, override_rule, mode + ): + lhs_yaml_file = create_temp_yaml_file(tmp_path_factory, """--- + hash: + lhs_exclusive: lhs value 1 + merge_targets: + subkey: lhs value 2 + subarray: + - one + - two + array_of_hashes: + - name: LHS Record 1 + id: 1 + prop: LHS value AoH 1 + - name: LHS Record 2 + id: 2 + prop: LHS value AoH 2 + """) + lhs_yaml = get_yaml_editor() + (lhs_data, lhs_loaded) = get_yaml_data(lhs_yaml, quiet_logger, lhs_yaml_file) + + mc = MergerConfig( + quiet_logger, + SimpleNamespace(arrays=arg_rule), + rules={"/hash/merge_targets/subarray": override_rule} + ) + mc.prepare(lhs_data) + + node = lhs_data["hash"]["merge_targets"]["subarray"] + parent = lhs_data["hash"]["merge_targets"] + parentref = "subarray" + + assert mc.array_merge_mode( + NodeCoords(node, parent, parentref)) == mode ### # aoh_merge_mode @@ -419,6 +584,95 @@ def test_aoh_merge_mode_ini_rule_overrides_cli( assert mc.aoh_merge_mode( NodeCoords(node, parent, parentref)) == mode + @pytest.mark.parametrize("ini_rule, override_rule, mode", [ + ("deep", "left", AoHMergeOpts.LEFT), + ("left", "right", AoHMergeOpts.RIGHT), + ("right", "unique", AoHMergeOpts.UNIQUE), + ("unique", "all", AoHMergeOpts.ALL), + ("all", "deep", AoHMergeOpts.DEEP), + ]) + def test_array_merge_mode_override_rule_overrides_ini_rule( + self, quiet_logger, tmp_path_factory, ini_rule, override_rule, mode + ): + config_file = create_temp_yaml_file(tmp_path_factory, """ + [rules] + /array_of_hashes = {} + """.format(ini_rule)) + lhs_yaml_file = create_temp_yaml_file(tmp_path_factory, """--- + hash: + lhs_exclusive: lhs value 1 + merge_targets: + subkey: lhs value 2 + subarray: + - one + - two + array_of_hashes: + - name: LHS Record 1 + id: 1 + prop: LHS value AoH 1 + - name: LHS Record 2 + id: 2 + prop: LHS value AoH 2 + """) + lhs_yaml = get_yaml_editor() + (lhs_data, lhs_loaded) = get_yaml_data(lhs_yaml, quiet_logger, lhs_yaml_file) + + mc = MergerConfig( + quiet_logger, + SimpleNamespace(config=config_file), + rules={"/array_of_hashes": override_rule} + ) + mc.prepare(lhs_data) + + node = lhs_data["array_of_hashes"] + parent = lhs_data + parentref = "array_of_hashes" + + assert mc.aoh_merge_mode( + NodeCoords(node, parent, parentref)) == mode + + @pytest.mark.parametrize("arg_rule, override_rule, mode", [ + ("deep", "left", AoHMergeOpts.LEFT), + ("left", "right", AoHMergeOpts.RIGHT), + ("right", "unique", AoHMergeOpts.UNIQUE), + ("unique", "all", AoHMergeOpts.ALL), + ("all", "deep", AoHMergeOpts.DEEP), + ]) + def test_array_merge_mode_override_rule_overrides_arg_rule( + self, quiet_logger, tmp_path_factory, arg_rule, override_rule, mode + ): + lhs_yaml_file = create_temp_yaml_file(tmp_path_factory, """--- + hash: + lhs_exclusive: lhs value 1 + merge_targets: + subkey: lhs value 2 + subarray: + - one + - two + array_of_hashes: + - name: LHS Record 1 + id: 1 + prop: LHS value AoH 1 + - name: LHS Record 2 + id: 2 + prop: LHS value AoH 2 + """) + lhs_yaml = get_yaml_editor() + (lhs_data, lhs_loaded) = get_yaml_data(lhs_yaml, quiet_logger, lhs_yaml_file) + + mc = MergerConfig( + quiet_logger, + SimpleNamespace(aoh=arg_rule), + rules={"/array_of_hashes": override_rule} + ) + mc.prepare(lhs_data) + + node = lhs_data["array_of_hashes"] + parent = lhs_data + parentref = "array_of_hashes" + + assert mc.aoh_merge_mode( + NodeCoords(node, parent, parentref)) == mode ### # aoh_merge_key @@ -526,6 +780,40 @@ def test_aoh_merge_key_ini_inferred_parent( assert mc.aoh_merge_key( NodeCoords(node, parent, parentref), record) == "prop" + def test_aoh_merge_key_override_rule_overrides_ini(self, quiet_logger, tmp_path_factory): + config_file = create_temp_yaml_file(tmp_path_factory, """ + [keys] + /array_of_hashes = name + """) + lhs_yaml_file = create_temp_yaml_file(tmp_path_factory, """--- + hash: + lhs_exclusive: lhs value 1 + merge_targets: + subkey: lhs value 2 + subarray: + - one + - two + array_of_hashes: + - name: LHS Record 1 + id: 1 + prop: LHS value AoH 1 + - name: LHS Record 2 + id: 2 + prop: LHS value AoH 2 + """) + lhs_yaml = get_yaml_editor() + (lhs_data, lhs_loaded) = get_yaml_data(lhs_yaml, quiet_logger, lhs_yaml_file) + + mc = MergerConfig(quiet_logger, SimpleNamespace(config=config_file), keys={"/array_of_hashes": "id"}) + mc.prepare(lhs_data) + + node = lhs_data["array_of_hashes"] + parent = lhs_data + parentref = "array_of_hashes" + record = node[0] + + assert mc.aoh_merge_key( + NodeCoords(node, parent, parentref), record) == "id" ### # set_merge_mode diff --git a/yamlpath/merger/mergerconfig.py b/yamlpath/merger/mergerconfig.py index cafc0c3..394abac 100644 --- a/yamlpath/merger/mergerconfig.py +++ b/yamlpath/merger/mergerconfig.py @@ -4,7 +4,7 @@ Copyright 2020, 2021 William W. Kimball, Jr. MBA MSIS """ import configparser -from typing import Any, Dict, Union +from typing import Any, Dict, Optional from argparse import Namespace from yamlpath.exceptions import YAMLPathException @@ -24,23 +24,35 @@ class MergerConfig: """Config file processor for the Merger.""" - def __init__(self, logger: ConsolePrinter, args: Namespace) -> None: + def __init__( + self, + logger: ConsolePrinter, + args: Namespace, + **kwargs: Any, + ) -> None: """ Instantiate this class into an object. Parameters: 1. logger (ConsolePrinter) Instance of ConsoleWriter or subclass 2. args (dict) Default options for merge rules + 3. kwargs (dict) Overrides for config values Returns: N/A """ self.log = logger self.args = args - self.config: Union[None, configparser.ConfigParser] = None + self.config: Optional[configparser.ConfigParser] = None self.rules: Dict[NodeCoords, str] = {} self.keys: Dict[NodeCoords, str] = {} + config_overrides: Dict[str, Any] = {} + + if "keys" in kwargs: + config_overrides["keys"] = kwargs.pop("keys") + if "rules" in kwargs: + config_overrides["rules"] = kwargs.pop("rules") - self._load_config() + self._load_config(config_overrides) def anchor_merge_mode(self) -> AnchorConflictResolutions: """ @@ -322,7 +334,7 @@ def _prepare_user_rules( "... NODE:", data=node_coord, prefix="MergerConfig::_prepare_user_rules: ") - def _load_config(self) -> None: + def _load_config(self, config_overrides: Dict[str, Any]) -> None: """Load the external configuration file.""" config = configparser.ConfigParser() @@ -334,8 +346,15 @@ def _load_config(self) -> None: if config_file: config.read(config_file) - if config.sections(): - self.config = config + + if "keys" in config_overrides: + config["keys"] = config_overrides["keys"] + + if "rules" in config_overrides: + config["rules"] = config_overrides["rules"] + + if config.sections(): + self.config = config def _get_config_for(self, node_coord: NodeCoords, section: dict) -> str: """