Source code for CIME.BuildTools.macroconditiontree

from CIME.XML.standard_module_setup import *
logger = logging.getLogger(__name__)

[docs]class MacroConditionTree(object): # pylint: disable=too-many-instance-attributes """Tree containing the various possible settings of a specific macro. Unlike the PossibleValues class, this class assumes that we have finished determining which settings could apply on a given machine. It also sorts the settings based on the conditions under which they take effect, in preparation for writing out the Macros file itself. Public methods: merge write_out """ def __init__(self, name, settings): """Create a MacroConditionTree recursively. Arguments: name - Name of the variable. settings - A list of all settings for this variable. """ # Search for any conditions controlling the number of settings. condition = None # Prefer the COMPILER attribute as the top level attribute, for # readability of the merged file. if any("COMPILER" in setting.conditions for setting in settings): condition = "COMPILER" else: # To make merging more effective, sort the conditions. all_conditions = [] for setting in settings: all_conditions += setting.conditions.keys() if all_conditions: condition = sorted(all_conditions)[0] if condition is None: # If there are no conditions, we have reached a leaf. # We combine whatever settings are left; there should be at # most one non-appending setting, or an arbitrary number of # appending settings. self._is_leaf = True self._assignments = [] self._set_up = [] self._tear_down = [] self._do_append = True for setting in settings: if not setting.do_append: self._do_append = False assert len(settings) == 1, \ "Internal error in macros: An ambiguity was " \ "found after the ambiguity check was complete, " \ "or there is a mixture of appending and initial " \ "settings in the condition tree." self._assignments.append((name, setting.value)) self._set_up += setting.set_up self._tear_down += setting.tear_down else: # If a condition was found, partition the settings depending on # how they use it, and recursively create a tree for each # partition. self._is_leaf = False self._condition = condition partition = dict() for setting in settings: # If some of the settings don't use a condition, we use # None to represent that. cond_val = setting.conditions.pop(condition, None) if cond_val in partition: partition[cond_val].append(setting) else: partition[cond_val] = [setting] branches = dict() for cond_val in partition: branches[cond_val] = \ MacroConditionTree(name, partition[cond_val]) self._branches = branches # pylint shouldn't concern itself with the way that we access other, since # it's actually a member of the same class. # pylint:disable=protected-access
[docs] def merge(self, other): """Merge another tree with this one. This should be considered destructive to both trees. The only valid value is the one that's returned. """ if self._is_leaf: if other._is_leaf: assert self._do_append == other._do_append, \ "Internal error in macros: Tried to merge an " \ "appending tree with a tree containing initial "\ "settings." # If both are leaves, just merge the values. self._assignments += other._assignments self._set_up += other._set_up self._tear_down += other._tear_down return self else: # If other is not a leaf, swap the arguments so that self # is the one that's not a leaf, handled below. return other.merge(self) else: # If self is not a leaf but other is, it should go in # self._branches[None]. The same goes for the case where the # conditions don't match, and self._condition is last # alphabetically. if other._is_leaf or self._condition > other._condition: if None in self._branches: self._branches[None] = self._branches[None].merge(other) else: self._branches[None] = other return self else: # If the other condition comes last alphabetically, swap # the order. if self._condition < other._condition: return other.merge(self) # If neither is a leaf and their conditions match, merge # their sets of branches. for (cond_val, other_branch) in other._branches.items(): if cond_val in self._branches: self._branches[cond_val] = \ self._branches[cond_val].merge(other_branch) else: self._branches[cond_val] = other_branch return self
# pylint:enable=protected-access
[docs] def write_out(self, writer): """Write tree to file. The writer argument is an object inheriting from MacroWriterBase. This function first writes out all the initial settings with appropriate conditionals, then the appending settings. """ if self._is_leaf: for line in self._set_up: writer.write_line(line) for (name, value) in self._assignments: if self._do_append: writer.append_variable(name, value) else: writer.set_variable(name, value) for line in self._tear_down: writer.write_line(line) else: condition = self._condition # Take care of the settings that don't use this condition. if None in self._branches: self._branches[None].write_out(writer) # Now all the if statements. for cond_val in self._branches: if cond_val is None: continue env_ref = writer.environment_variable_string(condition) writer.start_ifeq(env_ref, cond_val) self._branches[cond_val].write_out(writer) writer.end_ifeq()
[docs]def merge_optional_trees(tree, big_tree): """Merge two MacroConditionTrees when one or both objects may be `None`.""" if tree is not None: if big_tree is None: return tree else: return big_tree.merge(tree) else: return big_tree