Skip to content

Commit 65714eb

Browse files
authored
Merge pull request #523 from seperman/dev
8.3.0 Better summary for repr
2 parents ed25202 + 75c0cd9 commit 65714eb

26 files changed

+15288
-156
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,5 @@ temp*
6969

7070
# env file
7171
.env
72+
73+
pyrightconfig.json

CHANGELOG.md

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

3+
- v8-3-0
4+
- Fixed some static typing issues
5+
- Added the summarize module for better repr of nested values
6+
37
- v8-2-0
48
- Small optimizations so we don't load functions that are not needed
59
- Updated the minimum version of Orderly-set

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ Tested on Python 3.8+ and PyPy3.
2323

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

26+
DeepDiff 8-3-0
27+
28+
- Fixed some static typing issues
29+
- Added the summarize module for better repr of nested values
30+
2631
DeepDiff 8-2-0
2732

2833
- Small optimizations so we don't load functions that are not needed

conftest.py

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ def nested_a_result():
4646
return json.load(the_file)
4747

4848

49+
@pytest.fixture(scope='function')
50+
def compounds():
51+
with open(os.path.join(FIXTURES_DIR, 'compounds.json')) as the_file:
52+
return json.load(the_file)
53+
54+
4955
@pytest.fixture(scope='class')
5056
def nested_a_affected_paths():
5157
return {

deepdiff/__init__.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
logging.basicConfig(format='%(asctime)s %(levelname)8s %(message)s')
88

99

10-
from .diff import DeepDiff
11-
from .search import DeepSearch, grep
12-
from .deephash import DeepHash
13-
from .delta import Delta
14-
from .path import extract, parse_path
10+
from .diff import DeepDiff as DeepDiff
11+
from .search import DeepSearch as DeepSearch, grep as grep
12+
from .deephash import DeepHash as DeepHash
13+
from .delta import Delta as Delta
14+
from .path import extract as extract, parse_path as parse_path

deepdiff/commands.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,7 @@ def diff(
112112
sys.stdout.buffer.write(delta.dumps())
113113
else:
114114
try:
115-
if orjson:
116-
print(diff.to_json(option=orjson.OPT_INDENT_2))
117-
else:
118-
print(diff.to_json(indent=2))
115+
print(diff.to_json(indent=2))
119116
except Exception:
120117
pprint(diff, indent=2)
121118

deepdiff/deephash.py

+19-16
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
convert_item_or_items_into_set_else_none, get_doc,
1212
convert_item_or_items_into_compiled_regexes_else_none,
1313
get_id, type_is_subclass_of_type_group, type_in_type_group,
14-
number_to_string, datetime_normalize, KEY_TO_VAL_STR, short_repr,
14+
number_to_string, datetime_normalize, KEY_TO_VAL_STR,
1515
get_truncate_datetime, dict_, add_root_to_paths, PydanticBaseModel)
16+
17+
from deepdiff.summarize import summarize
1618
from deepdiff.base import Base
1719

1820
try:
@@ -105,8 +107,8 @@ def prepare_string_for_hashing(
105107
break
106108
except UnicodeDecodeError as er:
107109
err = er
108-
if not encoded:
109-
obj_decoded = obj.decode('utf-8', errors='ignore')
110+
if not encoded and err is not None:
111+
obj_decoded = obj.decode('utf-8', errors='ignore') # type: ignore
110112
start = max(err.start - 20, 0)
111113
start_prefix = ''
112114
if start > 0:
@@ -315,9 +317,10 @@ def __repr__(self):
315317
"""
316318
Hide the counts since it will be confusing to see them when they are hidden everywhere else.
317319
"""
318-
return short_repr(self._get_objects_to_hashes_dict(extract_index=0), max_length=500)
320+
return summarize(self._get_objects_to_hashes_dict(extract_index=0), max_length=500)
319321

320-
__str__ = __repr__
322+
def __str__(self):
323+
return str(self._get_objects_to_hashes_dict(extract_index=0))
321324

322325
def __bool__(self):
323326
return bool(self.hashes)
@@ -376,7 +379,7 @@ def _skip_this(self, obj, parent):
376379
skip = False
377380
break
378381
elif self.exclude_regex_paths and any(
379-
[exclude_regex_path.search(parent) for exclude_regex_path in self.exclude_regex_paths]):
382+
[exclude_regex_path.search(parent) for exclude_regex_path in self.exclude_regex_paths]): # type: ignore
380383
skip = True
381384
elif self.exclude_types_tuple and isinstance(obj, self.exclude_types_tuple):
382385
skip = True
@@ -537,7 +540,7 @@ def _hash(self, obj, parent, parents_ids=EMPTY_FROZENSET):
537540
elif isinstance(obj, datetime.date):
538541
result = self._prep_date(obj)
539542

540-
elif isinstance(obj, numbers):
543+
elif isinstance(obj, numbers): # type: ignore
541544
result = self._prep_number(obj)
542545

543546
elif isinstance(obj, MutableMapping):
@@ -546,17 +549,17 @@ def _hash(self, obj, parent, parents_ids=EMPTY_FROZENSET):
546549
elif isinstance(obj, tuple):
547550
result, counts = self._prep_tuple(obj=obj, parent=parent, parents_ids=parents_ids)
548551

549-
elif (pandas and isinstance(obj, pandas.DataFrame)):
550-
def gen():
551-
yield ('dtype', obj.dtypes)
552-
yield ('index', obj.index)
553-
yield from obj.items() # which contains (column name, series tuples)
552+
elif (pandas and isinstance(obj, pandas.DataFrame)): # type: ignore
553+
def gen(): # type: ignore
554+
yield ('dtype', obj.dtypes) # type: ignore
555+
yield ('index', obj.index) # type: ignore
556+
yield from obj.items() # type: ignore # which contains (column name, series tuples)
554557
result, counts = self._prep_iterable(obj=gen(), parent=parent, parents_ids=parents_ids)
555-
elif (polars and isinstance(obj, polars.DataFrame)):
558+
elif (polars and isinstance(obj, polars.DataFrame)): # type: ignore
556559
def gen():
557-
yield from obj.columns
558-
yield from list(obj.schema.items())
559-
yield from obj.rows()
560+
yield from obj.columns # type: ignore
561+
yield from list(obj.schema.items()) # type: ignore
562+
yield from obj.rows() # type: ignore
560563
result, counts = self._prep_iterable(obj=gen(), parent=parent, parents_ids=parents_ids)
561564

562565
elif isinstance(obj, Iterable):

deepdiff/delta.py

+26-26
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from deepdiff import DeepDiff
88
from deepdiff.serialization import pickle_load, pickle_dump
99
from deepdiff.helper import (
10-
strings, short_repr, numbers,
10+
strings, numbers,
1111
np_ndarray, np_array_factory, numpy_dtypes, get_doc,
1212
not_found, numpy_dtype_string_to_type, dict_,
1313
Opcode, FlatDeltaRow, UnkownValueCode, FlatDataAction,
@@ -20,7 +20,7 @@
2020
GET, GETATTR, parse_path, stringify_path,
2121
)
2222
from deepdiff.anyset import AnySet
23-
23+
from deepdiff.summarize import summarize
2424

2525
logger = logging.getLogger(__name__)
2626

@@ -165,13 +165,13 @@ def _deserializer(obj, safe_to_import=None):
165165
self.reset()
166166

167167
def __repr__(self):
168-
return "<Delta: {}>".format(short_repr(self.diff, max_length=100))
168+
return "<Delta: {}>".format(summarize(self.diff, max_length=100))
169169

170170
def reset(self):
171171
self.post_process_paths_to_convert = dict_()
172172

173173
def __add__(self, other):
174-
if isinstance(other, numbers) and self._numpy_paths:
174+
if isinstance(other, numbers) and self._numpy_paths: # type: ignore
175175
raise DeltaNumpyOperatorOverrideError(DELTA_NUMPY_OPERATOR_OVERRIDE_MSG)
176176
if self.mutate:
177177
self.root = other
@@ -240,7 +240,7 @@ def _get_elem_and_compare_to_old_value(
240240
if action == GET:
241241
current_old_value = obj[elem]
242242
elif action == GETATTR:
243-
current_old_value = getattr(obj, elem)
243+
current_old_value = getattr(obj, elem) # type: ignore
244244
else:
245245
raise DeltaError(INVALID_ACTION_WHEN_CALLING_GET_ELEM.format(action))
246246
except (KeyError, IndexError, AttributeError, TypeError) as e:
@@ -261,7 +261,7 @@ def _get_elem_and_compare_to_old_value(
261261
else:
262262
obj[elem] = _forced_old_value
263263
elif action == GETATTR:
264-
setattr(obj, elem, _forced_old_value)
264+
setattr(obj, elem, _forced_old_value) # type: ignore
265265
return _forced_old_value
266266
current_old_value = not_found
267267
if isinstance(path_for_err_reporting, (list, tuple)):
@@ -289,7 +289,7 @@ def _simple_set_elem_value(self, obj, path_for_err_reporting, elem=None, value=N
289289
else:
290290
self._raise_or_log(ELEM_NOT_FOUND_TO_ADD_MSG.format(elem, path_for_err_reporting))
291291
elif action == GETATTR:
292-
setattr(obj, elem, value)
292+
setattr(obj, elem, value) # type: ignore
293293
else:
294294
raise DeltaError(INVALID_ACTION_WHEN_CALLING_SIMPLE_SET_ELEM.format(action))
295295
except (KeyError, IndexError, AttributeError, TypeError) as e:
@@ -457,8 +457,8 @@ def _do_item_added(self, items, sort=True, insert=False):
457457
continue # pragma: no cover. Due to cPython peephole optimizer, this line doesn't get covered. https://github.com/nedbat/coveragepy/issues/198
458458

459459
# Insert is only true for iterables, make sure it is a valid index.
460-
if(insert and elem < len(obj)):
461-
obj.insert(elem, None)
460+
if(insert and elem < len(obj)): # type: ignore
461+
obj.insert(elem, None) # type: ignore
462462

463463
self._set_new_value(parent, parent_to_obj_elem, parent_to_obj_action,
464464
obj, elements, path, elem, action, new_value)
@@ -482,7 +482,7 @@ def _do_post_process(self):
482482
def _do_pre_process(self):
483483
if self._numpy_paths and ('iterable_item_added' in self.diff or 'iterable_item_removed' in self.diff):
484484
preprocess_paths = dict_()
485-
for path, type_ in self._numpy_paths.items():
485+
for path, type_ in self._numpy_paths.items(): # type: ignore
486486
preprocess_paths[path] = {'old_type': np_ndarray, 'new_type': list}
487487
try:
488488
type_ = numpy_dtype_string_to_type(type_)
@@ -507,7 +507,7 @@ def _get_elements_and_details(self, path):
507507
parent_to_obj_elem, parent_to_obj_action = elements[-2]
508508
obj = self._get_elem_and_compare_to_old_value(
509509
obj=parent, path_for_err_reporting=path, expected_old_value=None,
510-
elem=parent_to_obj_elem, action=parent_to_obj_action, next_element=next2_element)
510+
elem=parent_to_obj_elem, action=parent_to_obj_action, next_element=next2_element) # type: ignore
511511
else:
512512
# parent = self
513513
# obj = self.root
@@ -516,7 +516,7 @@ def _get_elements_and_details(self, path):
516516
parent = parent_to_obj_elem = parent_to_obj_action = None
517517
obj = self
518518
# obj = self.get_nested_obj(obj=self, elements=elements[:-1])
519-
elem, action = elements[-1]
519+
elem, action = elements[-1] # type: ignore
520520
except Exception as e:
521521
self._raise_or_log(UNABLE_TO_GET_ITEM_MSG.format(path, e))
522522
return None
@@ -550,7 +550,7 @@ def _do_values_or_type_changed(self, changes, is_type_change=False, verify_chang
550550
else:
551551
new_value = new_type(current_old_value)
552552
except Exception as e:
553-
self._raise_or_log(TYPE_CHANGE_FAIL_MSG.format(obj[elem], value.get('new_type', 'unknown'), e))
553+
self._raise_or_log(TYPE_CHANGE_FAIL_MSG.format(obj[elem], value.get('new_type', 'unknown'), e)) # type: ignore
554554
continue
555555
else:
556556
new_value = value['new_value']
@@ -582,7 +582,7 @@ def _do_item_removed(self, items):
582582
current_old_value = not_found
583583
try:
584584
if action == GET:
585-
current_old_value = obj[elem]
585+
current_old_value = obj[elem] # type: ignore
586586
elif action == GETATTR:
587587
current_old_value = getattr(obj, elem)
588588
look_for_expected_old_value = current_old_value != expected_old_value
@@ -644,15 +644,15 @@ def _do_iterable_opcodes(self):
644644
transformed.extend(opcode.new_values)
645645
elif opcode.tag == 'equal':
646646
# Items are the same in both lists, so we add them to the result
647-
transformed.extend(obj[opcode.t1_from_index:opcode.t1_to_index])
647+
transformed.extend(obj[opcode.t1_from_index:opcode.t1_to_index]) # type: ignore
648648
if is_obj_tuple:
649-
obj = tuple(obj)
649+
obj = tuple(obj) # type: ignore
650650
# Making sure that the object is re-instated inside the parent especially if it was immutable
651651
# and we had to turn it into a mutable one. In such cases the object has a new id.
652652
self._simple_set_elem_value(obj=parent, path_for_err_reporting=path, elem=parent_to_obj_elem,
653653
value=obj, action=parent_to_obj_action)
654654
else:
655-
obj[:] = transformed
655+
obj[:] = transformed # type: ignore
656656

657657

658658

@@ -745,7 +745,7 @@ def _do_ignore_order(self):
745745
fixed_indexes = self.diff.get('iterable_items_added_at_indexes', dict_())
746746
remove_indexes = self.diff.get('iterable_items_removed_at_indexes', dict_())
747747
paths = SetOrdered(fixed_indexes.keys()) | SetOrdered(remove_indexes.keys())
748-
for path in paths:
748+
for path in paths: # type: ignore
749749
# In the case of ignore_order reports, we are pointing to the container object.
750750
# Thus we add a [0] to the elements so we can get the required objects and discard what we don't need.
751751
elem_and_details = self._get_elements_and_details("{}[0]".format(path))
@@ -1021,7 +1021,7 @@ def _from_flat_dicts(flat_dict_list):
10211021
result['_iterable_opcodes'][path_str] = []
10221022
result['_iterable_opcodes'][path_str].append(
10231023
Opcode(
1024-
tag=FLAT_DATA_ACTION_TO_OPCODE_TAG[action],
1024+
tag=FLAT_DATA_ACTION_TO_OPCODE_TAG[action], # type: ignore
10251025
t1_from_index=flat_dict.get('t1_from_index'),
10261026
t1_to_index=flat_dict.get('t1_to_index'),
10271027
t2_from_index=flat_dict.get('t2_from_index'),
@@ -1091,7 +1091,7 @@ def to_flat_dicts(self, include_action_in_path=False, report_type_changes=True)
10911091
"""
10921092
return [
10931093
i._asdict() for i in self.to_flat_rows(include_action_in_path=False, report_type_changes=True)
1094-
]
1094+
] # type: ignore
10951095

10961096
def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -> List[FlatDeltaRow]:
10971097
"""
@@ -1141,13 +1141,13 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
11411141
for index, value in index_to_value.items():
11421142
path2 = path.copy()
11431143
if include_action_in_path:
1144-
path2.append((index, 'GET'))
1144+
path2.append((index, 'GET')) # type: ignore
11451145
else:
11461146
path2.append(index)
11471147
if report_type_changes:
1148-
row = FlatDeltaRow(path=path2, value=value, action=new_action, type=type(value))
1148+
row = FlatDeltaRow(path=path2, value=value, action=new_action, type=type(value)) # type: ignore
11491149
else:
1150-
row = FlatDeltaRow(path=path2, value=value, action=new_action)
1150+
row = FlatDeltaRow(path=path2, value=value, action=new_action) # type: ignore
11511151
result.append(row)
11521152
elif action in {'set_item_added', 'set_item_removed'}:
11531153
for path, values in info.items():
@@ -1167,15 +1167,15 @@ def to_flat_rows(self, include_action_in_path=False, report_type_changes=True) -
11671167
value = value[new_key]
11681168
elif isinstance(value, (list, tuple)) and len(value) == 1:
11691169
value = value[0]
1170-
path.append(0)
1170+
path.append(0) # type: ignore
11711171
action = 'iterable_item_added'
11721172
elif isinstance(value, set) and len(value) == 1:
11731173
value = value.pop()
11741174
action = 'set_item_added'
11751175
if report_type_changes:
1176-
row = FlatDeltaRow(path=path, value=value, action=action, type=type(value))
1176+
row = FlatDeltaRow(path=path, value=value, action=action, type=type(value)) # type: ignore
11771177
else:
1178-
row = FlatDeltaRow(path=path, value=value, action=action)
1178+
row = FlatDeltaRow(path=path, value=value, action=action) # type: ignore
11791179
result.append(row)
11801180
elif action in {
11811181
'dictionary_item_removed', 'iterable_item_added',

deepdiff/diff.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from inspect import getmembers
1919
from itertools import zip_longest
2020
from functools import lru_cache
21-
from deepdiff.helper import (strings, bytes_type, numbers, uuids, datetimes, ListItemRemovedOrAdded, notpresent,
21+
from deepdiff.helper import (strings, bytes_type, numbers, uuids, ListItemRemovedOrAdded, notpresent,
2222
IndexedHash, unprocessed, add_to_frozen_set, basic_types,
2323
convert_item_or_items_into_set_else_none, get_type,
2424
convert_item_or_items_into_compiled_regexes_else_none,
@@ -131,7 +131,7 @@ def __init__(self,
131131
encodings: Optional[List[str]]=None,
132132
exclude_obj_callback: Optional[Callable]=None,
133133
exclude_obj_callback_strict: Optional[Callable]=None,
134-
exclude_paths: Union[str, List[str]]=None,
134+
exclude_paths: Union[str, List[str], None]=None,
135135
exclude_regex_paths: Union[str, List[str], Pattern[str], List[Pattern[str]], None]=None,
136136
exclude_types: Optional[List[Any]]=None,
137137
get_deep_distance: bool=False,
@@ -151,7 +151,7 @@ def __init__(self,
151151
ignore_type_subclasses: bool=False,
152152
include_obj_callback: Optional[Callable]=None,
153153
include_obj_callback_strict: Optional[Callable]=None,
154-
include_paths: Union[str, List[str]]=None,
154+
include_paths: Union[str, List[str], None]=None,
155155
iterable_compare_func: Optional[Callable]=None,
156156
log_frequency_in_sec: int=0,
157157
math_epsilon: Optional[float]=None,

0 commit comments

Comments
 (0)