Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# DeepDiff Change log

- Unreleased
- Preserve original string key casing in reported paths when `ignore_string_case=True`, so path exclusions can use the input keys (#515).
- Fixed missing type changes between equal-comparing items inside ordered iterables, e.g. `DeepDiff([2], [2.0])` now reports the `int` → `float` change like `DeepDiff(2, 2.0)` and `DeepDiff({'a': 2}, {'a': 2.0})` already do (issue #605).

- v9-1-0
Expand Down
6 changes: 4 additions & 2 deletions deepdiff/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -813,13 +813,15 @@ def _diff_dict(
continue
parents_ids_added = add_to_frozen_set(parents_ids, item_id)

# Case normalization is for matching, not for paths into the input objects.
preserve_case = self.ignore_string_case and isinstance(key1, str) and isinstance(key2, str)
# Go one level deeper
next_level = level.branch_deeper(
t1[key1],
t2[key2],
child_relationship_class=rel_class,
child_relationship_param=key,
child_relationship_param2=key,
child_relationship_param=key1 if preserve_case else key,
child_relationship_param2=key2 if preserve_case else key,
)
self._diff(next_level, parents_ids_added, local_tree=local_tree)

Expand Down
30 changes: 30 additions & 0 deletions tests/test_diff_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,36 @@ def test_string_dict_key_ignore_case(self):
result = {}
assert result == ddiff

def test_ignore_case_preserves_original_dictionary_paths(self):
t1 = {'User': {'AboutMe': 1}}
t2 = {'user': {'aboutme': 2}}
diff = DeepDiff(t1, t2, ignore_string_case=True)
assert diff == {'values_changed': {
"root['User']['AboutMe']": {'old_value': 1, 'new_value': 2}
}}
tree = DeepDiff(t1, t2, ignore_string_case=True, view='tree')
change = next(iter(tree['values_changed']))
assert change.path() == "root['User']['AboutMe']"
assert change.path(use_t2=True) == "root['user']['aboutme']"

@pytest.mark.parametrize('path_option', ['exclude_paths', 'exclude_regex_paths'])
def test_ignore_case_excludes_original_dictionary_path(self, path_option):
t1 = {'User': {'AboutMe': 1}}
t2 = {'User': {'AboutMe': 2}}
path = "root['User']['AboutMe']"
if path_option == 'exclude_regex_paths':
path = r"root\['User'\]\['AboutMe'\]$"
assert not DeepDiff(t1, t2, ignore_string_case=True, **{path_option: [path]})

def test_ignore_case_preserves_paths_for_nested_additions_and_removals(self):
t1 = {'User': {'Keep': 1, 'Removed': 2}}
t2 = {'user': {'keep': 1, 'Added': 3}}
diff = DeepDiff(t1, t2, ignore_string_case=True, threshold_to_diff_deeper=0)
assert diff == {
'dictionary_item_added': ["root['User']['Added']"],
'dictionary_item_removed': ["root['User']['Removed']"],
}

def test_string_list_ignore_case(self):
t1 = ['AboutMe', 'ALIAS']
t2 = ['aboutme', 'alias']
Expand Down