From 01385447262362241e0881e6d296291a4a91f4c8 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Fri, 19 Aug 2016 17:41:35 +0200 Subject: [PATCH] Prepare for multiple result formats, move text result to separate object --- .gitignore | 2 ++ deepdiff/diff.py | 50 ++++++++++++++++++--------------------------- deepdiff/helper.py | 24 ++++++++++++++++++++++ refdesign.odt | Bin 0 -> 20450 bytes 4 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 refdesign.odt diff --git a/.gitignore b/.gitignore index d8731f78..bed0daa2 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,5 @@ target/ # Editor / IDE files *.swp .idea/ +.~lock* + diff --git a/deepdiff/diff.py b/deepdiff/diff.py index ef42e662..e5cb836c 100644 --- a/deepdiff/diff.py +++ b/deepdiff/diff.py @@ -8,7 +8,7 @@ from deepdiff.contenthash import DeepHash -class DeepDiff(RemapDict): +class DeepDiff(ResultDict): r""" **DeepDiff** @@ -287,34 +287,24 @@ def __init__(self, t1, t2, raise ValueError(("The following parameter(s) are not valid: %s\n" "The valid parameters are ignore_order, report_repetition, significant_digits," "exclude_paths, exclude_types and verbose_level.") % ', '.join(kwargs.keys())) + self.ignore_order = ignore_order self.report_repetition = report_repetition self.exclude_paths = set(exclude_paths) self.exclude_types = set(exclude_types) self.exclude_types_tuple = tuple(exclude_types) # we need tuple for checking isinstance - self.verbose_level = verbose_level self.hashes = {} if significant_digits is not None and significant_digits < 0: raise ValueError("significant_digits must be None or a non-negative integer") self.significant_digits = significant_digits - self.update({"type_changes": {}, "dictionary_item_added": self.__set_or_dict(), - "dictionary_item_removed": self.__set_or_dict(), - "values_changed": {}, "unprocessed": [], "iterable_item_added": {}, "iterable_item_removed": {}, - "attribute_added": self.__set_or_dict(), "attribute_removed": self.__set_or_dict(), - "set_item_removed": set([]), - "set_item_added": set([]), "repetition_change": {}}) + self.result_text = ResultDict(verbose_level) self.__diff(t1, t2, parents_ids=frozenset({id(t1)})) - empty_keys = [k for k, v in getattr(self, items)() if not v] - - for k in empty_keys: - del self[k] - - def __set_or_dict(self): - return {} if self.verbose_level >= 2 else set() + self.result_text.cleanup() # clean up text-style result dictionary + self.update(self.result_text) # use text style result as default view def __extend_result_list(self, keys, parent, report_obj, print_as_attribute=False, obj=None): """ @@ -345,13 +335,13 @@ def __extend_result_list(self, keys, parent, report_obj, print_as_attribute=Fals report_obj.add(key_in_report) def __unprocessed(self, parent, t1, t2): - self['unprocessed'].append("%s: %s and %s" % (parent, t1, t2)) + self.result_text['unprocessed'].append("%s: %s and %s" % (parent, t1, t2)) def __values_changed(self, parent, t1, t2, diff=None): if diff is not None: - self["values_changed"][parent] = RemapDict(old_value=t1, new_value=t2, diff=diff) + self.result_text["values_changed"][parent] = RemapDict(old_value=t1, new_value=t2, diff=diff) else: - self["values_changed"][parent] = RemapDict(old_value=t1, new_value=t2) + self.result_text["values_changed"][parent] = RemapDict(old_value=t1, new_value=t2) @staticmethod def __add_to_frozen_set(parents_ids, item_id): @@ -413,11 +403,11 @@ def __diff_dict(self, t1, t2, parent, parents_ids=frozenset({}), print_as_attrib if t_keys_added: self.__extend_result_list(keys=t_keys_added, parent=parent, - report_obj=self[item_added_key], print_as_attribute=print_as_attribute, obj=t2) + report_obj=self.result_text[item_added_key], print_as_attribute=print_as_attribute, obj=t2) if t_keys_removed: self.__extend_result_list(keys=t_keys_removed, parent=parent, - report_obj=self[item_removed_key], print_as_attribute=print_as_attribute, obj=t1) + report_obj=self.result_text[item_removed_key], print_as_attribute=print_as_attribute, obj=t1) self.__diff_common_children( t1, t2, t_keys_intersect, print_as_attribute, parents_ids, parent, parent_text) @@ -459,11 +449,11 @@ def __diff_set(self, t1, t2, parent="root"): if items_removed: self.__extend_result_list( - keys=items_removed, parent=parent, report_obj=self["set_item_removed"]) + keys=items_removed, parent=parent, report_obj=self.result_text["set_item_removed"]) if items_added: self.__extend_result_list( - keys=items_added, parent=parent, report_obj=self["set_item_added"]) + keys=items_added, parent=parent, report_obj=self.result_text["set_item_added"]) def __diff_iterable(self, t1, t2, parent="root", parents_ids=frozenset({})): """Difference of iterables except dictionaries, sets and strings.""" @@ -493,9 +483,9 @@ def __diff_iterable_subscriptable(self, t1, t2, parent="root", parents_ids=froze self.__diff(x, y, "%s[%s]" % (parent, i), parents_ids_added) if len(indices_added): - self.__extend_result_list(indices_added, parent, self["iterable_item_added"], obj=t2) + self.__extend_result_list(indices_added, parent, self.result_text["iterable_item_added"], obj=t2) if len(indices_removed): - self.__extend_result_list(indices_removed, parent, self["iterable_item_removed"], obj=t1) + self.__extend_result_list(indices_removed, parent, self.result_text["iterable_item_removed"], obj=t1) def __diff_str(self, t1, t2, parent): @@ -581,7 +571,7 @@ def __diff_iterable_with_contenthash(self, t1, t2, parent): new_indexes=t2_indexes, value=t1_item_and_index.item )} - self['repetition_change'].update(repetition_change) + self.result_text['repetition_change'].update(repetition_change) else: items_added = {"%s[%s]" % (parent, t2_hashtable[hash_value].indexes[0]): t2_hashtable[ @@ -590,8 +580,8 @@ def __diff_iterable_with_contenthash(self, t1, t2, parent): items_removed = {"%s[%s]" % (parent, t1_hashtable[hash_value].indexes[0]): t1_hashtable[ hash_value].item for hash_value in hashes_removed} - self["iterable_item_removed"].update(items_removed) - self["iterable_item_added"].update(items_added) + self.result_text["iterable_item_removed"].update(items_removed) + self.result_text["iterable_item_added"].update(items_added) def __diff_numbers(self, t1, t2, parent): """Diff Numbers""" @@ -616,9 +606,9 @@ def __diff_numbers(self, t1, t2, parent): def __diff_types(self, t1, t2, parent): """Diff types""" - self["type_changes"][parent] = RemapDict(old_type=type(t1), new_type=type(t2)) - if self.verbose_level: - self["type_changes"][parent].update(old_value=t1, new_value=t2) + self.result_text["type_changes"][parent] = RemapDict(old_type=type(t1), new_type=type(t2)) + if self.result_text.verbose_level: + self.result_text["type_changes"][parent].update(old_value=t1, new_value=t2) def __diff(self, t1, t2, parent="root", parents_ids=frozenset({})): """The main diff method""" diff --git a/deepdiff/helper.py b/deepdiff/helper.py index 084864ed..0a677701 100644 --- a/deepdiff/helper.py +++ b/deepdiff/helper.py @@ -114,3 +114,27 @@ def __getitem__(self, old_key): return self.get(new_key) else: # pragma: no cover raise KeyError(new_key) + + +class ResultDict(RemapDict): + def __init__(self, verbose_level): + """ + Initialize a result dict. + """ + self.verbose_level = verbose_level + + self.update({"type_changes": {}, "dictionary_item_added": self.__set_or_dict(), + "dictionary_item_removed": self.__set_or_dict(), + "values_changed": {}, "unprocessed": [], "iterable_item_added": {}, "iterable_item_removed": {}, + "attribute_added": self.__set_or_dict(), "attribute_removed": self.__set_or_dict(), + "set_item_removed": set([]), + "set_item_added": set([]), "repetition_change": {}}) + + def cleanup(self): + empty_keys = [k for k, v in getattr(self, items)() if not v] + + for k in empty_keys: + del self[k] + + def __set_or_dict(self): + return {} if self.verbose_level >= 2 else set() diff --git a/refdesign.odt b/refdesign.odt new file mode 100644 index 0000000000000000000000000000000000000000..7155d65cdebd3cbc7f1f3ad295a863a86d06bf98 GIT binary patch literal 20450 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 L0kHgu@doh#dy3%` literal 0 HcmV?d00001