def __diff_numbers(self, level): """Diff Numbers""" t1_type = "number" if self.ignore_numeric_type_changes else level.t1.__class__.__name__ t2_type = "number" if self.ignore_numeric_type_changes else level.t2.__class__.__name__ if self.significant_digits is None: if level.t1 != level.t2: self.__report_result('values_changed', level) else: # Bernhard10: I use string formatting for comparison, to be consistent with usecases where # data is read from files that were previousely written from python and # to be consistent with on-screen representation of numbers. # Other options would be abs(t1-t2)<10**-self.significant_digits # or math.is_close (python3.5+) # Note that abs(3.25-3.251) = 0.0009999999999998899 < 0.001 # Note also that "{:.3f}".format(1.1135) = 1.113, but "{:.3f}".format(1.11351) = 1.114 # For Decimals, format seems to round 2.5 to 2 and 3.5 to 4 (to closest even number) t1_s = self.number_to_string(level.t1, significant_digits=self.significant_digits, number_format_notation=self.number_format_notation) t2_s = self.number_to_string(level.t2, significant_digits=self.significant_digits, number_format_notation=self.number_format_notation) t1_s = KEY_TO_VAL_STR.format(t1_type, t1_s) t2_s = KEY_TO_VAL_STR.format(t2_type, t2_s) if t1_s != t2_s: self.__report_result('values_changed', level)
def _prep_iterable(self, obj, parent, parents_ids=EMPTY_FROZENSET): result = defaultdict(int) for i, item in enumerate(obj): if self._skip_this(item, parent="{}[{}]".format(parent, i)): continue item_id = get_id(item) if parents_ids and item_id in parents_ids: continue parents_ids_added = add_to_frozen_set(parents_ids, item_id) hashed = self._hash(item, parent=parent, parents_ids=parents_ids_added) # counting repetitions result[hashed] += 1 if self.ignore_repetition: result = list(result.keys()) else: result = [ '{}|{}'.format(i, v) for i, v in result.items() ] result = sorted(map(str, result)) # making sure the result items are string and sorted so join command works. result = ','.join(result) result = KEY_TO_VAL_STR.format(type(obj).__name__, result) return result
def _prep_dict(self, obj, parent, parents_ids=EMPTY_FROZENSET, print_as_attribute=False, original_type=None): result = [] key_text = "%s{}".format(INDEX_VS_ATTRIBUTE[print_as_attribute]) for key, item in obj.items(): key_formatted = "'%s'" % key if not print_as_attribute and isinstance(key, strings) else key key_in_report = key_text % (parent, key_formatted) key_hash = self._hash(key, parent=key_in_report, parents_ids=parents_ids) item_id = get_id(item) if (parents_ids and item_id in parents_ids) or self._skip_this(item, parent=key_in_report): continue parents_ids_added = add_to_frozen_set(parents_ids, item_id) hashed = self._hash(item, parent=key_in_report, parents_ids=parents_ids_added) hashed = KEY_TO_VAL_STR.format(key_hash, hashed) result.append(hashed) result.sort() result = ';'.join(result) if print_as_attribute: type_ = original_type or type(obj) type_str = type_.__name__ for type_group in self.ignore_type_in_groups: if self.type_check_func(type_, type_group): type_str = ','.join(map(lambda x: x.__name__, type_group)) break else: type_str = 'dict' return "%s:{%s}" % (type_str, result)
def prepare_string_for_hashing(obj, ignore_string_type_changes=False, ignore_string_case=False): """ Clean type conversions """ original_type = obj.__class__.__name__ if isinstance(obj, bytes): obj = obj.decode('utf-8') if not ignore_string_type_changes: obj = KEY_TO_VAL_STR.format(original_type, obj) if ignore_string_case: obj = obj.lower() return obj
def __get_clean_to_keys_mapping(self, keys, level): result = {} for key in keys: if self.ignore_string_type_changes and isinstance(key, bytes): clean_key = key.decode('utf-8') elif isinstance(key, numbers): type_ = "number" if self.ignore_numeric_type_changes else key.__class__.__name__ clean_key = self.number_to_string(key, significant_digits=self.significant_digits, number_format_notation=self.number_format_notation) clean_key = KEY_TO_VAL_STR.format(type_, clean_key) else: clean_key = key if clean_key in result: logger.warning(('{} and {} in {} become the same key when ignore_numeric_type_changes' 'or ignore_numeric_type_changes are set to be true.').format( key, result[clean_key], level.path())) else: result[clean_key] = key return result
def __get_clean_to_keys_mapping(self, keys, level): result = {} for key in keys: if self.ignore_string_type_changes and isinstance(key, bytes): clean_key = key.decode('utf-8') elif isinstance(key, numbers): type_ = "number" if self.ignore_numeric_type_changes else key.__class__.__name__ clean_key = self.number_to_string( key, significant_digits=self.significant_digits, number_format_notation=self.number_format_notation) clean_key = KEY_TO_VAL_STR.format(type_, clean_key) else: clean_key = key if clean_key in result: logger.warning(( '{} and {} in {} become the same key when ignore_numeric_type_changes' 'or ignore_numeric_type_changes are set to be true.' ).format(key, result[clean_key], level.path())) else: result[clean_key] = key return result
def _prep_dict(self, obj, parent, parents_ids=EMPTY_FROZENSET, print_as_attribute=False, original_type=None): result = [] counts = 1 key_text = "%s{}".format(INDEX_VS_ATTRIBUTE[print_as_attribute]) for key, item in obj.items(): counts += 1 # ignore private variables if self.ignore_private_variables and isinstance(key, str) and key.startswith('__'): continue key_formatted = "'%s'" % key if not print_as_attribute and isinstance(key, strings) else key key_in_report = key_text % (parent, key_formatted) key_hash, _ = self._hash(key, parent=key_in_report, parents_ids=parents_ids) if not key_hash: continue item_id = get_id(item) if (parents_ids and item_id in parents_ids) or self._skip_this(item, parent=key_in_report): continue parents_ids_added = add_to_frozen_set(parents_ids, item_id) hashed, count = self._hash(item, parent=key_in_report, parents_ids=parents_ids_added) hashed = KEY_TO_VAL_STR.format(key_hash, hashed) result.append(hashed) counts += count result.sort() result = ';'.join(result) if print_as_attribute: type_ = original_type or type(obj) type_str = type_.__name__ for type_group in self.ignore_type_in_groups: if self.type_check_func(type_, type_group): type_str = ','.join(map(lambda x: x.__name__, type_group)) break else: type_str = 'dict' return "{}:{{{}}}".format(type_str, result), counts
def _prep_number(self, obj): type_ = "number" if self.ignore_numeric_type_changes else obj.__class__.__name__ if self.significant_digits is not None: obj = self.number_to_string(obj, significant_digits=self.significant_digits, number_format_notation=self.number_format_notation) return KEY_TO_VAL_STR.format(type_, obj)
def _prep_datetime(self, obj): type_ = 'datetime' obj = datetime_normalize(self.truncate_datetime, obj) return KEY_TO_VAL_STR.format(type_, obj)
def _prep_number(self, obj): type_ = "number" if self.ignore_numeric_type_changes else obj.__class__.__name__ if self.significant_digits is not None: obj = self.number_to_string(obj, significant_digits=self.significant_digits, number_format_notation=self.number_format_notation) return KEY_TO_VAL_STR.format(type_, obj)