def register_change(self, key, left_value, right_value, change_type): """Assuming that left_value is the base state and the right value is the value to be set, we assure that - Modifications + Modified values will be passed on to the resolver, which will merge them or pick a side - Deletions + right values which are missing will be missing in the value we produce - Additions + right values which are where added will be added to the data structure unchecked - Unchanged + Unchanged values will be taken right away. We will copy right values to assure they are independent. """ value_to_set = NoValue if change_type is self.added: value_to_set = self._handle_added(key, left_value, right_value) elif change_type is self.modified: value_to_set = self._resolve_conflict(key, left_value, right_value) elif change_type is self.unchanged: value_to_set = self._handle_unchanged(key, right_value) elif change_type is self.deleted: # If there was no change made to our current merge_value (tree) yet, we might have it still # at the initial value, NoValue. # However, the _handle_deleted method requires parent_tree to be a tree-like objects, and asserts for it # We assure this now parent_tree = self._merged_value if parent_tree is NoValue: parent_tree = self.DictType() # handle possiblity of having NoValue as merged_value self._handle_deleted(key, parent_tree, left_value) #end handle change type if value_to_set is not NoValue: self._set_merged_value(key, smart_deepcopy(value_to_set))
def register_change(self, key, left_value, right_value, change_type): """Pick either a default value (right-hand side value) or the stored value (left-hand side) @note handles AnyKey""" if isinstance(key, type) and issubclass(key, AnyKey): # AnyKeys and their values are ignored if we get to the point where were are supposed to merge # them. This happens if it is indirectly removed or added, when whole branches are missing # in our data. # In that case, we just don't want to see it, the one who usees AnyKey needs to handle no keys # underneath AnyKeys parent tree anyway. # Change instance attribute, now AnyKey will map to an empty dict, which needs to be kept # to help clients to deal with it nicely self.delete_empty_trees = False return #end ignore everything underneath AnyKeys actual_value = NoValue qkey = self._qualified_key(self._to_string_key(key)) # assure we have an instance for our assignments right_value_inst = right_value if isinstance(right_value, type) and right_value not in (TreeItem, NoValue): right_value_inst = right_value() # assure we have an instance for our assignments if change_type is self.added: actual_value = right_value_inst elif change_type is self.deleted: if self.keep_values_not_in_schema: # msg_prefix = 'Using' actual_value = left_value else: # msg_prefix = 'Dropped' actual_value = NoValue # end handle schema elif change_type is self.modified: try: if left_value is NoValue: raise TypeError("NoValue is not a valid value") #end handle missing keys in storage # In case of a type conversion, we must assure the value is already # 'final' and substituted. Otherwise the type conversion can fail prematurely. if right_value is None: actual_value = left_value elif left_value is None: actual_value = right_value_inst else: if isinstance(right_value_inst, list) and not isinstance(left_value, list): if self.should_resolve_values(): # have to assure we copy the nested value, otherwise the resolved value # shows up in our source date-structure. We do this here, just to prevent # unnecessary work in the resolver right_value_inst = self._resolve_value(key, smart_deepcopy(right_value_inst)) actual_value = type(right_value_inst)() actual_value.append(left_value) elif isinstance(right_value, type): # handle types if self.should_resolve_values(): left_value = self._resolve_value(key, smart_deepcopy(left_value)) actual_value = right_value(left_value) else: # handle instances - only convert the type if this is necesary. # Not all types are primitive, and invoking their constructor might just not work if isinstance(left_value, type(right_value)): actual_value = left_value else: if self.should_resolve_values(): left_value = self._resolve_value(key, left_value) actual_value = type(right_value)(left_value) # end handle list packing # handle value type - special handling for None except Exception, err: # Assure we have a real value for error printing and value handling actual_value = right_value_inst if self.should_resolve_values(): msg = "Could not convert value type %s of value '%s' at key '%s' " msg += "to the desired type %s one of the default value with error: %s" msg += ", using default value instead" self._log.warn(msg, type(left_value), str(left_value), qkey, type(right_value_inst), str(err))
if self.should_resolve_values(): msg = "Could not convert value type %s of value '%s' at key '%s' " msg += "to the desired type %s one of the default value with error: %s" msg += ", using default value instead" self._log.warn(msg, type(left_value), str(left_value), qkey, type(right_value_inst), str(err)) # end handle logging #end handle type conversion else: # both values are equal - just use it assert change_type is self.unchanged actual_value = left_value #end handle change type # always perform a smart-copy here, value could be a nested list with mutable values if actual_value is not NoValue: self._set_merged_value(key, smart_deepcopy(actual_value)) def _resolve_scalar_value(self, key, value): """@return a resolved single scalar string value""" # Actually, all of the values we see should be strings # however, the caller is and may be 'stupid', so we handle it here if not isinstance(value, basestring): return value # end ignore non-string types formatter = self.StringFormatterType() try: last_value = '' count = 0 while last_value != value: count += 1