Skip to content

Commit

Permalink
Merge pull request #459 from seperman/dev
Browse files Browse the repository at this point in the history
7.0.1
  • Loading branch information
seperman authored Apr 8, 2024
2 parents 4c337cf + b391ae9 commit be22027
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 82 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# DeepDiff Change log


- v7-0-1
- Fixes the translation between Difflib opcodes and Delta flat rows.
- v7-0-0
- 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).
- Dropping support for Python 3.7
Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ authors:
given-names: "Sep"
orcid: "https://orcid.org/0009-0009-5828-4345"
title: "DeepDiff"
version: 7.0.0
version: 7.0.1
date-released: 2024
url: "https://github.com/seperman/deepdiff"
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# DeepDiff v 7.0.0
# DeepDiff v 7.0.1

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

Tested on Python 3.8+ and PyPy3.

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

## What is new?

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

DeepDiff 7-0-1

- Fixes the translation between Difflib opcodes and Delta flat rows.

DeepDiff 7-0-0

- 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).
Expand Down
2 changes: 1 addition & 1 deletion deepdiff/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""This module offers the DeepDiff, DeepSearch, grep, Delta and DeepHash classes."""
# flake8: noqa
__version__ = '7.0.0'
__version__ = '7.0.1'
import logging

if __name__ == '__main__':
Expand Down
118 changes: 103 additions & 15 deletions deepdiff/delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
strings, short_repr, numbers,
np_ndarray, np_array_factory, numpy_dtypes, get_doc,
not_found, numpy_dtype_string_to_type, dict_,
Opcode, FlatDeltaRow, UnkownValueCode,
Opcode, FlatDeltaRow, UnkownValueCode, FlatDataAction,
OPCODE_TAG_TO_FLAT_DATA_ACTION,
FLAT_DATA_ACTION_TO_OPCODE_TAG,
)
from deepdiff.path import (
_path_to_elements, _get_nested_obj, _get_nested_obj_and_force,
Expand Down Expand Up @@ -877,8 +879,33 @@ def dumps(self):
def to_dict(self):
return dict(self.diff)

def _flatten_iterable_opcodes(self, _parse_path):
"""
Converts op_codes to FlatDeltaRows
"""
result = []
for path, op_codes in self.diff['_iterable_opcodes'].items():
for op_code in op_codes:
result.append(
FlatDeltaRow(
path=_parse_path(path),
action=OPCODE_TAG_TO_FLAT_DATA_ACTION[op_code.tag],
value=op_code.new_values,
old_value=op_code.old_values,
type=type(op_code.new_values),
old_type=type(op_code.old_values),
new_path=None,
t1_from_index=op_code.t1_from_index,
t1_to_index=op_code.t1_to_index,
t2_from_index=op_code.t2_from_index,
t2_to_index=op_code.t2_to_index,

)
)
return result

@staticmethod
def _get_flat_row(action, info, _parse_path, keys_and_funcs):
def _get_flat_row(action, info, _parse_path, keys_and_funcs, report_type_changes=True):
for path, details in info.items():
row = {'path': _parse_path(path), 'action': action}
for key, new_key, func in keys_and_funcs:
Expand All @@ -887,6 +914,11 @@ def _get_flat_row(action, info, _parse_path, keys_and_funcs):
row[new_key] = func(details[key])
else:
row[new_key] = details[key]
if report_type_changes:
if 'value' in row and 'type' not in row:
row['type'] = type(row['value'])
if 'old_value' in row and 'old_type' not in row:
row['old_type'] = type(row['old_value'])
yield FlatDeltaRow(**row)

@staticmethod
Expand Down Expand Up @@ -918,28 +950,44 @@ def _from_flat_dicts(flat_dict_list):
if action in FLATTENING_NEW_ACTION_MAP:
action = FLATTENING_NEW_ACTION_MAP[action]
index = path.pop()
if action in {'attribute_added', 'attribute_removed'}:
if action in {
FlatDataAction.attribute_added,
FlatDataAction.attribute_removed,
}:
root_element = ('root', GETATTR)
else:
root_element = ('root', GET)
path_str = stringify_path(path, root_element=root_element) # We need the string path
if isinstance(path, str):
path_str = path
else:
path_str = stringify_path(path, root_element=root_element) # We need the string path
if new_path and new_path != path:
new_path = stringify_path(new_path, root_element=root_element)
else:
new_path = None
if action not in result:
result[action] = {}
if action in {'iterable_items_added_at_indexes', 'iterable_items_removed_at_indexes'}:
if action in {
'iterable_items_added_at_indexes',
'iterable_items_removed_at_indexes',
}:
if path_str not in result[action]:
result[action][path_str] = {}
result[action][path_str][index] = value
elif action in {'set_item_added', 'set_item_removed'}:
elif action in {
FlatDataAction.set_item_added,
FlatDataAction.set_item_removed
}:
if path_str not in result[action]:
result[action][path_str] = set()
result[action][path_str].add(value)
elif action in {
'dictionary_item_added', 'dictionary_item_removed',
'attribute_removed', 'attribute_added', 'iterable_item_added', 'iterable_item_removed',
FlatDataAction.dictionary_item_added,
FlatDataAction.dictionary_item_removed,
FlatDataAction.attribute_removed,
FlatDataAction.attribute_added,
FlatDataAction.iterable_item_added,
FlatDataAction.iterable_item_removed,
}:
result[action][path_str] = value
elif action == 'values_changed':
Expand All @@ -959,8 +1007,29 @@ def _from_flat_dicts(flat_dict_list):
]:
if elem_value != UnkownValueCode:
result[action][path_str][elem] = elem_value
elif action == 'iterable_item_moved':
elif action == FlatDataAction.iterable_item_moved:
result[action][path_str] = {'value': value}
elif action in {
FlatDataAction.iterable_items_inserted,
FlatDataAction.iterable_items_deleted,
FlatDataAction.iterable_items_replaced,
FlatDataAction.iterable_items_equal,
}:
if '_iterable_opcodes' not in result:
result['_iterable_opcodes'] = {}
if path_str not in result['_iterable_opcodes']:
result['_iterable_opcodes'][path_str] = []
result['_iterable_opcodes'][path_str].append(
Opcode(
tag=FLAT_DATA_ACTION_TO_OPCODE_TAG[action],
t1_from_index=flat_dict.get('t1_from_index'),
t1_to_index=flat_dict.get('t1_to_index'),
t2_from_index=flat_dict.get('t2_from_index'),
t2_to_index=flat_dict.get('t2_to_index'),
new_values=flat_dict.get('value'),
old_values=flat_dict.get('old_value'),
)
)
if new_path:
result[action][path_str]['new_path'] = new_path

Expand Down Expand Up @@ -1060,6 +1129,9 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
'iterable_items_removed_at_indexes': 'unordered_iterable_item_removed',
}
for action, info in self.diff.items():
if action == '_iterable_opcodes':
result.extend(self._flatten_iterable_opcodes(_parse_path=_parse_path))
continue
if action.startswith('_'):
continue
if action in FLATTENING_NEW_ACTION_MAP:
Expand All @@ -1072,12 +1144,20 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
path2.append((index, 'GET'))
else:
path2.append(index)
result.append(FlatDeltaRow(path=path2, value=value, action=new_action))
if report_type_changes:
row = FlatDeltaRow(path=path2, value=value, action=new_action, type=type(value))
else:
row = FlatDeltaRow(path=path2, value=value, action=new_action)
result.append(row)
elif action in {'set_item_added', 'set_item_removed'}:
for path, values in info.items():
path = _parse_path(path)
for value in values:
result.append(FlatDeltaRow(path=path, value=value, action=action))
if report_type_changes:
row = FlatDeltaRow(path=path, value=value, action=action, type=type(value))
else:
row = FlatDeltaRow(path=path, value=value, action=action)
result.append(row)
elif action == 'dictionary_item_added':
for path, value in info.items():
path = _parse_path(path)
Expand All @@ -1092,14 +1172,22 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
elif isinstance(value, set) and len(value) == 1:
value = value.pop()
action = 'set_item_added'
result.append(FlatDeltaRow(path=path, value=value, action=action))
if report_type_changes:
row = FlatDeltaRow(path=path, value=value, action=action, type=type(value))
else:
row = FlatDeltaRow(path=path, value=value, action=action)
result.append(row)
elif action in {
'dictionary_item_removed', 'iterable_item_added',
'iterable_item_removed', 'attribute_removed', 'attribute_added'
}:
for path, value in info.items():
path = _parse_path(path)
result.append(FlatDeltaRow(path=path, value=value, action=action))
if report_type_changes:
row = FlatDeltaRow(path=path, value=value, action=action, type=type(value))
else:
row = FlatDeltaRow(path=path, value=value, action=action)
result.append(row)
elif action == 'type_changes':
if not report_type_changes:
action = 'values_changed'
Expand All @@ -1109,16 +1197,16 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
info=info,
_parse_path=_parse_path,
keys_and_funcs=keys_and_funcs,
report_type_changes=report_type_changes,
):
result.append(row)
elif action == '_iterable_opcodes':
result.extend(self._flatten_iterable_opcodes())
else:
for row in self._get_flat_row(
action=action,
info=info,
_parse_path=_parse_path,
keys_and_funcs=keys_and_funcs,
report_type_changes=report_type_changes,
):
result.append(row)
return result
Expand Down
32 changes: 30 additions & 2 deletions deepdiff/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ class pydantic_base_model_type:
NUMERICS = frozenset(string.digits)


class EnumBase(str, enum.Enum):
def __repr__(self):
"""
We need to add a single quotes so we can easily copy the value when we do ipdb.
"""
return f"'{self.name}'"

def __str__(self):
return self.name


def _int_or_zero(value):
"""
Tries to extract some number from a string.
Expand Down Expand Up @@ -739,6 +750,13 @@ def named_tuple_repr(self):
return f"{self.__class__.__name__}({', '.join(fields)})"


