Source code for aviary.utils.merge_hierarchies

import copy

from aviary.utils.compare_hierarchies import (compare_hierarchies_to_merge,
                                              compare_inner_classes)


[docs] def merge_attributes(base_class, merge_class, base_class_attributes, merge_class_attributes): ''' Adds unique attributes of merge_class to base_class. For all the attributes of merge_class that are not present in base_class we add them to base_class. Attributes present in both classes are ignored because if they are variables they have already been checked and found identical and if they are inner classes they will be addressed on a recursive call to this function. Attributes present only in base_class are ignored because we are adding to base_class and thus all of its features are automatically preserved. Parameters ---------- base_class : class A class that is all or part of a variable hierarchy. This can be the top=level class in the hierarchy, or any inner class nested at any depth within that top-level class. merge_class : class A class that is all or part of a variable hierarchy. This can be the top=level class in the hierarchy, or any inner class nested at any depth within that top-level class. base_class_attributes : set of strings A set of the name of all attributes (either variables, inner classes, or both) of base_class. merge_class_attributes : set of strings A set of the name of all attributes (either variables, inner classes, or both) of merge_class. Returns ------- base_class : class A class that contains the merged together attributes of base_class and merge_class, with the exception that inner class base_class and merge_class share which have diverging attributes inside of them are not necessarily included. Raises ------ None No exceptions raised by this method, although other methods called within may raise exceptions. ''' merge_class_unique = merge_class_attributes - \ base_class_attributes # attributes present only in merge_class for attr in merge_class_unique: setattr(base_class, attr, getattr(merge_class, attr)) return base_class
[docs] def recursive_merge(overlapping_inners, base_class, merge_class): ''' Recursively compares all inner classes to an infinite depth and identifies mismatched string-named values. For all of the inner class names provided in overlapping_inner_classes this function calls compare_inner_classes and compares those inner classes recursively until it reaches the full depth to which outer_class_a and outer_class_b have any inner classes in common. Parameters ---------- overlapping_inner_classes : set of strings This is a set of strings where each string is the name of an inner class that outer_class_a and outer_class_b have in common. outer_class_a : class A class that is all or part of a variable hierarchy. This can be the top-level class in the hierarchy, or any inner class nested at any depth within that top-level class. outer_class_b : class A class that is all or part of a variable hierarchy. This can be the top-level class in the hierarchy, or any inner class nested at any depth within that top-level class. Returns ------- None No variables returned by this method. Raises ---------- None No exceptions raised by this method, although other methods called within may raise exceptions. ''' for overlapping_class_name in overlapping_inners: overlapping_inner_class_base = getattr(base_class, overlapping_class_name) overlapping_inner_class_merge = getattr(merge_class, overlapping_class_name) [overlapping_second_inners, vars_base_inner, vars_merge_inner, inners_base_inner, inners_merge_inner] = compare_inner_classes( overlapping_inner_class_base, overlapping_inner_class_merge, show_all=True) merge_attributes(overlapping_inner_class_base, overlapping_inner_class_merge, vars_base_inner, vars_merge_inner) merge_attributes(overlapping_inner_class_base, overlapping_inner_class_merge, inners_base_inner, inners_merge_inner) recursive_merge(overlapping_second_inners, overlapping_inner_class_base, overlapping_inner_class_merge)
[docs] def merge_two_hierarchies(base_hierarchy, hierarchy_b): ''' Merge two variable hierarchies together by adding the second into the first. Add the attributes (variables and inner classes) of two variable hierarchies together, so that the attributes that are unique to each hierarchy are combined in the resultant hierarchy. This is accomplished by adding on to the first of the two provided hierarchies, and returning it. Parameters ---------- base_hierarchy : class An Aviary variable hierarchy. This hierarchy will function as the 'base' hierarchy, which is the hierarchy that has attributes added onto it from another hierarchy in order to combine the attributes of the base and the other hierarchy. hierarchy_b : class An Aviary variable hierarchy. This hierarchy will function as the 'auxiliary' hierarhcy, which is the hierarchy whose unique attributes are added onto a base in order to combine the attributes of the base and the auxiliary. Returns ------- base_hierarchy : class An Aviary variable hierarchy which includes both the attributes of the inputted base_hierarchy and hierarchy_b. This is the same object as the inputted base_hierarchy and has been updated through mutability. Raises ---------- None No exceptions raised by this method, although other methods called within may raise exceptions. ''' [overlapping_inners, merged_vars, b_vars, merged_inners, b_inners] = compare_inner_classes( base_hierarchy, hierarchy_b, show_all=True) # this adds the variables of hierarchy_b which are not in base_hierarchy to the overall merged hierarchy base_hierarchy = merge_attributes( base_hierarchy, hierarchy_b, merged_vars, b_vars) # this adds the inner classes of hierarchy_b which are not in base_hierarchy to the overall merged hierarchy base_hierarchy = merge_attributes( base_hierarchy, hierarchy_b, merged_inners, b_inners) recursive_merge(overlapping_inners, base_hierarchy, hierarchy_b) return base_hierarchy
[docs] def merge_hierarchies(hierarchies_to_merge): ''' Combines all provided variable hierarchies into one unified variable hierarchy. Performs checks on the user-provided list of variable hierarchies in order to ensure that they are compatible for merge. Ensures that they are not derived from different superclasses, and that they do not have conflicting values. Assuming all checks pass, the provided hierarchies are combined into a single hierarchy. Parameters ---------- hierarchies_to_merge : list of classes This is a list of all the variable hierarchies which should be merged into a single hierarchy. This list should not include hierarchies of multiple types (i.e. an av.Mission hierarchy extension should not be mixed with an av.Aircraft hierarchy extension) Returns ------- merged_hierarchy : class Aviary variable hierarchy that includes all the variables present in the inputted hierarchy list. Duplicates have been removed, and conflicting duplicates are not possible. Raises ------ ValueError Raises an exception if the list of inputted variable hierarchies includes hierarchies that have been subclassed from different superclasses. ''' compare_hierarchies_to_merge(hierarchies_to_merge) subclass_type = None subclass_hierarchy = None for hierarchy in hierarchies_to_merge: # check that there are not hierarchies subclassing from different classes that we are attempting to merge # checks if the given hierarchy is the first subclass of the hierarchies if (len(hierarchy.__mro__) > 2) and (subclass_type == None): # gets highest level superclass of the class before "class" itself subclass_type = copy.deepcopy(hierarchy.__mro__[-2]) subclass_hierarchy = copy.deepcopy(hierarchy) elif (len(hierarchy.__mro__) > 2): # checks if the given hierarchy is a subclass if hierarchy.__mro__[-2] != subclass_type: raise ValueError( f"You have attempted to merge together variable hierarchies that subclass from different superclasses. '{subclass_hierarchy.__qualname__}' is a subclass of '{subclass_type}' and '{hierarchy.__qualname__}' is a subclass of '{hierarchy.__mro__[-2]}'.") if subclass_hierarchy != None: # ensure that we build on the subclassed hierarchy if we have one, that way the super class information is not lost merged_hierarchy = subclass_hierarchy else: merged_hierarchy = copy.deepcopy(hierarchies_to_merge[0]) for hierarchy in hierarchies_to_merge: merged_hierarchy = merge_two_hierarchies(merged_hierarchy, hierarchy) return merged_hierarchy