From 5f22bd27ad73b62183cc85dd550d669ddb9706e2 Mon Sep 17 00:00:00 2001 From: "Aaron D. Marasco" Date: Wed, 9 Oct 2024 21:40:40 -0400 Subject: [PATCH] Add print() option --- AUTHORS.md | 1 + CHANGELOG.md | 1 + deepdiff/serialization.py | 8 +++++-- docs/view.rst | 23 ++++++++++++++++++++ tests/test_serialization.py | 43 +++++++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 2 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 32ae5fc..79d9edb 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -64,3 +64,4 @@ Authors in order of the timeline of their contributions: - [dtorres-sf](https://github.com/dtorres-sf) for fixing iterable moved items when iterable_compare_func is used. - [Florian Finkernagel](https://github.com/TyberiusPrime) for pandas and polars support. - Mathis Chenuet [artemisart](https://github.com/artemisart) for fixing slots classes comparison. +- [Aaron D. Marasco](https://github.com/AaronDMarasco) added `prefix` option to `pretty()` diff --git a/CHANGELOG.md b/CHANGELOG.md index 95cd2c7..12da1c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - v8-0-1 - Bugfix. Numpy should be optional. + - Added `prefix` option to `pretty()` - v8-0-0 diff --git a/deepdiff/serialization.py b/deepdiff/serialization.py index 5b4075e..e350b3c 100644 --- a/deepdiff/serialization.py +++ b/deepdiff/serialization.py @@ -296,7 +296,7 @@ def _to_delta_dict(self, directed=True, report_repetition_required=True, always_ return deepcopy(dict(result)) - def pretty(self): + def pretty(self, prefix=None): """ The pretty human readable string output for the diff object regardless of what view was used to generate the diff. @@ -310,12 +310,16 @@ def pretty(self): Item root[1] removed from set. """ result = [] + if prefix is None: + prefix = '' keys = sorted(self.tree.keys()) # sorting keys to guarantee constant order across python versions. for key in keys: for item_key in self.tree[key]: result += [pretty_print_diff(item_key)] - return '\n'.join(result) + if callable(prefix): + return "\n".join(f"{prefix(diff=self)}{r}" for r in result) + return "\n".join(f"{prefix}{r}" for r in result) class _RestrictedUnpickler(pickle.Unpickler): diff --git a/docs/view.rst b/docs/view.rst index f50fc9f..6343590 100644 --- a/docs/view.rst +++ b/docs/view.rst @@ -299,6 +299,29 @@ Use the pretty method for human readable output. This is regardless of what view Item root[4] removed from set. Item root[1] removed from set. +The pretty method has an optional parameter ``prefix`` that allows a prefix string before every output line (*e.g.* for logging): + >>> from deepdiff import DeepDiff + >>> t1={1,2,4} + >>> t2={2,3} + >>> print(DeepDiff(t1, t2).pretty(prefix='Diff: ')) + Diff: Item root[3] added to set. + Diff: Item root[4] removed from set. + Diff: Item root[1] removed from set. + +The ``prefix`` may also be a callable function. This function must accept ``**kwargs``; as of this version, the only parameter is ``diff`` but the signature allows for future expansion. +The ``diff`` given will be the ``DeepDiff`` that ``pretty`` was called on; this allows interesting capabilities such as: + >>> from deepdiff import DeepDiff + >>> t1={1,2,4} + >>> t2={2,3} + >>> def callback(**kwargs): + ... """Helper function using a hidden variable on the diff that tracks which count prints next""" + ... kwargs['diff']._diff_count = 1 + getattr(kwargs['diff'], '_diff_count', 0) + ... return f"Diff #{kwargs['diff']._diff_count}: " + ... + >>> print(DeepDiff(t1, t2).pretty(prefix=callback)) + Diff #1: Item root[3] added to set. + Diff #2: Item root[4] removed from set. + Diff #3: Item root[1] removed from set. Text view vs. Tree view vs. vs. pretty() method diff --git a/tests/test_serialization.py b/tests/test_serialization.py index facda24..d578e53 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -330,6 +330,49 @@ def test_pretty_form_method(self, expected, verbose_level): result = ddiff.pretty() assert result == expected + @pytest.mark.parametrize("expected, verbose_level", + ( + ('\t\tItem root[5] added to dictionary.' + '\n\t\tItem root[3] removed from dictionary.' + '\n\t\tType of root[2] changed from int to str and value changed from 2 to "b".' + '\n\t\tValue of root[4] changed from 4 to 5.', 0), + ('\t\tItem root[5] (5) added to dictionary.' + '\n\t\tItem root[3] (3) removed from dictionary.' + '\n\t\tType of root[2] changed from int to str and value changed from 2 to "b".' + '\n\t\tValue of root[4] changed from 4 to 5.', 2), + ), ids=("verbose=0", "verbose=2") + ) + def test_pretty_form_method_prefixed_simple(self, expected, verbose_level): + t1 = {2: 2, 3: 3, 4: 4} + t2 = {2: 'b', 4: 5, 5: 5} + ddiff = DeepDiff(t1, t2, verbose_level=verbose_level) + result = ddiff.pretty(prefix="\t\t") + assert result == expected + + @pytest.mark.parametrize("expected, verbose_level", + ( + ('Diff #1: Item root[5] added to dictionary.' + '\nDiff #2: Item root[3] removed from dictionary.' + '\nDiff #3: Type of root[2] changed from int to str and value changed from 2 to "b".' + '\nDiff #4: Value of root[4] changed from 4 to 5.', 0), + ('Diff #1: Item root[5] (5) added to dictionary.' + '\nDiff #2: Item root[3] (3) removed from dictionary.' + '\nDiff #3: Type of root[2] changed from int to str and value changed from 2 to "b".' + '\nDiff #4: Value of root[4] changed from 4 to 5.', 2), + ), ids=("verbose=0", "verbose=2") + ) + def test_pretty_form_method_prefixed_callback(self, expected, verbose_level): + def prefix_callback(**kwargs): + """Helper function using a hidden variable on the diff that tracks which count prints next""" + kwargs['diff']._diff_count = 1 + getattr(kwargs['diff'], '_diff_count', 0) + return f"Diff #{kwargs['diff']._diff_count}: " + + t1 = {2: 2, 3: 3, 4: 4} + t2 = {2: 'b', 4: 5, 5: 5} + ddiff = DeepDiff(t1, t2, verbose_level=verbose_level) + result = ddiff.pretty(prefix=prefix_callback) + assert result == expected + @pytest.mark.parametrize('test_num, value, func_to_convert_back', [ (1, {'10': None}, None), (2, {"type_changes": {"root": {"old_type": None, "new_type": list, "new_value": ["你好", 2, 3, 5]}}}, None),