class OpcodeTag(EnumBase):
insert = 'insert'
delete = 'delete'
equal = 'equal'
replace = 'replace'


class Opcode(NamedTuple):
tag: str
t1_from_index: int
Expand All @@ -751,7 +769,7 @@ class Opcode(NamedTuple):
__repr__ = __str__ = named_tuple_repr


class FlatDataAction(str, enum.Enum):
class FlatDataAction(EnumBase):
values_changed = 'values_changed'
type_changes = 'type_changes'
set_item_added = 'set_item_added'
Expand All @@ -771,7 +789,17 @@ class FlatDataAction(str, enum.Enum):
unordered_iterable_item_removed = 'unordered_iterable_item_removed'


UnkownValueCode = '*-UNKNOWN-*'
OPCODE_TAG_TO_FLAT_DATA_ACTION = {
OpcodeTag.insert: FlatDataAction.iterable_items_inserted,
OpcodeTag.delete: FlatDataAction.iterable_items_deleted,
OpcodeTag.replace: FlatDataAction.iterable_items_replaced,
OpcodeTag.equal: FlatDataAction.iterable_items_equal,
}

FLAT_DATA_ACTION_TO_OPCODE_TAG = {v: i for i, v in OPCODE_TAG_TO_FLAT_DATA_ACTION.items()}


