def clear(self): """Sets a boolean option to false""" if self.type in ["config", "menuconfig"]: config = data.get_config(self.value) if len(config['selected_by']) > 0: # menuconfig shouldn't mark set by user if option can't be changed return if config['datatype'] == 'bool': set_config(self.value, False)
def set_config_selectifs(key): c = data.get_config(key) value = c['value'] if "select_if" in c: for k in c["select_if"]: if expr.condexpr_value(k[1]): force_config(k[0], value, key) else: # No longer forced, so turn off if previously forced force_config(k[0], False, key)
def enforce_dependent_values(stage, fix_bools=False, error_level=logging.WARNING): """ Check that values are consistently set, specifically with respect to whether the dependencies are correct. This function is called when we expect the values to be consistent. i.e. after reading in a new config, or prior to writing it out. It is called prior to plugin execution, to try and ensure the plugins see consistent state. For non-boolean configs (string, int), set their value to default ("", 0) if the config's dependencies are not met. This can happen where a user sets the value of the config, and then changes another config resulting in the dependency being disabled. Boolean values are treated specially. Normally they will be kept in a consistent state (wrt dependencies) by set_config(). However they can be forced by a 'select' statement even when the dependencies are not met. This indicates there is a problem with the Mconfig that needs to be fixed. This function will only reset Boolean values to n if specifically requested by fix_bools=True. This is only expected to be used after reading in a config file. That file may have been written with a different Mconfig that allowed the selection. The error_level determines the log level at which bool inconsistencies are reported. When set to logging.ERROR this forces the user to fix the Mconfig. """ for i in data.get_config_list(): c = data.get_config(i) if can_enable(c): continue if c['datatype'] == 'bool' and c['value'] is True: if len(c['selected_by']) > 0: msg = "{0}unmet direct dependencies: {1} depends on {2}, " \ "but is selected by [{3}].".format(stage, i, c['depends'][1], ",".join(c['selected_by'])) if error_level is logging.ERROR: msg += " Update the Mconfig so that this can't happen" logger.log(error_level, msg) else: raise Exception("Unmet direct dependencies without select") if fix_bools: set_config_internal(i, False) elif c['datatype'] == 'string': set_config_internal(i, '') elif c['datatype'] == 'int': set_config_internal(i, 0)
def validate_configs(): """ Ensure that the types in 'default' statements are correct, and that the configs referred to in 'select' statements are boolean. """ config_list = data.get_config_list() for k in config_list: c = data.get_config(k) if "default_cond" in c: for i in c['default_cond']: check_type(k, c, i['expr']) if "default" in c: check_type(k, c, c["default"]) if "select_if" in c: for k in c["select_if"]: try: c1 = data.get_config(k[0]) except KeyError: logger.warning("Ignoring unknown configuration option %s" % k[0]) continue if c1['datatype'] != "bool": logger.error( 'Select option must have type bool but got type %s instead' % c1['datatype']) if "select" in c: for k in c["select"]: try: c1 = data.get_config(k) except KeyError: logger.warning("Ignoring unknown configuration option %s" % k) continue if c1['datatype'] != "bool": logger.error( 'Select option must have type bool but got type %s instead' % c1['datatype'])
def get_user_set_options(): """ Return all the options which have been set by the user """ user_set_options = [] for (i_type, i_symbol) in data.iter_symbols_menuorder(): if i_type in ["config", "menuconfig"]: c = data.get_config(i_symbol) if c['is_user_set']: value = c['value'] if c['datatype'] == "bool": value = "y" if c['value'] else "n" source = c['source'] if "source" in c else "" user_set_options.append((i_symbol, value, source)) return user_set_options
def test_condexpr_evaluation(caplog, tmpdir, inputdata, result, error): mconfig_file = tmpdir.join("Mconfig") mconfig = template_condexpr_mconfig.format(**inputdata) mconfig_file.write(mconfig, "wt") data.init(str(mconfig_file), False) c = data.get_config("OPTION") general.set_initial_values() val = expr.condexpr_value(c['default_cond'][0]['cond']) if error is not None: assert error in caplog.text else: assert val == result assert caplog.text == ""
def config_to_json(): properties = dict() for key in data.get_config_list(): c = data.get_config(key) key = key.lower() datatype = c["datatype"] value = c["value"] if datatype not in ["bool", "int", "string"]: logger.error("Invalid config type: %s (with value '%s')\n" % (datatype, str(value))) if datatype == "int": value = int(value) properties[key] = {"ignore": c["bob_ignore"] == 'y', "value": value} return properties
def check_value_as_requested(key, requested_value, later_keys, later_values): try: opt = get_config(key) except KeyError: logger.error("unknown configuration option \"%s\"" % key) return final_value = opt['value'] if opt['datatype'] == 'int': final_value = str(final_value) elif opt['datatype'] == 'bool': final_value = 'y' if final_value else 'n' if final_value == requested_value: return # See if the argument was overridden by a later argument last_idx = rindex(later_keys, key) if last_idx != -1 and later_values[last_idx] != requested_value: logger.error("%s=%s was overridden by later argument %s=%s", key, requested_value, key, later_values[last_idx]) return if not can_enable(opt): depends = opt['depends'] logger.error("%s=%s was ignored; its dependencies were not met: %s", key, requested_value, format_dependency_list(depends, skip_parens=True)) return # Check this *after* dependencies. This allows users to investigate why an # option with unmet dependencies wasn't enabled, even if it isn't user-settable. if not opt.get("title"): logger.error( "%s=%s was ignored; it has no title, so is not user-settable " "(%s has no unmet dependencies)", key, requested_value, key) return logger.error("%s=%s was ignored or overriden. Value is '%s' %s %s", key, requested_value, final_value, type(requested_value), type(final_value))
def set_config_if_prompt(key, value, is_user_set=True): """ Used to set the option value from the command line, and through profile files. Only options that have prompts (indicating they are user-settable) can be set. """ logger.debug("Trying to set %s %s" % (key, value)) try: c = data.get_config(key) except KeyError: logger.warning("Ignoring unknown configuration option %s" % key) return c['is_new'] = False logger.debug(c) if 'title' in c: logger.debug("Setting %s : %s " % (key, value)) if c['datatype'] == 'bool': value = True if value == 'y' else False set_config(key, value, is_user_set)
def config_to_json(): properties = dict() for key in data.get_config_list(): c = data.get_config(key) key = key.lower() datatype = c["datatype"] value = c["value"] if datatype == "bool": properties[key] = value elif datatype == "int": properties[key] = int(value) elif datatype == "string": properties[key] = value else: logger.error("Invalid config type: %s (with value '%s')\n" % (datatype, str(value))) return properties
def get_help(self): if self.type in ["config", "menuconfig"]: config = data.get_config(self.value) text = self.value + ": " + config['title'] + "\n\n" if "help" in config: text += config['help'] else: text += "No help available" return text elif self.type in ["choice"]: choice = data.get_choice_group(self.value) text = choice['title'] + "\n\n" if "help" in choice: text += choice['help'] else: text += "No help available" return text elif self.type in ["menu"]: config = data.get_menu(self.value) if 'help' in config: return config['help'] return "No help available"
def _condexpr_value(e): """Evaluate the value of the input expression. """ assert type(e) == tuple assert len(e) in [2, 3] if len(e) == 3: if e[0] in ARITH_SET: return _expr_value(e) left = _condexpr_value(e[1]) right = _condexpr_value(e[2]) if type(left) != type(right): # Boolean result expected return False elif e[0] == 'and': return left and right elif e[0] == 'or': return left or right elif e[0] == '=': return left == right elif e[0] == '!=': return left != right elif e[0] == '>': return left > right elif e[0] == '>=': return left >= right elif e[0] == '<': return left < right elif e[0] == '<=': return left <= right elif e[0] == 'not': return not _condexpr_value(e[1]) elif e[0] in ['string', 'number', 'boolean']: return e[1] elif e[0] == 'identifier': return get_config(e[1])['value'] raise Exception("Unexpected depend list: " + str(e))
def force_config(key, value, source): try: c = data.get_config(key) except KeyError: logger.warn("Ignoring unknown configuration option %s" % key) return if value is True: if source in c['selected_by']: return c['selected_by'].add(source) elif source in c['selected_by']: c['selected_by'].remove(source) else: # Option wasn't previously forced, so don't change it return if len(c['selected_by']) > 0: set_config_internal(key, True) elif 'requested_value' in c: set_config(key, c['requested_value']) else: update_defaults(key)
def menu_parse(): menus = {None: []} for i in data.get_menu_list(): menus[i] = [] menuconfig_stack = [] depends_stack = [] for (i_type, i_symbol) in data.iter_symbols_menuorder(): if i_type == "config": config = data.get_config(i_symbol) inmenu = config.get('inmenu') if 'title' not in config: # No title, so we don't display it continue while len(depends_stack) > 0: if expr.check_depends(config.get('depends'), depends_stack[-1]): break depends_stack.pop() while len(menuconfig_stack) > 0: if expr.check_depends(config.get('depends'), menuconfig_stack[-1]): inmenu = menuconfig_stack[-1] break menuconfig_stack.pop() depends_stack = [] if 'choice_group' in config: inmenu = config['choice_group'] config['depends_indent'] = len(depends_stack) menus[inmenu].append(MenuItem('config', i_symbol)) depends_stack.append(i_symbol) elif i_type == "menuconfig": config = data.get_config(i_symbol) inmenu = config.get('inmenu') while len(menuconfig_stack) > 0: if expr.check_depends(config.get('depends'), menuconfig_stack[-1]): inmenu = menuconfig_stack[-1] break menuconfig_stack.pop() menuconfig_stack.append(i_symbol) menus[i_symbol] = [] menus[inmenu].append(MenuItem('menuconfig', i_symbol)) elif i_type == "menu": menu = data.get_menu(i_symbol) inmenu = menu.get('inmenu') menuconfig_stack = [] menus[inmenu].append(MenuItem('menu', i_symbol)) elif i_type == "choice": inmenu = data.get_choice_group(i_symbol).get('inmenu') menus[i_symbol] = [] menus[inmenu].append(MenuItem('choice', i_symbol)) else: raise Exception("Unexpected menu item: type {}, symbol {}".format( i_type, i_symbol)) return menus
def get_title(self): return data.get_config(self.value).get('title')
def needs_inputbox(self): if self.type == "config": config = data.get_config(self.value) return config['datatype'] in ["string", "int"] return False
def get_styled_text(self, is_selected, max_width): text_parts = [] if self.type == "config": config = data.get_config(self.value) indent = config.get("depends_indent") or 0 # Display "(new)" next to menu options that have no previously selected value new_text = " (new)" if config.get('is_new') else "" show_value = display_value(config['value'], config['datatype']) if len(config['selected_by']) > 0: text_parts.append(StyledText("-")) text_parts.append(StyledText("%s" % show_value)) text_parts.append(StyledText("-")) elif 'choice_group' in config or config['datatype'] != "bool": text_parts.append(StyledText("(")) trim_to = max_width - len( config['title']) - indent - len(new_text) - 3 trim_to = max(trim_to, 8) # we want to display something if trim_to >= len(show_value): text_parts.append(StyledText("%s" % show_value)) else: text_parts.append( StyledText("%s..." % show_value[:(trim_to - 3)])) text_parts.append(StyledText(")")) else: text_parts.append(StyledText("[")) text_parts.append(StyledText("%s" % show_value)) text_parts.append(StyledText("]")) if config['is_user_set']: text_parts[1].style = 'option_set_by_user' text_parts.append( StyledText(" %s%s%s" % (" " * (indent), config['title'], new_text))) elif self.type == "menu": text_parts.append(StyledText(" ")) text_parts.append( StyledText(" %s --->" % data.get_menu_title(self.value))) elif self.type == "menuconfig": config = data.get_config(self.value) is_menu_enabled = '>' if config['value'] is False: # Submenu is empty is_menu_enabled = '-' text_parts.append(StyledText("[")) show_value = display_value(config['value'], config['datatype']) trim_to = max_width - len(config['title']) - 5 trim_to = max(trim_to, 8) # we want to display something if trim_to >= len(show_value): text_parts.append(StyledText("%s" % show_value)) else: text_parts.append( StyledText("%s..." % show_value[:(trim_to - 5)])) text_parts.append(StyledText("]")) text_parts.append( StyledText(" %s ---%s" % (config['title'], is_menu_enabled))) if config['is_user_set']: text_parts[1].style = 'option_set_by_user' elif self.type == "choice": choice = data.get_choice_group(self.value) current_value = '' for i in choice['configs']: if data.get_config(i)['value'] is True: current_value = data.get_config(i).get('title') break text_parts.append(StyledText(" ")) text_parts.append( StyledText(" %s (%s)" % (choice['title'], current_value))) elif self.type == "empty": text_parts.append(StyledText("***")) text_parts.append(StyledText(" Empty Menu ***")) else: raise Exception("Unknown type (%s)" % self.type) text_parts[ -1].style = 'highlight' if is_selected else get_default_style() return text_parts
def is_menu(self): if self.type == "menuconfig" and data.get_config( self.value)['value'] is False: # Prevent entry to a menuconfig that is disabled return False return self.type in ["menu", "menuconfig", "choice"]
def get_config_string(key): c = data.get_config(key) assert c['datatype'] == 'string', \ 'Config option %s is not a string (has type %s)' % (key, c['datatype']) return c['value']
def get_config_int(key): c = data.get_config(key) assert c['datatype'] == 'int', \ 'Config option %s is not an int (has type %s)' % (key, c['datatype']) return int(c['value'])
def set_config(key, value, is_user_set=True): try: c = data.get_config(key) except KeyError: logger.warning("Ignoring unknown configuration option %s" % key) return if is_user_set: # Validate user input if c['datatype'] == 'bool': # Must be y or n if value not in [True, False]: logger.warning( "Ignoring boolean configuration option %s with non-boolean value '%s'." % (key, value)) return elif c['datatype'] == 'int': # Must convert to an integer try: value = int(value) except ValueError: logger.warning( "Ignoring integer configuration option %s with non-integer value '%s'" % (key, value)) return # Record user specified value even if it is (currently) impossible c['is_user_set'] |= is_user_set if is_user_set: if "choice_group" in c: group = c['choice_group'] if value is True: # Record the selection for this group data.get_choice_group(group)['requested_value'] = key else: c['requested_value'] = value if c['datatype'] == 'bool': if value is False and len(c['selected_by']) > 0: # Option is forced, so cannot be turned off return if value is True and not can_enable(c): # Option is unavailable, so cannot be turned on. However if the # option is selected by another we force it on regardless if len(c['selected_by']) == 0: return c['value'] = value if is_user_set: c['is_new'] = False if "choice_group" in c: group = c['choice_group'] cg = data.get_choice_group(group) if value is True: # Record the selection for this group cg['selected'] = key # Member of a choice group - unset all other items for k in cg['configs']: if k != key: set_config(k, False, is_user_set=is_user_set) if data.get_config(k)['value'] is True: # Failed to turn the other option off, so set this to n c['value'] = False return else: # Check if this is the last entry in a choice being unset. # If there is no other option set then either this entry will be # set back to 'y' - or if this entry cannot be set, the best default # entry in the choice will be picked for k in cg['configs']: if k != key and data.get_config(k)['value'] is True: break else: if can_enable(c): cg['selected'] = key c['value'] = True else: # Reset current selection. cg['selected'] = None update_choice_default(group) if "select" in c: for k in c["select"]: force_config(k, value, key) set_config_selectifs(key) if "rdepends" in c: # Check any reverse dependencies to see if they need updating for k in c['rdepends']: c2 = data.get_config(k) if c2['value'] is True and not can_enable(c2): set_config_internal(k, False) elif not c2['is_user_set']: update_defaults(k) elif "choice_group" in c2: update_choice_default(c2['choice_group']) elif 'requested_value' in c2 and c2['value'] != c2[ 'requested_value']: set_config(k, c2['requested_value']) if "rdefault" in c: # Check whether any default values need updating for k in c['rdefault']: update_defaults(k) if "rselect_if" in c: # Update any select_ifs that might now be valid for k in c['rselect_if']: set_config_selectifs(k)
def get_options_selecting(selected): """Return the options which select `selected`""" opt = data.get_config(selected) return opt.get("selected_by", [])
def set_initial_values(): "Set all configuration objects to their default value" config_list = data.get_config_list() # Set up initial values, and set up reverse dependencies for k in config_list: c = data.get_config(k) c['selected_by'] = set() c['is_user_set'] = False c['is_new'] = True if c['datatype'] == "bool": c['value'] = False elif c['datatype'] == "int": c['value'] = 0 else: c['value'] = '' for i in expr.dependency_list(c.get("depends")): data.get_config(i).setdefault('rdepends', []).append(k) if "default_cond" in c: for j in c['default_cond']: for i in expr.dependency_list(j['cond']): data.get_config(i).setdefault('rdefault', []).append(k) value_deps = expr.dependency_list(j['expr']) for d in value_deps: data.get_config(d).setdefault('rdefault', []).append(k) if "default" in c: value_deps = expr.dependency_list(c['default']) for d in value_deps: data.get_config(d).setdefault('rdefault', []).append(k) if "select_if" in c: for j in c['select_if']: for i in expr.dependency_list(j[1]): data.get_config(i).setdefault('rselect_if', []).append(k) # Check for dependency cycles for k in config_list: c = data.get_config(k) if k in c.get('rdepends', []): logger.error("%s depends on itself" % k) c['rdepends'].remove(k) if k in c.get('rdefault', []): logger.error("The default value of %s depends on itself" % k) c['rdefault'].remove(k) # Set values using defaults and taking into account dependencies for k in config_list: update_defaults(k)