diff --git a/deepdiff/helper.py b/deepdiff/helper.py index 5143a3b7..499de538 100644 --- a/deepdiff/helper.py +++ b/deepdiff/helper.py @@ -475,7 +475,7 @@ def number_to_string(number: Any, significant_digits: int, number_format_notatio except KeyError: raise ValueError("number_format_notation got invalid value of {}. The valid values are 'f' and 'e'".format(number_format_notation)) from None - if not isinstance(number, numbers): # type: ignore + if not isinstance(number, only_numbers): # type: ignore return number elif isinstance(number, Decimal): with localcontext() as ctx: diff --git a/tests/test_diff_datetime.py b/tests/test_diff_datetime.py index c3905291..3ed8b3df 100644 --- a/tests/test_diff_datetime.py +++ b/tests/test_diff_datetime.py @@ -1,5 +1,7 @@ +import numpy as np +import pytest import pytz -from datetime import date, datetime, time, timezone +from datetime import date, datetime, time, timedelta, timezone from deepdiff import DeepDiff @@ -123,3 +125,55 @@ def test_datetime_within_array_with_timezone_diff(self): assert not DeepDiff(d1, d2) assert not DeepDiff(d1, d2, ignore_order=True) assert not DeepDiff(d1, d2, truncate_datetime='second') + + +class TestTemporalDictionaryKeys: + def test_ignore_numeric_types_with_datetime_key(self): + key = datetime(2020, 5, 17, 22, 15) + assert DeepDiff({key: 10.0}, {key: 10}, ignore_numeric_type_changes=True) == {} + + @pytest.mark.parametrize("key", [ + datetime(2020, 5, 17, 22, 15), + datetime(2020, 5, 17, 22, 15, tzinfo=timezone.utc), + date(2020, 5, 17), + time(22, 15), + timedelta(days=2, seconds=3), + np.datetime64("2020-05-17T22:15"), + ]) + @pytest.mark.parametrize("options", [ + {"ignore_numeric_type_changes": True}, + {"ignore_string_case": True}, + {"ignore_string_type_changes": True}, + {"ignore_string_case": True, "significant_digits": 3}, + {"ignore_string_type_changes": True, "significant_digits": 3}, + ]) + def test_temporal_keys_with_key_cleaning(self, key, options): + assert DeepDiff({key: "value"}, {key: "value"}, **options) == {} + + @pytest.mark.parametrize("first, second, paths", [ + (datetime(2020, 5, 17, 22, 15), datetime(2020, 5, 17, 22, 16), + ("number:2020-05-17 22:15:00", "number:2020-05-17 22:16:00")), + (date(2020, 5, 17), date(2020, 5, 18), + ("number:2020-05-17", "number:2020-05-18")), + (time(22, 15), time(22, 16), ("number:22:15:00", "number:22:16:00")), + (timedelta(seconds=1), timedelta(seconds=2), ("number:0:00:01", "number:0:00:02")), + (np.datetime64("2020-05-17"), np.datetime64("2020-05-18"), + ("number:2020-05-17", "number:2020-05-18")), + ]) + def test_distinct_temporal_keys_keep_their_values(self, first, second, paths): + diff = DeepDiff( + {first: "old-first", second: "old-second"}, + {first: "new-first", second: "new-second"}, + ignore_numeric_type_changes=True, + view="tree", + ) + assert set(diff) == {"values_changed"} + changes = diff["values_changed"] + assert len(changes) == 2 + assert { + change.path(output_format="list")[0]: (change.t1, change.t2) + for change in changes + } == { + paths[0]: ("old-first", "new-first"), + paths[1]: ("old-second", "new-second"), + } diff --git a/tests/test_helper.py b/tests/test_helper.py index a42407bd..c76ad735 100644 --- a/tests/test_helper.py +++ b/tests/test_helper.py @@ -4,6 +4,7 @@ import numpy as np from enum import Enum from decimal import Decimal +from fractions import Fraction from deepdiff.helper import ( short_repr, number_to_string, get_numpy_ndarray_rows, cartesian_product_of_shape, literal_eval_extended, @@ -197,6 +198,29 @@ def test_number_to_string_complex_digits(self, t1, t2, significant_digits, numbe assert st1 == expected_result[0] assert st2 == expected_result[1] + @pytest.mark.parametrize("value", [ + datetime.datetime(2020, 5, 17, 22, 15), + datetime.date(2020, 5, 17), + datetime.time(22, 15), + datetime.timedelta(days=2, seconds=3), + np.datetime64("2020-05-17T22:15"), + ]) + @pytest.mark.parametrize("notation", ["f", "e"]) + def test_number_to_string_leaves_temporal_values_unchanged(self, value, notation): + assert number_to_string(value, 3, number_format_notation=notation) is value + + @pytest.mark.parametrize("value, expected", [ + (10, "10.00"), + (1.234, "1.23"), + (Decimal("1.234"), "1.23"), + (Fraction(1, 8), "0.12"), + (complex(1, -2), "1.00-2.00j"), + (np.int64(10), "10.00"), + (np.float64(1.234), "1.23"), + ]) + def test_number_to_string_numeric_types_keep_formatting(self, value, expected): + assert number_to_string(value, 2) == expected + def test_number_to_string_with_invalid_notation(self): with pytest.raises(ValueError): number_to_string(10, significant_digits=4, number_format_notation='blah')