UnkownValueCode = 'unknown___'


class FlatDeltaRow(NamedTuple):
Expand Down
3 changes: 2 additions & 1 deletion deepdiff/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ def parse_path(path, root_element=DEFAULT_FIRST_ELEMENT, include_actions=False):

result = _path_to_elements(path, root_element=root_element)
result = iter(result)
next(result) # We don't want the root item
if root_element:
next(result) # We don't want the root item
if include_actions is False:
return [i[0] for i in result]
return [{'element': i[0], 'action': i[1]} for i in result]
Expand Down
5 changes: 5 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ Changelog

DeepDiff Changelog


- v7-0-1

- Fixes the translation between Difflib opcodes and Delta flat rows.

- v7-0-0

- When verbose=2, return ``new_path`` when the ``path`` and
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@
# built documents.
#
# The short X.Y version.
version = '7.0.0'
version = '7.0.1'
# The full version, including alpha/beta/rc tags.
release = '7.0.0'
release = '7.0.1'

load_dotenv(override=True)
DOC_VERSION = os.environ.get('DOC_VERSION', version)
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
contain the root `toctree` directive.
DeepDiff 7.0.0 documentation!
DeepDiff 7.0.1 documentation!
=============================

*******
Expand Down
Loading

0 comments on commit be22027

Please sign in to comment.