From 0652d614d20a9617490d038bed454b71e9ac83f8 Mon Sep 17 00:00:00 2001 From: gaoflow Date: Mon, 1 Jun 2026 22:55:29 +0200 Subject: [PATCH] Fix TypeError when a datetime is used as a dict key with key-cleaning flags number_to_string guards on `numbers`, which includes the datetime types (they are orderable). Datetimes therefore passed the guard and reached `round(number, ...)`, which they do not support. Using a datetime/date as a dict key together with a flag that cleans keys (ignore_numeric_type_changes, ignore_string_case, ignore_string_type_changes) raised: TypeError: type datetime.datetime doesn't define __round__ method Guard on `only_numbers` instead so non-round()-able values (datetimes, dates, ...) are returned unchanged, like every other non-numeric value. Adds a regression test. Fixes #550. --- deepdiff/helper.py | 7 ++++++- tests/test_diff_datetime.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/deepdiff/helper.py b/deepdiff/helper.py index 3fc61183..0ce1ed05 100644 --- a/deepdiff/helper.py +++ b/deepdiff/helper.py @@ -475,7 +475,12 @@ 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 + # `numbers` also includes datetime types (they are orderable), but those + # cannot be reduced to significant digits via round(); leave any + # non-numeric value (datetimes, dates, etc.) unchanged. Otherwise using + # e.g. a datetime as a dict key together with ignore_numeric_type_changes + # raised "TypeError: type datetime.datetime doesn't define __round__". 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..105f4abc 100644 --- a/tests/test_diff_datetime.py +++ b/tests/test_diff_datetime.py @@ -123,3 +123,22 @@ 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') + + def test_datetime_dict_key_with_ignore_flags(self): + # Using a datetime/date as a dict key together with flags that clean the + # keys (ignore_numeric_type_changes / ignore_string_case / + # ignore_string_type_changes) used to raise + # "TypeError: type datetime.datetime doesn't define __round__ method" + # because the keys were routed through number_to_string, which only + # handles real numbers. + dt = datetime(2020, 5, 17, 22, 15) + assert not DeepDiff({dt: 10.0}, {dt: 10}, ignore_numeric_type_changes=True) + # date objects (no time component) too + assert not DeepDiff( + {date(2020, 5, 17): 10.0}, + {date(2020, 5, 17): 10}, + ignore_numeric_type_changes=True, + ) + # the other key-cleaning flags must not raise either + assert not DeepDiff({dt: 1}, {dt: 1}, ignore_string_case=True) + assert not DeepDiff({dt: 1}, {dt: 1}, ignore_string_type_changes=True)