diff --git a/deepdiff/diff.py b/deepdiff/diff.py index 01f8b258..9e92168a 100755 --- a/deepdiff/diff.py +++ b/deepdiff/diff.py @@ -775,6 +775,42 @@ def to_delta_dict(self, directed=True): result['ignore_order'] = True return result + def pretty_form(self): + result = [] + keys = sorted(self.tree.keys()) # sorting keys to guarantee constant order in Python<3.7 + for key in keys: + for item_key in self.tree[key]: + result += [pretty_print_diff(item_key)] + + return '\n'.join(result) + + +def pretty_print_diff(diff: DiffLevel): + type_t1 = get_type(diff.t1).__name__ + type_t2 = get_type(diff.t2).__name__ + + val_t1 = '"{}"'.format(str(diff.t1)) if type_t1 == "str" else str(diff.t1) + val_t2 = '"{}"'.format(str(diff.t2)) if type_t2 == "str" else str(diff.t2) + + diff_path = diff.path(root='root') + + texts = { + "type_changes": "Type of {diff_path} changed from {type_t1} to {type_t2} and value changed from {val_t1} to {val_t2}.", + "values_changed": "Value of {diff_path} changed from {val_t1} to {val_t2}.", + "dictionary_item_added": "Item {diff_path} added to dictionary.", + "dictionary_item_removed": "Item {diff_path} removed from dictionary.", + "iterable_item_added": "Item {diff_path} added to iterable.", + "iterable_item_removed": "Item {diff_path} removed from iterable.", + "attribute_added": "Attribute {diff_path} added.", + "attribute_removed": "Attribute {diff_path} removed.", + "set_item_added": "Item root[{val_t2}] added to set.", + "set_item_removed": "Item root[{val_t1}] removed from set.", + "repetition_change": "Repetition change for item {diff_path}.", + } + + return texts.get(diff.report_type, "").format(diff_path=diff_path, type_t1=type_t1, type_t2=type_t2, val_t1=val_t1, + val_t2=val_t2) + if __name__ == "__main__": # pragma: no cover import doctest diff --git a/tests/test_diff_tree.py b/tests/test_diff_tree.py index 44abd648..4a03dbc9 100644 --- a/tests/test_diff_tree.py +++ b/tests/test_diff_tree.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import pytest from deepdiff import DeepDiff +from deepdiff.diff import pretty_print_diff from deepdiff.helper import pypy3, notpresent from deepdiff.model import DictRelationship, NonSubscriptableIterableRelationship @@ -184,3 +185,128 @@ def test_adding_list_diff(self): ddiff = DeepDiff(t1, t2, view='tree') addition = ddiff + t1 assert addition == t2 + + +class TestDeepDiffPrettyForm: + """Tests for pretty_form() method of DeepDiff""" + + class TestingClass: + one = 1 + + testing_class = TestingClass + + @pytest.mark.parametrize('t1, t2, item_path, old_type, new_type, old_val_displayed, new_val_displayed', + [ + [{2: 2, 4: 4}, {2: 'b', 4: 4}, 'root[2]', 'int', 'str', '2', '"b"'], + [[1, 2, 3], [1, '2', 3], 'root[1]', 'int', 'str', '2', '"2"'], + [[1, 2, 3], {1, 2, 3}, 'root', 'list', 'set', '[1, 2, 3]', '{1, 2, 3}'] + ]) + def test_pretty_print_diff_type_changes(self, t1, t2, item_path, old_type, new_type, old_val_displayed, + new_val_displayed): + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['type_changes'].items[0]) + assert result == 'Type of {} changed from {} to {} and value changed from {} to {}.'.format(item_path, old_type, new_type, old_val_displayed, new_val_displayed) + + @pytest.mark.parametrize('t1, t2, item_path', + [ + [{2: 2, 4: 4}, {2: 2, 4: 4, 5: 5}, 'root[5]'], + ]) + def test_pretty_print_diff_dictionary_item_added(self, t1, t2, item_path): + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['dictionary_item_added'].items[0]) + assert result == 'Item {} added to dictionary.'.format(item_path) + + @pytest.mark.parametrize('t1, t2, item_path', + [ + [{2: 2, 4: 4}, {2: 2}, 'root[4]'], + ]) + def test_pretty_print_diff_dictionary_item_removed(self, t1, t2, item_path): + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['dictionary_item_removed'].items[0]) + assert result == 'Item {} removed from dictionary.'.format(item_path) + + @pytest.mark.parametrize('t1, t2, item_path, old_val_displayed, new_val_displayed', + [ + [{2: 2, 4: 4}, {2: 3, 4: 4}, 'root[2]', '2', '3'], + [['a', 'b', 'c'], ['a', 'b', 'd'], 'root[2]', '"c"', '"d"'] + ]) + def test_pretty_print_diff_values_changed(self, t1, t2, item_path, old_val_displayed, new_val_displayed): + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['values_changed'].items[0]) + assert result == 'Value of {} changed from {} to {}.'.format(item_path, old_val_displayed, new_val_displayed) + + @pytest.mark.parametrize('t1, t2, item_path', + [ + [[1, 2, 3], [1, 2, 3, 4], 'root[3]'], + ]) + def test_pretty_print_diff_iterable_item_added(self, t1, t2, item_path): + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['iterable_item_added'].items[0]) + assert result == 'Item {} added to iterable.'.format(item_path) + + @pytest.mark.parametrize('t1, t2, item_path', + [ + [[1, 2, 3], [1, 2], 'root[2]'], + ]) + def test_pretty_print_diff_iterable_item_removed(self, t1, t2, item_path): + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['iterable_item_removed'].items[0]) + assert result == 'Item {} removed from iterable.'.format(item_path) + + def test_pretty_print_diff_attribute_added(self): + t1 = self.testing_class() + t2 = self.testing_class() + t2.two = 2 + + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['attribute_added'].items[0]) + assert result == 'Attribute root.two added.' + + def test_pretty_print_diff_attribute_removed(self): + t1 = self.testing_class() + t1.two = 2 + t2 = self.testing_class() + + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['attribute_removed'].items[0]) + assert result == 'Attribute root.two removed.' + + @pytest.mark.parametrize('t1, t2, item_path', + [ + [{1, 2}, {1, 2, 3}, 'root[3]'], + ]) + def test_pretty_print_diff_set_item_added(self, t1, t2, item_path): + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['set_item_added'].items[0]) + assert result == 'Item {} added to set.'.format(item_path) + + @pytest.mark.parametrize('t1, t2, item_path', + [ + [{1, 2, 3}, {1, 2}, 'root[3]'], + ]) + def test_pretty_print_diff_set_item_removed(self, t1, t2, item_path): + ddiff = DeepDiff(t1, t2, view='tree') + result = pretty_print_diff(ddiff.tree['set_item_removed'].items[0]) + assert result == 'Item {} removed from set.'.format(item_path) + + @pytest.mark.parametrize('t1, t2, item_path', + [ + [[1, 2, 3, 2], [1, 2, 3], 'root[1]'], + ]) + def test_pretty_print_diff_repetition_change(self, t1, t2, item_path): + ddiff = DeepDiff(t1, t2, view='tree', ignore_order=True, report_repetition=True) + result = pretty_print_diff(ddiff.tree['repetition_change'].items[0]) + assert result == 'Repetition change for item {}.'.format(item_path) + + def test_pretty_form_method(self): + t1 = {2: 2, 3: 3, 4: 4} + t2 = {2: 'b', 4: 5, 5: 5} + ddiff = DeepDiff(t1, t2, view='tree') + result = ddiff.pretty_form() + expected = ( + 'Item root[5] added to dictionary.' + '\nItem root[3] removed from dictionary.' + '\nType of root[2] changed from int to str and value changed from 2 to "b".' + '\nValue of root[4] changed from 4 to 5.' + ) + assert result == expected