Repository navigation
Conversation
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #635 +/- ##
==========================================
+ Coverage 95.07% 95.11% +0.04%
==========================================
Files 18 18
Lines 4918 4918
==========================================
+ Hits 4676 4678 +2
+ Misses 242 240 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
seperman
left a comment
There was a problem hiding this comment.
Static review: #635 Keep temporal dictionary keys out of numeric formatting
Summary: Changes the type guard in number_to_string from numbers (which includes datetimes) to only_numbers, so datetime/date/time/timedelta/np.datetime64 values are returned unchanged instead of reaching round() and raising TypeError. This fixes the reproduction in #550 (a datetime dict key combined with ignore_numeric_type_changes, or with ignore_string_case/ignore_string_type_changes plus significant_digits).
Overlap with other PRs
- The
helper.pychange is the same one-line change proposed in #599 and #624. Both were closed by their authors and neither was merged. #615 proposed an equivalent early return fordatetimes; its author also closed it. This PR is the only open fix for #550, and #550 is still open. - It doesn't overlap with #597 (merged on
dev) or #617 (open). Those changedatetime_normalize(truncate_datetime), which is a different code path. The only contact is textual: #617 and #641 also append totests/test_diff_datetime.pyand edit its import line, so whichever merges second will need a small rebase.
Security
No security concerns found in static review. The change is one isinstance guard plus tests. There are no new imports in library code and no I/O, network or subprocess use, the CI/build/dependency files are untouched, and the diff has no non-ASCII or hidden characters.
Correctness
-
deepdiff/helper.py:478:only_numbersdiffers fromnumbersonly by thedatetimestuple (helper.py:191-196), andnp.datetime64is not innumpy_numbers. So only temporal types (and their subclasses) change behaviour. Before this change each of them fell through toround(number, ndigits)and raised, so I don't see a regression for any input that worked before. -
The guard affects every caller of
number_to_string, not just dict-key cleaning. From reading the code, it also appears to fix:DeepHash._prep_number(deephash.py:582-587) fortimedeltawhensignificant_digitsis set. This includesignore_order=Truecombined withignore_numeric_type_changes, which defaultssignificant_digitsto 12.timedeltais not intimesordate, so it reaches_prep_number.DeepDiff._diff_numbers(diff.py:1968) fornp.datetime64values whensignificant_digitsis set.
Both look like improvements, but they are not exercised by DeepDiff- or DeepHash-level tests.
-
Hot path:
isinstanceagainst a shorter tuple, so there is no performance cost. -
Behavioural note, not a regression: when key cleaning applies, temporal keys are now matched by their
str()form. This is already how the no-precision path (ignore_string_casealone) works. One consequence: two aware datetimes for the same instant in different timezones become different cleaned keys, while without key cleaning they match as dict keys. This seems acceptable. I'm mentioning it so it's a deliberate choice.
Tests
tests/test_helper.py:209(temporal values returned unchanged, usingis) andtests/test_diff_datetime.py:131,150(ignore_numeric_type_changes, and the string flags combined withsignificant_digits) go throughround()ondev, so they should fail there and pass with the fix. The no-precision string-flag cases andtest_number_to_string_numeric_types_keep_formatting(test_helper.py:221) are controls that already pass. I checked their expected strings by hand and they look correct.test_distinct_temporal_keys_keep_their_values(test_diff_datetime.py:163) asserts that the reported path element is the cleaned key, e.g."number:2020-05-17 22:15:00". This matches how_diff_dictalready reports cleaned numeric keys (diff.py:805-822). The test fixes that path format in place for temporal keys, and the path can't be used to navigate back to the original object.
Suggestions
- Consider adding one test that covers the non-key paths, e.g.
DeepHash(timedelta(seconds=1), significant_digits=2)orDeepDiff([timedelta(1)], [timedelta(1)], ignore_order=True, ignore_numeric_type_changes=True). - A short comment at
helper.py:478explaining whyonly_numbersis used (temporal types are innumbersfor type grouping but can't be rounded) would help keep this from being reverted later.
AI-assisted static review; the code was not executed.
Fixes #550.
Temporal dictionary keys can reach
round()throughnumber_to_string()and raiseTypeError. Restricting its guard toonly_numberslets temporal objects pass through unchanged while preserving numeric formatting and existing key-path and timezone semantics.ignore_numeric_type_changes=Truetriggers this with its default precision. The string-only flags (ignore_string_caseandignore_string_type_changes) trigger it whensignificant_digitsis explicitly set; their working no-precision behavior is covered as a control.Validation on CPython 3.14.7/macOS with an isolated existing dependency environment (pytest 9.1.1):
__round__error; 19 controls already passed.deepdiff/helper.py: 0 errors, 1 warning thatpytz.tzinfosource could not be resolved.The full repository suite and hosted Python/platform matrix were not run locally.
Related fixes were proposed in author-closed #599, #615, and #624; this patch was independently revalidated against current
dev.AI disclosure: OpenAI Codex was used for the implementation, tests, agent review, and this description.