Source code for CIME.BuildTools.possiblevalues

from CIME.XML.standard_module_setup import *
from CIME.BuildTools.macroconditiontree import MacroConditionTree

logger = logging.getLogger(__name__)

[docs]class PossibleValues(object): """Holds a list of settings for a single "Macros" variable. This helper class takes in variable settings and, for each one, decides whether to throw it out, add it to the list of values, or replace the existing list of values with the new, more specific setting. This class also performs ambiguity checking; if it is possible at build time for more than one setting to match the same variable, this is considered an error. Public attributes: name - The name of the variable. settings - The current list of possible initial settings for the variable. append_settings - A dictionary of lists of possible appending settings for the variable, with the specificity of each list as the associated dictionary key. Public methods: add_setting ambiguity_check dependencies to_cond_trees """ def __init__(self, name, setting, specificity, depends): """Construct a PossibleValues object. The name argument refers to the name of the variable. The other arguments are the same as for append_match. """ self.name = name # If this is an appending setting, its specificity can't cause it # to overwrite other settings. if setting.do_append: self.settings = [] self.append_settings = [setting] self._specificities = [] self._depends = [] self._append_depends = depends else: self.settings = [setting] self.append_settings = [] self._specificities = [specificity] self._depends = [depends] self._append_depends = set()
[docs] def add_setting(self, setting, specificity, depends): """Add a possible value for a variable. Arguments: setting - A ValueSetting to start the list. specificity - An integer representing how specific the setting is. Low-specificity settings that will never be used will be dropped from the list. The lowest allowed specificity is 0. depends - A set of variable names, specifying the variables that have to be set before this setting can be used (e.g. if SLIBS refers to NETCDF_PATH, then NETCDF_PATH has to be set first). >>> from CIME.BuildTools.valuesetting import ValueSetting >>> a = ValueSetting('foo', False, dict(), [], []) >>> b = ValueSetting('bar', False, dict(), [], []) >>> vals = PossibleValues('var', a, 0, {'dep1'}) >>> vals.add_setting(b, 1, {'dep2'}) >>> a not in vals.settings and b in vals.settings True >>> 'dep1' not in vals.dependencies() and 'dep2' in vals.dependencies() True >>> vals.add_setting(a, 1, {'dep1'}) >>> a in vals.settings and b in vals.settings True >>> 'dep1' in vals.dependencies() and 'dep2' in vals.dependencies() True """ if setting.do_append: self.append_settings.append(setting) self._append_depends |= depends else: mark_deletion = [] for i in range(len(self.settings)): other_setting = self.settings[i] other_specificity = self._specificities[i] # Ignore this case if it's less specific than one we have. if other_specificity > specificity: if other_setting.has_special_case(setting): return # Override cases that are less specific than this one. elif other_specificity < specificity: if setting.has_special_case(other_setting): mark_deletion.append(i) mark_deletion.reverse() for i in mark_deletion: del self.settings[i] del self._specificities[i] del self._depends[i] self.settings.append(setting) self._specificities.append(specificity) self._depends.append(depends)
[docs] def ambiguity_check(self): """Check the current list of settings for ambiguity. This function raises an error if an ambiguity is found. """ for i in range(len(self.settings)-1): for j in range(i+1, len(self.settings)): if self._specificities[i] != self._specificities[j]: continue other = self.settings[j] expect(not self.settings[i].is_ambiguous_with(other), "Variable "+self.name+" is set ambiguously in " "config_compilers.xml. Check the file for these " "conflicting settings: \n1: {}\n2: {}".format( self.settings[i].conditions, other.conditions))
[docs] def dependencies(self): """Returns a set of names of variables needed to set this variable.""" depends = self._append_depends.copy() for other in self._depends: depends |= other return depends
[docs] def to_cond_trees(self): """Convert this object to a pair of MacroConditionTree objects. This represents the step where the list of possible values is frozen and we're ready to convert it into an actual text file. This object is checked for ambiguities before conversion. The return value is a tuple of two items. The first is a dict of condition trees containing all initial settings, with the specificities as the dictionary keys. The second is a single tree containing all appending settings. If the appending tree would be empty, None is returned instead. """ self.ambiguity_check() # Get all values of specificity for which we need to make a tree. specificities = sorted(list(set(self._specificities))) # Build trees, starting from the least specific and working up. normal_trees = {} for specificity in specificities: settings_for_tree = [self.settings[i] for i in range(len(self.settings)) if self._specificities[i] == specificity] normal_trees[specificity] = MacroConditionTree(self.name, settings_for_tree) if self.append_settings: append_tree = MacroConditionTree(self.name, self.append_settings) else: append_tree = None return (normal_trees, append_tree)