Fix missing type changes for equal-comparing list items (#605)#607
Open
Sanjays2402 wants to merge 1 commit into
Open
Fix missing type changes for equal-comparing list items (#605)#607Sanjays2402 wants to merge 1 commit into
Sanjays2402 wants to merge 1 commit into
Conversation
DeepDiff([2], [2.0]) returned {} even though the scalar equivalent
DeepDiff(2, 2.0) and the dict equivalent DeepDiff({'a': 2}, {'a': 2.0})
both correctly report an int -> float type_change.
Root cause: for ordered iterables whose items are all basic-hashable,
DeepDiff diffs the two sequences with difflib.SequenceMatcher. Because
2 == 2.0 (and hash(2) == hash(2.0)), SequenceMatcher classifies the pair
as an 'equal' opcode, so the elements are never handed to _diff() -- the
only place the type-change check (get_type(t1) != get_type(t2)) lives.
The whole difference silently disappeared. The same happened for other
equal-but-differently-typed pairs such as [1] vs [Decimal(1)] and
[True] vs [1], and for changes buried among unchanged items, e.g.
[1, 2, 3] vs [1, 2.0, 3].
Fix: in the 'equal' opcode branch of _diff_ordered_iterable_by_difflib,
recurse into _diff() for any aligned pair whose types differ. Routing
through _diff() reuses the existing type-change logic, so all options are
honored automatically -- ignore_numeric_type_changes=True and
ignore_type_in_groups still suppress the report, and genuinely identical
items still produce nothing.
Added regression tests (fail before this change, pass after):
- [2] vs [2.0] reports the int -> float type_change and matches the
scalar result
- the change is detected among surrounding unchanged items
- ignore_numeric_type_changes=True still yields {}
Full test suite: 1251 passed, unchanged from baseline.
Fixes qlustered#605
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #605
The bug
DeepDiff([2], [2.0])reports no difference at all, even though the scalar and dict equivalents both correctly report anint→floattype change:The same silent miss affects any equal-but-differently-typed pair inside a list, and changes buried among unchanged items:
Root cause
For ordered iterables whose items are all basic-hashable,
_diff_iterable_in_orderdiffs the two sequences withdifflib.SequenceMatcher. Because2 == 2.0(andhash(2) == hash(2.0)),SequenceMatcherclassifies the pair as an'equal'opcode:The
'equal'branch in_diff_ordered_iterable_by_difflibrecorded the opcode andcontinued — the items were never handed to_diff(), which is the only place the type-change checkget_type(t1) != get_type(t2)lives. So the difference vanished. Scalars and dict values go through_diff()directly, which is why they behave correctly.This is the ordered-diff complement of the
ignore_order=Trueint-vs-float fix already noted in the v9.0.0 changelog.The fix
In the
'equal'opcode branch, recurse into_diff()for any aligned pair whose types differ:Routing through
_diff()reuses the existing type-change machinery, so every option is honored automatically — no duplicated logic.Before / after
DeepDiff([2], [2.0]){}type_changes root[0]: int→floatDeepDiff([1,2,3], [1,2.0,3]){}type_changes root[1]: int→floatDeepDiff([1], [Decimal(1)]){}type_changes root[0]: int→DecimalDeepDiff([2], [2.0], ignore_numeric_type_changes=True){}{}(unchanged — correctly suppressed)DeepDiff([2], [2]){}{}(unchanged — no over-reporting)Tests
Added three regression tests in
tests/test_diff_text.py(repo style):test_item_type_change_in_list—[2]vs[2.0]reports the change and asserts it matches the scalar resulttest_item_type_change_in_list_among_unchanged_items—[1,2,3]vs[1,2.0,3]test_item_type_change_in_list_ignored_when_requested—ignore_numeric_type_changes=Truestill yields{}Proof they guard the bug: with the source change stashed, the two positive tests FAIL; restored, all three PASS.
Full suite unchanged from baseline: 1251 passed, 44 skipped (the only failures locally are missing-optional-dep / macOS
setrlimitenv artifacts, identical with and without this change).