Skip to content

Commit be22027

Browse files
authored
Merge pull request #459 from seperman/dev
7.0.1
2 parents 4c337cf + b391ae9 commit be22027

14 files changed

+241
-82
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# DeepDiff Change log
22

3+
4+
- v7-0-1
5+
- Fixes the translation between Difflib opcodes and Delta flat rows.
36
- v7-0-0
47
- When verbose=2, return `new_path` when the `path` and `new_path` are different (for example when ignore_order=True and the index of items have changed).
58
- Dropping support for Python 3.7

CITATION.cff

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ authors:
55
given-names: "Sep"
66
orcid: "https://orcid.org/0009-0009-5828-4345"
77
title: "DeepDiff"
8-
version: 7.0.0
8+
version: 7.0.1
99
date-released: 2024
1010
url: "https://github.com/seperman/deepdiff"

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# DeepDiff v 7.0.0
1+
# DeepDiff v 7.0.1
22

33
![Downloads](https://img.shields.io/pypi/dm/deepdiff.svg?style=flat)
44
![Python Versions](https://img.shields.io/pypi/pyversions/deepdiff.svg?style=flat)
@@ -17,12 +17,16 @@
1717

1818
Tested on Python 3.8+ and PyPy3.
1919

20-
- **[Documentation](https://zepworks.com/deepdiff/7.0.0/)**
20+
- **[Documentation](https://zepworks.com/deepdiff/7.0.1/)**
2121

2222
## What is new?
2323

2424
Please check the [ChangeLog](CHANGELOG.md) file for the detailed information.
2525

26+
DeepDiff 7-0-1
27+
28+
- Fixes the translation between Difflib opcodes and Delta flat rows.
29+
2630
DeepDiff 7-0-0
2731

2832
- DeepDiff 7 comes with an improved delta object. [Delta to flat dictionaries](https://zepworks.com/deepdiff/current/serialization.html#delta-serialize-to-flat-dictionaries) have undergone a major change. We have also introduced [Delta serialize to flat rows](https://zepworks.com/deepdiff/current/serialization.html#delta-serialize-to-flat-rows).

deepdiff/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""This module offers the DeepDiff, DeepSearch, grep, Delta and DeepHash classes."""
22
# flake8: noqa
3-
__version__ = '7.0.0'
3+
__version__ = '7.0.1'
44
import logging
55

66
if __name__ == '__main__':

deepdiff/delta.py

+103-15
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
strings, short_repr, numbers,
1212
np_ndarray, np_array_factory, numpy_dtypes, get_doc,
1313
not_found, numpy_dtype_string_to_type, dict_,
14-
Opcode, FlatDeltaRow, UnkownValueCode,
14+
Opcode, FlatDeltaRow, UnkownValueCode, FlatDataAction,
15+
OPCODE_TAG_TO_FLAT_DATA_ACTION,
16+
FLAT_DATA_ACTION_TO_OPCODE_TAG,
1517
)
1618
from deepdiff.path import (
1719
_path_to_elements, _get_nested_obj, _get_nested_obj_and_force,
@@ -877,8 +879,33 @@ def dumps(self):
877879
def to_dict(self):
878880
return dict(self.diff)
879881

882+
def _flatten_iterable_opcodes(self, _parse_path):
883+
"""
884+
Converts op_codes to FlatDeltaRows
885+
"""
886+
result = []
887+
for path, op_codes in self.diff['_iterable_opcodes'].items():
888+
for op_code in op_codes:
889+
result.append(
890+
FlatDeltaRow(
891+
path=_parse_path(path),
892+
action=OPCODE_TAG_TO_FLAT_DATA_ACTION[op_code.tag],
893+
value=op_code.new_values,
894+
old_value=op_code.old_values,
895+
type=type(op_code.new_values),
896+
old_type=type(op_code.old_values),
897+
new_path=None,
898+
t1_from_index=op_code.t1_from_index,
899+
t1_to_index=op_code.t1_to_index,
900+
t2_from_index=op_code.t2_from_index,
901+
t2_to_index=op_code.t2_to_index,
902+
903+
)
904+
)
905+
return result
906+
880907
@staticmethod
881-
def _get_flat_row(action, info, _parse_path, keys_and_funcs):
908+
def _get_flat_row(action, info, _parse_path, keys_and_funcs, report_type_changes=True):
882909
for path, details in info.items():
883910
row = {'path': _parse_path(path), 'action': action}
884911
for key, new_key, func in keys_and_funcs:
@@ -887,6 +914,11 @@ def _get_flat_row(action, info, _parse_path, keys_and_funcs):
887914
row[new_key] = func(details[key])
888915
else:
889916
row[new_key] = details[key]
917+
if report_type_changes:
918+
if 'value' in row and 'type' not in row:
919+
row['type'] = type(row['value'])
920+
if 'old_value' in row and 'old_type' not in row:
921+
row['old_type'] = type(row['old_value'])
890922
yield FlatDeltaRow(**row)
891923

892924
@staticmethod
@@ -918,28 +950,44 @@ def _from_flat_dicts(flat_dict_list):
918950
if action in FLATTENING_NEW_ACTION_MAP:
919951
action = FLATTENING_NEW_ACTION_MAP[action]
920952
index = path.pop()
921-
if action in {'attribute_added', 'attribute_removed'}:
953+
if action in {
954+
FlatDataAction.attribute_added,
955+
FlatDataAction.attribute_removed,
956+
}:
922957
root_element = ('root', GETATTR)
923958
else:
924959
root_element = ('root', GET)
925-
path_str = stringify_path(path, root_element=root_element) # We need the string path
960+
if isinstance(path, str):
961+
path_str = path
962+
else:
963+
path_str = stringify_path(path, root_element=root_element) # We need the string path
926964
if new_path and new_path != path:
927965
new_path = stringify_path(new_path, root_element=root_element)
928966
else:
929967
new_path = None
930968
if action not in result:
931969
result[action] = {}
932-
if action in {'iterable_items_added_at_indexes', 'iterable_items_removed_at_indexes'}:
970+
if action in {
971+
'iterable_items_added_at_indexes',
972+
'iterable_items_removed_at_indexes',
973+
}:
933974
if path_str not in result[action]:
934975
result[action][path_str] = {}
935976
result[action][path_str][index] = value
936-
elif action in {'set_item_added', 'set_item_removed'}:
977+
elif action in {
978+
FlatDataAction.set_item_added,
979+
FlatDataAction.set_item_removed
980+
}:
937981
if path_str not in result[action]:
938982
result[action][path_str] = set()
939983
result[action][path_str].add(value)
940984
elif action in {
941-
'dictionary_item_added', 'dictionary_item_removed',
942-
'attribute_removed', 'attribute_added', 'iterable_item_added', 'iterable_item_removed',
985+
FlatDataAction.dictionary_item_added,
986+
FlatDataAction.dictionary_item_removed,
987+
FlatDataAction.attribute_removed,
988+
FlatDataAction.attribute_added,
989+
FlatDataAction.iterable_item_added,
990+
FlatDataAction.iterable_item_removed,
943991
}:
944992
result[action][path_str] = value
945993
elif action == 'values_changed':
@@ -959,8 +1007,29 @@ def _from_flat_dicts(flat_dict_list):
9591007
]:
9601008
if elem_value != UnkownValueCode:
9611009
result[action][path_str][elem] = elem_value
962-
elif action == 'iterable_item_moved':
1010+
elif action == FlatDataAction.iterable_item_moved:
9631011
result[action][path_str] = {'value': value}
1012+
elif action in {
1013+
FlatDataAction.iterable_items_inserted,
1014+
FlatDataAction.iterable_items_deleted,
1015+
FlatDataAction.iterable_items_replaced,
1016+
FlatDataAction.iterable_items_equal,
1017+
}:
1018+
if '_iterable_opcodes' not in result:
1019+
result['_iterable_opcodes'] = {}
1020+
if path_str not in result['_iterable_opcodes']:
1021+
result['_iterable_opcodes'][path_str] = []
1022+
result['_iterable_opcodes'][path_str].append(
1023+
Opcode(
1024+
tag=FLAT_DATA_ACTION_TO_OPCODE_TAG[action],
1025+
t1_from_index=flat_dict.get('t1_from_index'),
1026+
t1_to_index=flat_dict.get('t1_to_index'),
1027+
t2_from_index=flat_dict.get('t2_from_index'),
1028+
t2_to_index=flat_dict.get('t2_to_index'),
1029+
new_values=flat_dict.get('value'),
1030+
old_values=flat_dict.get('old_value'),
1031+
)
1032+
)
9641033
if new_path:
9651034
result[action][path_str]['new_path'] = new_path
9661035

@@ -1060,6 +1129,9 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
10601129
'iterable_items_removed_at_indexes': 'unordered_iterable_item_removed',
10611130
}
10621131
for action, info in self.diff.items():
1132+
if action == '_iterable_opcodes':
1133+
result.extend(self._flatten_iterable_opcodes(_parse_path=_parse_path))
1134+
continue
10631135
if action.startswith('_'):
10641136
continue
10651137
if action in FLATTENING_NEW_ACTION_MAP:
@@ -1072,12 +1144,20 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
10721144
path2.append((index, 'GET'))
10731145
else:
10741146
path2.append(index)
1075-
result.append(FlatDeltaRow(path=path2, value=value, action=new_action))
1147+
if report_type_changes:
1148+
row = FlatDeltaRow(path=path2, value=value, action=new_action, type=type(value))
1149+
else:
1150+
row = FlatDeltaRow(path=path2, value=value, action=new_action)
1151+
result.append(row)
10761152
elif action in {'set_item_added', 'set_item_removed'}:
10771153
for path, values in info.items():
10781154
path = _parse_path(path)
10791155
for value in values:
1080-
result.append(FlatDeltaRow(path=path, value=value, action=action))
1156+
if report_type_changes:
1157+
row = FlatDeltaRow(path=path, value=value, action=action, type=type(value))
1158+
else:
1159+
row = FlatDeltaRow(path=path, value=value, action=action)
1160+
result.append(row)
10811161
elif action == 'dictionary_item_added':
10821162
for path, value in info.items():
10831163
path = _parse_path(path)
@@ -1092,14 +1172,22 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
10921172
elif isinstance(value, set) and len(value) == 1:
10931173
value = value.pop()
10941174
action = 'set_item_added'
1095-
result.append(FlatDeltaRow(path=path, value=value, action=action))
1175+
if report_type_changes:
1176+
row = FlatDeltaRow(path=path, value=value, action=action, type=type(value))
1177+
else:
1178+
row = FlatDeltaRow(path=path, value=value, action=action)
1179+
result.append(row)
10961180
elif action in {
10971181
'dictionary_item_removed', 'iterable_item_added',
10981182
'iterable_item_removed', 'attribute_removed', 'attribute_added'
10991183
}:
11001184
for path, value in info.items():
11011185
path = _parse_path(path)
1102-
result.append(FlatDeltaRow(path=path, value=value, action=action))
1186+
if report_type_changes:
1187+
row = FlatDeltaRow(path=path, value=value, action=action, type=type(value))
1188+
else:
1189+
row = FlatDeltaRow(path=path, value=value, action=action)
1190+
result.append(row)
11031191
elif action == 'type_changes':
11041192
if not report_type_changes:
11051193
action = 'values_changed'
@@ -1109,16 +1197,16 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
11091197
info=info,
11101198
_parse_path=_parse_path,
11111199
keys_and_funcs=keys_and_funcs,
1200+
report_type_changes=report_type_changes,
11121201
):
11131202
result.append(row)
1114-
elif action == '_iterable_opcodes':
1115-
result.extend(self._flatten_iterable_opcodes())
11161203
else:
11171204
for row in self._get_flat_row(
11181205
action=action,
11191206
info=info,
11201207
_parse_path=_parse_path,
11211208
keys_and_funcs=keys_and_funcs,
1209+
report_type_changes=report_type_changes,
11221210
):
11231211
result.append(row)
11241212
return result

deepdiff/helper.py

+30-2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ class pydantic_base_model_type:
110110
NUMERICS = frozenset(string.digits)
111111

112112

113+
class EnumBase(str, enum.Enum):
114+
def __repr__(self):
115+
"""
116+
We need to add a single quotes so we can easily copy the value when we do ipdb.
117+
"""
118+
return f"'{self.name}'"
119+
120+
def __str__(self):
121+
return self.name
122+
123+
113124
def _int_or_zero(value):
114125
"""
115126
Tries to extract some number from a string.
@@ -739,6 +750,13 @@ def named_tuple_repr(self):
739750
return f"{self.__class__.__name__}({', '.join(fields)})"
740751

741752

753+
class OpcodeTag(EnumBase):
754+
insert = 'insert'
755+
delete = 'delete'
756+
equal = 'equal'
757+
replace = 'replace'
758+
759+
742760
class Opcode(NamedTuple):
743761
tag: str
744762
t1_from_index: int
@@ -751,7 +769,7 @@ class Opcode(NamedTuple):
751769
__repr__ = __str__ = named_tuple_repr
752770

753771

754-
class FlatDataAction(str, enum.Enum):
772+
class FlatDataAction(EnumBase):
755773
values_changed = 'values_changed'
756774
type_changes = 'type_changes'
757775
set_item_added = 'set_item_added'
@@ -771,7 +789,17 @@ class FlatDataAction(str, enum.Enum):
771789
unordered_iterable_item_removed = 'unordered_iterable_item_removed'
772790

773791

774-
UnkownValueCode = '*-UNKNOWN-*'
792+
OPCODE_TAG_TO_FLAT_DATA_ACTION = {
793+
OpcodeTag.insert: FlatDataAction.iterable_items_inserted,
794+
OpcodeTag.delete: FlatDataAction.iterable_items_deleted,
795+
OpcodeTag.replace: FlatDataAction.iterable_items_replaced,
796+
OpcodeTag.equal: FlatDataAction.iterable_items_equal,
797+
}
798+
799+
FLAT_DATA_ACTION_TO_OPCODE_TAG = {v: i for i, v in OPCODE_TAG_TO_FLAT_DATA_ACTION.items()}
800+
801+
802+
UnkownValueCode = 'unknown___'
775803

776804

777805
class FlatDeltaRow(NamedTuple):

deepdiff/path.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ def parse_path(path, root_element=DEFAULT_FIRST_ELEMENT, include_actions=False):
261261

262262
result = _path_to_elements(path, root_element=root_element)
263263
result = iter(result)
264-
next(result) # We don't want the root item
264+
if root_element:
265+
next(result) # We don't want the root item
265266
if include_actions is False:
266267
return [i[0] for i in result]
267268
return [{'element': i[0], 'action': i[1]} for i in result]

docs/changelog.rst

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ Changelog
55

66
DeepDiff Changelog
77

8+
9+
- v7-0-1
10+
11+
- Fixes the translation between Difflib opcodes and Delta flat rows.
12+
813
- v7-0-0
914

1015
- When verbose=2, return ``new_path`` when the ``path`` and

docs/conf.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@
6161
# built documents.
6262
#
6363
# The short X.Y version.
64-
version = '7.0.0'
64+
version = '7.0.1'
6565
# The full version, including alpha/beta/rc tags.
66-
release = '7.0.0'
66+
release = '7.0.1'
6767

6868
load_dotenv(override=True)
6969
DOC_VERSION = os.environ.get('DOC_VERSION', version)

docs/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
contain the root `toctree` directive.
55
66
7-
DeepDiff 7.0.0 documentation!
7+
DeepDiff 7.0.1 documentation!
88
=============================
99

1010
*******

0 commit comments

Comments
 (0)