def test_deep_merge_with_non_mappings_in_secondary_value_positions(): dict_a = {'a': 1, 'b': {'x': 5, 'y': 6}} dict_b = {'c': 3, 'b': 72} dict_c = {'d': 5, 'b': {'z': 9}} expected = {'a': 1, 'b': {'x': 5, 'y': 6, 'z': 9}, 'c': 3, 'd': 5} actual = deep_merge_dicts(dict_a, dict_b, dict_c) assert expected == actual
def test_deep_merge_with_deep_dicts(): dict_a = {'a': 1, 'b': {'x': 5, 'y': 6}} dict_b = {'c': 3, 'b': {'y': 7, 'z': 8}} dict_c = {'d': 5, 'b': {'z': 9}} expected = {'a': 1, 'b': {'x': 5, 'y': 7, 'z': 9}, 'c': 3, 'd': 5} actual = deep_merge_dicts(dict_a, dict_b, dict_c) assert expected == actual
def upgrade_legacy_config_file(project_dir): has_ini_config = check_if_ini_config_file_exists(project_dir) has_json_config = check_if_json_config_file_exists(project_dir) if not has_ini_config: raise ValueError("No `populus.ini` file found") elif has_json_config: raise ValueError( "Cannot upgrade config file if there is already an existing " "`populus.json` file.") ini_config_file_path = get_ini_config_file_path(project_dir) json_config_file_path = get_json_config_file_path(project_dir) upgraded_config = translate_legacy_ini_config_file(ini_config_file_path) default_config = load_default_config(version=V1) config = deep_merge_dicts(default_config, upgraded_config) write_config( project_dir, config, json_config_file_path, ) backup_ini_config_file_path = "{0}.bak".format(ini_config_file_path) shutil.move(ini_config_file_path, backup_ini_config_file_path) return backup_ini_config_file_path
def test_deep_merge_does_not_mutate(): dict_a = {'a': 1, 'b': 2} dict_b = {'b': 3, 'c': 4} dict_c = {'c': 5, 'd': 6} assert deep_merge_dicts(dict_a, dict_b, dict_c) == {'a': 1, 'b': 3, 'c': 5, 'd': 6} assert dict_a == {'a': 1, 'b': 2} assert dict_b == {'b': 3, 'c': 4} assert dict_c == {'c': 5, 'd': 6}
def test_deep_merge_with_non_mapping_as_primary_value(): dict_a = {'a': 1, 'b': {'x': 5, 'y': 6}} dict_b = {'c': 3, 'b': {'y': 7, 'z': 8}} dict_c = {'d': 5, 'b': 6} assert deep_merge_dicts(dict_a, dict_b, dict_c) == { 'a': 1, 'b': 6, 'c': 3, 'd': 5 }
def test_deep_merge_orger_precidence(): assert deep_merge_dicts({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }) == { 'a': 1, 'b': 3, 'c': 4 }
def test_deep_merge_does_not_mutate(): dict_a = {'a': 1, 'b': 2} dict_b = {'b': 3, 'c': 4} dict_c = {'c': 5, 'd': 6} assert deep_merge_dicts(dict_a, dict_b, dict_c) == { 'a': 1, 'b': 3, 'c': 5, 'd': 6 } assert dict_a == {'a': 1, 'b': 2} assert dict_b == {'b': 3, 'c': 4} assert dict_c == {'c': 5, 'd': 6}
def test_deep_merge_with_multiple_args(): assert deep_merge_dicts({ 'a': 1, 'b': 2 }, { 'c': 3, 'd': 4 }) == { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
def test_deep_merge_with_deep_dicts(): dict_a = {'a': 1, 'b': {'x': 5, 'y': 6}} dict_b = {'c': 3, 'b': {'y': 7, 'z': 8}} dict_c = {'d': 5, 'b': {'z': 9}} assert deep_merge_dicts(dict_a, dict_b, dict_c) == { 'a': 1, 'b': { 'x': 5, 'y': 7, 'z': 9 }, 'c': 3, 'd': 5 }
} }, "TestRPC": { "provider": { "class": "web3.providers.tester.TestRPCProvider" } }, "Tester": { "provider": { "class": "web3.providers.tester.EthereumTesterProvider" } } } } EXPECTED_V2_CONFIG = deep_merge_dicts( V2_DEFAULT_CONFIG, {'web3': {'Ropsten': BASE_V1_CONFIG['web3']['Ropsten']}}, {'web3': {'InfuraMainnet': {'eth': BASE_V1_CONFIG['web3']['InfuraMainnet']['eth']}}}, {'compilation': {'contracts_source_dir': BASE_V1_CONFIG['compilation']['contracts_dir']}}, {'chains': {'ropsten': {'web3': BASE_V1_CONFIG['chains']['ropsten']['web3']}}}, ) def test_non_default_v1_config_upgrade(): v1_config = copy.deepcopy(BASE_V1_CONFIG) copy.deepcopy(V2_DEFAULT_CONFIG) upgraded_config = upgrade_v1_to_v2(v1_config) assert upgraded_config == EXPECTED_V2_CONFIG
def test_deep_merge_with_no_args(): assert deep_merge_dicts() == {}
def test_deep_merge_with_single_arg(): assert deep_merge_dicts({'a': 1, 'b': 2}) == {'a': 1, 'b': 2}
def upgrade_v4_to_v5(v4_config): """ Upgrade a v4 config file to a v5 config file. """ errors = get_validation_errors(v4_config, version=V4) if errors: raise ValueError( "Cannot upgrade invalid config. Please ensure that your current " "configuration file is valid:\n\n{0}".format( format_errors(errors), )) v4_default_config = load_default_config(version=V4) v5_default_config = load_default_config(version=V5) if v4_config == v4_default_config: return v5_default_config upgraded_v4_config = copy.deepcopy(v4_config) # new configuration values whos keys were not present in the previous # configuration. for key_path in NEW_V5_PATHS: if has_nested_key(upgraded_v4_config, key_path): continue set_nested_key( upgraded_v4_config, key_path, get_nested_key(v5_default_config, key_path), ) # keys in the new configuration that were relocated. for old_path, new_path in MOVED_V4_PATHS.items(): default_value = get_nested_key(v5_default_config, new_path) if has_nested_key(upgraded_v4_config, old_path): existing_value = pop_nested_key(upgraded_v4_config, old_path) if is_dict(default_value) and is_dict(existing_value): merged_value = deep_merge_dicts(default_value, existing_value) elif is_list_like(default_value) and is_list_like(existing_value): merged_value = list( set(itertools.chain(default_value, existing_value))) else: raise ValueError("Unable to merge {0} with {1}".format( type(default_value), type(existing_value), )) set_nested_key( upgraded_v4_config, new_path, merged_value, ) else: set_nested_key( upgraded_v4_config, new_path, default_value, ) # keys from the previous configuration that were changed. for key_path in MODIFIED_V4_PATHS: new_default = get_nested_key(v5_default_config, key_path) if key_path not in upgraded_v4_config: set_nested_key( upgraded_v4_config, key_path, new_default, ) else: current_value = get_nested_key(upgraded_v4_config, key_path) old_default = get_nested_key(v4_default_config, key_path) if current_value == old_default: set_nested_key( upgraded_v4_config, key_path, new_default, ) # bump the version set_nested_key(upgraded_v4_config, 'version', V5) errors = get_validation_errors(upgraded_v4_config, version=V5) if errors: raise ValueError("Upgraded configuration did not pass validation:\n\n" "\n=============Original-Configuration============\n" "{0}" "\n=============Upgraded-Configuration============\n" "{1}" "\n=============Validation-Errors============\n" "{2}".format( pprint.pformat(dict(v4_config)), pprint.pformat(dict(upgraded_v4_config)), format_errors(errors), )) return upgraded_v4_config
def test_deep_merge_with_multiple_args(): expected = {'a': 1, 'b': 2, 'c': 3, 'd': 4} actual = deep_merge_dicts({'a': 1, 'b': 2}, {'c': 3, 'd': 4}) assert expected == actual
def upgrade_v3_to_v4(v3_config): """ Upgrade a v3 config file to a v4 config file. """ errors = get_validation_errors(v3_config, version=V3) if errors: raise ValueError( "Cannot upgrade invalid config. Please ensure that your current " "configuration file is valid:\n\n{0}".format( format_errors(errors), ) ) v3_default_config = load_default_config(version=V3) v4_default_config = load_default_config(version=V4) if v3_config == v3_default_config: return v4_default_config upgraded_v3_config = copy.deepcopy(v3_config) for key_path in NEW_V4_PATHS: if has_nested_key(upgraded_v3_config, key_path): continue set_nested_key( upgraded_v3_config, key_path, get_nested_key(v4_default_config, key_path), ) for old_path, new_path in MOVED_V3_PATHS.items(): default_value = get_nested_key(v4_default_config, new_path) if has_nested_key(upgraded_v3_config, old_path): existing_value = pop_nested_key(upgraded_v3_config, old_path) if is_dict(default_value) and is_dict(existing_value): merged_value = deep_merge_dicts(default_value, existing_value) elif is_list_like(default_value) and is_list_like(existing_value): merged_value = list(set(itertools.chain(default_value, existing_value))) else: raise ValueError( "Unable to merge {0} with {1}".format( type(default_value), type(existing_value), ) ) set_nested_key( upgraded_v3_config, new_path, merged_value, ) else: set_nested_key( upgraded_v3_config, new_path, default_value, ) # bump the version set_nested_key(upgraded_v3_config, 'version', V4) errors = get_validation_errors(upgraded_v3_config, version=V4) if errors: raise ValueError( "Upgraded configuration did not pass validation:\n\n" "\n=============Original-Configuration============\n" "{0}" "\n=============Upgraded-Configuration============\n" "{1}" "\n=============Validation-Errors============\n" "{2}".format( pprint.pformat(dict(v3_config)), pprint.pformat(dict(upgraded_v3_config)), format_errors(errors), ) ) return upgraded_v3_config
"provider": { "class": "web3.providers.tester.EthereumTesterProvider" } } } } EXPECTED_V4_CONFIG = deep_merge_dicts( V4_DEFAULT_CONFIG, { 'compilation': { 'backends': { 'SolcCombinedJSON': { 'settings': { 'optimize': True, 'optimize_runs': 201, } } } } }, {'compilation': { 'import_remappings': ['zeppelin=zeppelin'] }}, ) def test_non_default_v3_config_upgrade(): v3_config = copy.deepcopy(BASE_V3_CONFIG) v4_default_config = copy.deepcopy(V4_DEFAULT_CONFIG) upgraded_config = upgrade_v3_to_v4(v3_config)
def test_deep_merge_orger_precidence(): assert deep_merge_dicts({'a': 1, 'b': 2}, {'b': 3, 'c': 4}) == {'a': 1, 'b': 3, 'c': 4}
}, "TestRPC": { "provider": { "class": "web3.providers.tester.TestRPCProvider" } }, "Tester": { "provider": { "class": "web3.providers.tester.EthereumTesterProvider" } } } } EXPECTED_V3_CONFIG = deep_merge_dicts( V3_DEFAULT_CONFIG, {'web3': {'Ropsten': BASE_V2_CONFIG['web3']['Ropsten']}}, {'web3': {'InfuraMainnet': {'eth': BASE_V2_CONFIG['web3']['InfuraMainnet']['eth']}}}, {'compilation': {'contracts_source_dir': BASE_V2_CONFIG['compilation']['contracts_source_dir']}}, {'chains': {'ropsten': {'web3': BASE_V2_CONFIG['chains']['ropsten']['web3']}}}, ) def test_non_default_v2_config_upgrade(): v2_config = copy.deepcopy(BASE_V2_CONFIG) v3_default_config = copy.deepcopy(V3_DEFAULT_CONFIG) upgraded_config = upgrade_v2_to_v3(v2_config) assert upgraded_config == EXPECTED_V3_CONFIG
def test_deep_merge_with_non_mapping_as_primary_value(): dict_a = {'a': 1, 'b': {'x': 5, 'y': 6}} dict_b = {'c': 3, 'b': {'y': 7, 'z': 8}} dict_c = {'d': 5, 'b': 6} assert deep_merge_dicts(dict_a, dict_b, dict_c) == {'a': 1, 'b': 6, 'c': 3, 'd': 5}
"TestRPC": { "provider": { "class": "web3.providers.tester.TestRPCProvider" } }, "Tester": { "provider": { "class": "web3.providers.tester.EthereumTesterProvider" } } } } EXPECTED_V4_CONFIG = deep_merge_dicts( V4_DEFAULT_CONFIG, {'compilation': {'backends': {'SolcCombinedJSON': {'settings': { 'optimize': True, 'optimize_runs': 201, }}}}}, {'compilation': {'import_remappings': ['zeppelin=zeppelin']}}, ) def test_non_default_v3_config_upgrade(): v3_config = copy.deepcopy(BASE_V3_CONFIG) copy.deepcopy(V4_DEFAULT_CONFIG) upgraded_config = upgrade_v3_to_v4(v3_config) assert upgraded_config == EXPECTED_V4_CONFIG
def upgrade_v3_to_v4(v3_config): """ Upgrade a v3 config file to a v4 config file. """ errors = get_validation_errors(v3_config, version=V3) if errors: raise ValueError( "Cannot upgrade invalid config. Please ensure that your current " "configuration file is valid:\n\n{0}".format( format_errors(errors), )) v3_default_config = load_default_config(version=V3) v4_default_config = load_default_config(version=V4) if v3_config == v3_default_config: return v4_default_config upgraded_v3_config = copy.deepcopy(v3_config) for key_path in NEW_V4_PATHS: if has_nested_key(upgraded_v3_config, key_path): continue set_nested_key( upgraded_v3_config, key_path, get_nested_key(v4_default_config, key_path), ) for old_path, new_path in MOVED_V3_PATHS.items(): default_value = get_nested_key(v4_default_config, new_path) if has_nested_key(upgraded_v3_config, old_path): existing_value = pop_nested_key(upgraded_v3_config, old_path) if is_dict(default_value) and is_dict(existing_value): merged_value = deep_merge_dicts(default_value, existing_value) elif is_list_like(default_value) and is_list_like(existing_value): merged_value = list( set(itertools.chain(default_value, existing_value))) else: raise ValueError("Unable to merge {0} with {1}".format( type(default_value), type(existing_value), )) set_nested_key( upgraded_v3_config, new_path, merged_value, ) else: set_nested_key( upgraded_v3_config, new_path, default_value, ) # bump the version set_nested_key(upgraded_v3_config, 'version', V4) errors = get_validation_errors(upgraded_v3_config, version=V4) if errors: raise ValueError("Upgraded configuration did not pass validation:\n\n" "\n=============Original-Configuration============\n" "{0}" "\n=============Upgraded-Configuration============\n" "{1}" "\n=============Validation-Errors============\n" "{2}".format( pprint.pformat(dict(v3_config)), pprint.pformat(dict(upgraded_v3_config)), format_errors(errors), )) return upgraded_v3_config
def upgrade_v4_to_v5(v4_config): """ Upgrade a v4 config file to a v5 config file. """ errors = get_validation_errors(v4_config, version=V4) if errors: raise ValueError( "Cannot upgrade invalid config. Please ensure that your current " "configuration file is valid:\n\n{0}".format( format_errors(errors), ) ) v4_default_config = load_default_config(version=V4) v5_default_config = load_default_config(version=V5) if v4_config == v4_default_config: return v5_default_config upgraded_v4_config = copy.deepcopy(v4_config) # new configuration values whos keys were not present in the previous # configuration. for key_path in NEW_V5_PATHS: if has_nested_key(upgraded_v4_config, key_path): continue set_nested_key( upgraded_v4_config, key_path, get_nested_key(v5_default_config, key_path), ) # keys in the new configuration that were relocated. for old_path, new_path in MOVED_V4_PATHS.items(): default_value = get_nested_key(v5_default_config, new_path) if has_nested_key(upgraded_v4_config, old_path): existing_value = pop_nested_key(upgraded_v4_config, old_path) if is_dict(default_value) and is_dict(existing_value): merged_value = deep_merge_dicts(default_value, existing_value) elif is_list_like(default_value) and is_list_like(existing_value): merged_value = list(set(itertools.chain(default_value, existing_value))) else: raise ValueError( "Unable to merge {0} with {1}".format( type(default_value), type(existing_value), ) ) set_nested_key( upgraded_v4_config, new_path, merged_value, ) else: set_nested_key( upgraded_v4_config, new_path, default_value, ) # keys from the previous configuration that were changed. for key_path in MODIFIED_V4_PATHS: new_default = get_nested_key(v5_default_config, key_path) if key_path not in upgraded_v4_config: set_nested_key( upgraded_v4_config, key_path, new_default, ) else: current_value = get_nested_key(upgraded_v4_config, key_path) old_default = get_nested_key(v4_default_config, key_path) if current_value == old_default: set_nested_key( upgraded_v4_config, key_path, new_default, ) # bump the version set_nested_key(upgraded_v4_config, 'version', V5) errors = get_validation_errors(upgraded_v4_config, version=V5) if errors: raise ValueError( "Upgraded configuration did not pass validation:\n\n" "\n=============Original-Configuration============\n" "{0}" "\n=============Upgraded-Configuration============\n" "{1}" "\n=============Validation-Errors============\n" "{2}".format( pprint.pformat(dict(v4_config)), pprint.pformat(dict(upgraded_v4_config)), format_errors(errors), ) ) return upgraded_v4_config