diff options
| -rw-r--r-- | kconfiglib.py | 187 | ||||
| -rw-r--r-- | tests/Kmisc | 4 |
2 files changed, 115 insertions, 76 deletions
diff --git a/kconfiglib.py b/kconfiglib.py index 6fe9f88..d5143cf 100644 --- a/kconfiglib.py +++ b/kconfiglib.py @@ -420,6 +420,7 @@ class Kconfig(object): """ __slots__ = ( "_choices", + "_loading_config", "_print_undef_assign", "_print_warnings", "_set_re_match", @@ -583,6 +584,8 @@ class Kconfig(object): # Build Symbol._dependents for all symbols self._build_dep() + self._loading_config = False + @property def mainmenu_text(self): """ @@ -624,12 +627,30 @@ class Kconfig(object): True if all existing user values should be cleared before loading the .config. """ + # Are we currently loading a .config file? This disables a warning. + self._loading_config = True + + # This stub only exists to make sure _loading_config gets unset + try: + self._load_config(filename, replace) + finally: + self._loading_config = False + + def _load_config(self, filename, replace): with self._open(filename) as f: if replace: - # Invalidates all symbols as a side effect - self.unset_values() - else: - self._invalidate_all() + # If we're replacing the configuration, keep track of which + # symbols and choices got set so that we can unset the rest + # later. This avoids invalidating everything and is a tiny bit + # faster in the test suite. The main benefit though is that + # invalidation must be rock solid for it to work, making it a + # good test. + + for sym in self.defined_syms: + sym._was_set = False + + for choice in self._choices: + choice._was_set = False # Small optimizations set_re_match = self._set_re_match @@ -715,7 +736,7 @@ class Kconfig(object): # Done parsing the assignment. Set the value. - if sym.user_value is not None: + if sym._was_set: # Use strings for tristate values in the warning if sym.orig_type in (BOOL, TRISTATE): display_val = TRI_TO_STR[val] @@ -729,7 +750,19 @@ class Kconfig(object): .format(name, display_user_val, display_val), filename, linenr) - sym._set_value_no_invalidate(val, True) + sym.set_value(val) + + if replace: + # If we're replacing the configuration, unset the symbols that + # didn't get set + + for sym in self.defined_syms: + if not sym._was_set: + sym.unset_value() + + for choice in self._choices: + if not choice._was_set: + choice.unset_value() def write_config(self, filename, header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"): @@ -791,16 +824,11 @@ class Kconfig(object): # set_value() already rejects undefined symbols, and they don't need to # be invalidated (because their value never changes), so we can just # iterate over defined symbols - for sym in self.defined_syms: - # We're iterating over all (defined) symbols, so no need for - # symbols to invalidate their dependent symbols - sym.user_value = None - sym._invalidate() + sym.unset_value() for choice in self._choices: - choice.user_value = choice.user_selection = None - choice._invalidate() + choice.unset_value() def enable_warnings(self): """ @@ -2187,6 +2215,7 @@ class Symbol(object): "_cached_tri_val", "_cached_vis", "_dependents", + "_was_set", "_write_to_conf", "choice", "defaults", @@ -2482,21 +2511,69 @@ class Symbol(object): BOOL) are ignored and won't be stored in Symbol.user_str/tri_value. Kconfiglib will print a warning by default for invalid assignments. """ - self._set_value_no_invalidate(value, False) + if value == self.user_value: + # We know the value must be valid if it was successfully set + # previously + self._was_set = True + return - if self is self.kconfig.modules: - # Changing MODULES has wide-ranging effects - self.kconfig._invalidate_all() + # Check if the value is valid for our type + if not ((self.orig_type == BOOL and value in (0, 2) ) or + (self.orig_type == TRISTATE and value in (0, 1, 2) ) or + (self.orig_type == STRING and isinstance(value, str)) or + (self.orig_type == INT and isinstance(value, str) + and _is_base_n(value, 10) ) or + (self.orig_type == HEX and isinstance(value, str) + and _is_base_n(value, 16))): + + warning = "the value '{}' is invalid for {}, which has type {}" \ + .format(value, self.name, TYPE_TO_STR[self.orig_type]) + + if self.orig_type in (BOOL, TRISTATE) and \ + value in ("n", "m", "y"): + warning += ' (pass 0, 1, 2 for n, m, y, respectively)' + + self.kconfig._warn(warning) + + return + + if not self.nodes: + self.kconfig._warn_undef_assign( + "{} is constant or undefined. '{}' assignment ignored." + .format(self.name, value)) + return + + # Assignments to promptless symbols are expected when loading a .config + if not self.kconfig._loading_config: + for node in self.nodes: + if node.prompt is not None: + break + else: + self.kconfig._warn("{} has no prompt. '{}' assignment ignored." + .format(self.name, value)) + return + + if self.choice is not None and value == 2: + # Remember this as a choice selection only. Makes switching back + # and forth between choice modes work as expected, and makes the + # check for whether the user value is the same as before above + # safe. + self.choice.user_selection = self + self.choice._rec_invalidate() + self.choice._was_set = True else: + self.user_value = value self._rec_invalidate() + self._was_set = True def unset_value(self): """ Resets the user value of the symbol, as if the symbol had never gotten a user value via Kconfig.load_config() or Symbol.set_value(). """ - self.user_value = None - self._rec_invalidate() + if self.user_value is not None: + self.user_value = None + self._rec_invalidate() def __repr__(self): """ @@ -2618,6 +2695,8 @@ class Symbol(object): self.env_var = None self.is_allnoconfig_y = False + self._was_set = False + # Should the symbol get an entry in .config? Calculated along with the # value. self._write_to_conf = False @@ -2665,55 +2744,6 @@ class Symbol(object): return (1,) - def _set_value_no_invalidate(self, value, suppress_prompt_warning): - """ - Like set_value(), but does not invalidate any symbols. - - suppress_prompt_warning: - The warning about assigning a value to a promptless symbol gets - spammy for Linux defconfigs, so turn it off when loading .configs. - It's still helpful when manually invoking set_value(). - """ - # Check if the value is valid for our type - if not ((self.orig_type == BOOL and value in (0, 2) ) or - (self.orig_type == TRISTATE and value in (0, 1, 2) ) or - (self.orig_type == STRING and isinstance(value, str)) or - (self.orig_type == INT and isinstance(value, str) - and _is_base_n(value, 10) ) or - (self.orig_type == HEX and isinstance(value, str) - and _is_base_n(value, 16))): - - warning = "the value '{}' is invalid for {}, which has type {}" \ - .format(value, self.name, TYPE_TO_STR[self.orig_type]) - - if self.orig_type in (BOOL, TRISTATE) and \ - value in ("n", "m", "y"): - warning += ' (pass 0, 1, 2 for n, m, y, respectively)' - - self.kconfig._warn(warning) - - return - - if not self.nodes: - self.kconfig._warn_undef_assign( - 'assigning the value "{}" to the undefined symbol {} will ' - "have no effect".format(value, self.name)) - - if not suppress_prompt_warning: - for node in self.nodes: - if node.prompt is not None: - break - else: - self.kconfig._warn('assigning the value "{}" to the ' - "promptless symbol {} will have no effect" - .format(value, self.name)) - - self.user_value = value - - if self.choice is not None and value == 2: - self.choice.user_selection = self - self.choice._rec_invalidate() - def _invalidate(self): """ Marks the symbol as needing to be recalculated. @@ -2725,10 +2755,10 @@ class Symbol(object): """ Invalidates the symbol and all items that (possibly) depend on it. """ - # Constant symbols must never be invalidated, because they lose their - # value. They never appear as dependencies, but can still be manually - # assigned a user value (and that's OK, though pointless). - if not self.is_constant: + if self is self.kconfig.modules: + # Invalidating MODULES has wide-ranging effects + self.kconfig._invalidate_all() + else: self._invalidate() for item in self._dependents: @@ -2892,6 +2922,7 @@ class Choice(object): "_cached_selection", "_cached_vis", "_dependents", + "_was_set", "defaults", "is_constant", "is_optional", @@ -3015,6 +3046,12 @@ class Choice(object): attribute (is_optional) can never be in n mode, but 0 is still accepted (and ignored) since it's not a malformed value. """ + if value == self.user_value: + # We know the value must be valid if it was successfully set + # previously + self._was_set = True + return + if not ((self.orig_type == BOOL and value in (0, 2) ) or (self.orig_type == TRISTATE and value in (0, 1, 2))): self.kconfig._warn("the value '{}' is invalid for the choice, " @@ -3024,14 +3061,16 @@ class Choice(object): self.user_value = value self._rec_invalidate() + self._was_set = True def unset_value(self): """ Resets the user value (mode) and user selection of the Choice, as if the user had never touched the mode or any of the choice symbols. """ - self.user_value = self.user_selection = None - self._rec_invalidate() + if self.user_value is not None or self.user_selection is not None: + self.user_value = self.user_selection = None + self._rec_invalidate() def __repr__(self): """ diff --git a/tests/Kmisc b/tests/Kmisc index c3a21f8..cea3ca9 100644 --- a/tests/Kmisc +++ b/tests/Kmisc @@ -39,13 +39,13 @@ config BOOL bool "bool" if NOT_DEFINED_1 config TRISTATE - tristate # Visibility should not affect user value + tristate "A" config STRING string "string" config INT - int # Visibility should not affect user value + int "INT" config HEX hex "hex" |
