From 7e83d0876ddb84a45e130eeba28bc40ef53c074b Mon Sep 17 00:00:00 2001 From: Yaron Yogev Date: Thu, 27 Jul 2017 09:02:54 +0300 Subject: Calipso initial release for OPNFV Change-Id: I7210c244b0c10fa80bfa8c77cb86c9d6ddf8bc88 Signed-off-by: Yaron Yogev --- app/utils/deep_merge.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 app/utils/deep_merge.py (limited to 'app/utils/deep_merge.py') diff --git a/app/utils/deep_merge.py b/app/utils/deep_merge.py new file mode 100644 index 0000000..acb54ff --- /dev/null +++ b/app/utils/deep_merge.py @@ -0,0 +1,77 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +""" +Do a deep merge of dictionariesi, +recursively merging dictionaries with boltons.iterutils.remap + +Taken from: +https://gist.github.com/mahmoud/db02d16ac89fa401b968 + + +This is an extension of the technique first detailed here: +http://sedimental.org/remap.html#add_common_keys +In short, it calls remap on each container, back to front, +using the accumulating previous values as the default for +the current iteration. +""" + +from boltons.iterutils import remap, get_path, default_enter, default_visit + + +def remerge(target_list, sourced=False): + """ + Takes a list of containers (e.g., dicts) and merges them using + boltons.iterutils.remap. Containers later in the list take + precedence (last-wins). + By default, returns a new, merged top-level container. With the + *sourced* option, `remerge` expects a list of (*name*, container*) + pairs, and will return a source map: a dictionary mapping between + path and the name of the container it came from. + """ + + if not sourced: + target_list = [(id(t), t) for t in target_list] + + ret = None + source_map = {} + + def remerge_enter(path, key, value): + new_parent, new_items = default_enter(path, key, value) + if ret and not path and key is None: + new_parent = ret + try: + cur_val = get_path(ret, path + (key,)) + except KeyError: + pass + else: + # TODO: type check? + new_parent = cur_val + + if isinstance(value, list): + # lists are purely additive. + # See https://github.com/mahmoud/boltons/issues/81 + new_parent.extend(value) + new_items = [] + + return new_parent, new_items + + for t_name, target in target_list: + if sourced: + def remerge_visit(path, key, value): + source_map[path + (key,)] = t_name + return True + else: + remerge_visit = default_visit + + ret = remap(target, enter=remerge_enter, visit=remerge_visit) + + if not sourced: + return ret + return ret, source_map -- cgit 1.2.3-korg