Source code for aviary.utils.compare_hierarchies
from aviary.variable_info.variables import Aircraft as _Aircraft
from aviary.variable_info.variables import Mission as _Mission
[docs]
def compare_inner_classes(class1, class2, show_all=False):
"""
Compare two nested class hierarchies and return a set of shared inner-classes.
Summary:
This function takes in two classes that both are part of variable hierarchies and may contain
inner-classes or variables with string-named values. This function compares those two classes
and returns a set of inner classes that have the same name in both inputted classes. It will throw
an error if the two classes have the same variable with a different string-named value.
Parameters
----------
class1 : 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.
class2 : 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.
show_all : bool, optional
Flag to tell the function to return the sets of variables and inner classes for
each provided class.
Returns
-------
overlapping_inner_classes : set of strings
A set of string names of all the inner classes which are common between the two
input classes.
class1_vars_set : set of strings, optional
Set of string names of the variables belonging to class1, optional return based
on show_all flag.
class2_vars_set : set of strings, optional
Set of string names of the variables belonging to class2, optional return based
on show_all flag.
class1_inner_classes_set : set of strings, optional
Set of the string names of inner classes belonging to class1, optional return based
on show_all flag.
class2_inner_classes_set : set of strings, optional
Set of the string names of inner classes belonging to class2, optional return based
on show_all flag.
Raises
------
ValueError
If the two input classes both have a variable with the same variable name but
different string-named value.
"""
class1_vars_inner_classes = vars(class1)
class2_vars_inner_classes = vars(class2)
class1_vars = []
class1_inner_classes = []
class2_vars = []
class2_inner_classes = []
# separate out a list of string names of the variables belonging to class1, and the inner classes belonging to class1
for key in class1_vars_inner_classes.keys():
# just checks if it is a class
if type(class1_vars_inner_classes[key]) == type(class1):
class1_inner_classes.append(key)
elif (type(class1_vars_inner_classes[key]) == str) and not (key == '__module__') and not (key == '__doc__'):
class1_vars.append(key)
# separate out a list of string names of the variables belonging to class2, and the inner classes belonging to class2
for key in class2_vars_inner_classes.keys():
if type(class2_vars_inner_classes[key]) == type(class2):
class2_inner_classes.append(key)
elif (type(class2_vars_inner_classes[key]) == str) and not (key == '__module__') and not (key == '__doc__'):
class2_vars.append(key)
class1_vars_set = set(class1_vars)
class2_vars_set = set(class2_vars)
# get set of the variables in the provided classes that have the same name
overlapping_vars = class1_vars_set & class2_vars_set
for var in overlapping_vars: # go through overlapping variables and check that they have the same value associated with them
value1 = getattr(class1, var) # value of the variable in the first class
value2 = getattr(class2, var) # value of the variable in the second class
if value1 != value2:
raise ValueError(
f"You have attempted to merge two variable hierarchies together that have the same variable with a different string name associated to it. The offending variable is '{var}'. In '{class1.__qualname__}' it has a value of '{value1}' and in '{class2.__qualname__}' it has a value of '{value2}'.")
class1_inner_classes_set = set(class1_inner_classes)
class2_inner_classes_set = set(class2_inner_classes)
overlapping_inner_classes = class1_inner_classes_set & class2_inner_classes_set
if show_all:
return (overlapping_inner_classes, class1_vars_set, class2_vars_set, class1_inner_classes_set, class2_inner_classes_set)
else:
return (overlapping_inner_classes)
[docs]
def recursive_comparison(overlapping_inner_classes, outer_class_a, outer_class_b):
"""
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
Exceptions
----------
No exceptions explicitly raised by this function, although called functions may raise exceptions.
"""
for overlapping_class_name in overlapping_inner_classes:
overlapping_inner_class_a = getattr(outer_class_a, overlapping_class_name)
overlapping_inner_class_b = getattr(outer_class_b, overlapping_class_name)
overlapping_second_inner_classes = compare_inner_classes(
overlapping_inner_class_a, overlapping_inner_class_b)
recursive_comparison(overlapping_second_inner_classes,
overlapping_inner_class_a, overlapping_inner_class_b)
[docs]
def compare_hierarchies_to_merge(hierarchies_to_merge):
"""
Compares variable hierarchies to ensure there are no string-valued variable conflicts.
For all the variable hierarchies provided in hierarchies_to_merge this function compares each
hierarchy with every other hierarchy as well as the Aviary core aircraft and mission hierarchies
to ensure there are no string-valued variable conflicts within the same class or inner class to
and infinite depth.
Parameters
----------
hierarchies_to_merge : list of classes
This is a list of variable hierarchy classes which will be compared for merge compatibility
with one another.
Returns
-------
None
Raises
------
No explicit exceptions are raised by this function, although called functions may raise exceptions.
"""
for hierarchy in hierarchies_to_merge:
# check if hierarchy has developed conflicts with original hierarchy
if issubclass(hierarchy, _Aircraft):
overlap = compare_inner_classes(hierarchy, _Aircraft)
recursive_comparison(overlap, hierarchy, _Aircraft)
# check if hierarchy has developed conflicts with original hierarchy
if issubclass(hierarchy, _Mission):
overlap = compare_inner_classes(hierarchy, _Mission)
recursive_comparison(overlap, hierarchy, _Mission)
for hierarchy2 in hierarchies_to_merge:
if hierarchy != hierarchy2:
overlap = compare_inner_classes(hierarchy, hierarchy2)
recursive_comparison(overlap, hierarchy, hierarchy2)