
class DeepMerge(object):
    # Merges two deeply nested json-like dictionaries,
    # useful for updating things like task data.
    # I know in my heart, that this isn't completely correct.
    # But I don't want to create a dependency, and this is passing
    # all the failure points I've found so far.  So I'll just
    # keep plugging away at it.
    # This will merge all updates from b into a, but it does not
    # remove items from a that are not in b.  Passing a prune of
    # true, and it WILL remove items in a that are not in b.

    @staticmethod
    def merge(a, b, path=None):
        "merges b into a"
        if path is None: path = []
        for key in b:
            if key in a:
                if a[key] == b[key]:
                    continue
                elif isinstance(a[key], dict) and isinstance(b[key], dict):
                    DeepMerge.merge(a[key], b[key], path + [str(key)])
                elif isinstance(a[key], list) and isinstance(b[key], list):
                    DeepMerge.merge_array(a[key], b[key], path + [str(key)])
                else:
                    a[key] = b[key]  # Just overwrite the value in a.
            else:
                a[key] = b[key]
        return a

    @staticmethod
    def merge_array(a, b, path=None):

        for idx, val in enumerate(b):
            if isinstance(b[idx], dict):  # Recurse back on dictionaries.
                # If lists of dictionaries get out of order, this might
                # cause us some pain.
                if len(a) > idx:
                    a[idx] = DeepMerge.merge(a[idx], b[idx], path + [str(idx)])
                else:
                    a.append(b[idx])
            else: # Just merge whatever it is back in.
                a.extend(x for x in b if x not in a)

        # Trim a back to the length of b.  In the end, the two arrays should match
        del a[len(b):]
