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
11 changes: 11 additions & 0 deletions deepdiff/delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,17 @@ def _do_type_changes(self):
self._do_values_or_type_changed(type_changes, is_type_change=True)

def _do_post_process(self):
# Whole-array replacements (including scalar-shaped arrays) bypass
# iterable preprocessing, but still need their recorded dtype restored.
if self._numpy_paths:
for path, type_ in self._numpy_paths.items():
if path in self.diff.get('values_changed', {}) or path in self.diff.get('type_changes', {}):
try:
dtype = numpy_dtype_string_to_type(type_)
except Exception as e:
self._raise_or_log(NOT_VALID_NUMPY_TYPE.format(e))
continue
self.post_process_paths_to_convert[path] = {'old_type': list, 'new_type': dtype}
if self.post_process_paths_to_convert:
# Example: We had converted some object to be mutable and now we are converting them back to be immutable.
# We don't need to check the change because it is not really a change that was part of the original diff.
Expand Down
9 changes: 9 additions & 0 deletions deepdiff/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -2013,6 +2013,15 @@ def _diff_numpy_array(self, level, parents_ids=frozenset(), local_tree=None):
# which means numpy module needs to be available. So np can't be None.
raise ImportError(CANT_FIND_NUMPY_MSG) # pragma: no cover

# A zero-dimensional array contains a scalar and cannot be iterated.
# Dispatch its Python value normally, also handling scalar/array shape
# changes instead of passing a scalar into the iterable comparison.
if level.t1.ndim == 0 or level.t2.ndim == 0:
level.t1 = level.t1.tolist()
level.t2 = level.t2.tolist()
self._diff(level, parents_ids, local_tree=local_tree)
return

if (self.ignore_order_func and not self.ignore_order_func(level)) or not self.ignore_order:
# fast checks
if self.significant_digits is None:
Expand Down
51 changes: 50 additions & 1 deletion tests/test_diff_numpy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from deepdiff import DeepDiff
from deepdiff import DeepDiff, Delta
from deepdiff.helper import np
from tests import parameterize_cases

Expand Down Expand Up @@ -174,3 +174,52 @@ class TestNumpy:
def test_numpy(self, test_name, t1, t2, deepdiff_kwargs, expected_result):
diff = DeepDiff(t1, t2, **deepdiff_kwargs)
assert expected_result == diff, f"test_numpy {test_name} failed."


@pytest.mark.parametrize('ignore_order', [False, True])
@pytest.mark.parametrize('before, after', [(1, 2), (1.5, 2.5), ('a', 'b'), (True, False)])
def test_zero_dimensional_array_values(before, after, ignore_order):
options = {'ignore_order': ignore_order}
assert DeepDiff(np.array(before), np.array(after), **options) == {
'values_changed': {'root': {'old_value': before, 'new_value': after}}
}
assert not DeepDiff(np.array(before), np.array(before), **options)
assert DeepDiff({'value': np.array(before)}, {'value': np.array(after)}, **options) == {
'values_changed': {"root['value']": {'old_value': before, 'new_value': after}}
}


@pytest.mark.parametrize('ignore_order', [False, True])
def test_zero_dimensional_array_comparison_options(ignore_order):
assert not DeepDiff(np.array(1.01), np.array(1.02), significant_digits=1, ignore_order=ignore_order)
assert not DeepDiff(np.array(1.01), np.array(1.02), math_epsilon=0.1, ignore_order=ignore_order)
assert not DeepDiff(np.array(float('nan')), np.array(float('nan')),
ignore_nan_inequality=True, ignore_order=ignore_order)
assert not DeepDiff(np.array(1), np.array(1.0), ignore_numeric_type_changes=True, ignore_order=ignore_order)


@pytest.mark.parametrize('before, after', [(1, 2), (1.5, 2.5), (True, False), (1, [1]), ([1], 1)])
def test_zero_dimensional_array_delta(before, after):
t1, t2 = np.array(before), np.array(after)
result = Delta(DeepDiff(t1, t2)) + t1
assert isinstance(result, np.ndarray)
assert result.shape == t2.shape
np.testing.assert_array_equal(result, t2)


def test_nested_zero_dimensional_array_delta():
t1 = {'value': np.array(1)}
t2 = {'value': np.array(2)}
result = Delta(DeepDiff(t1, t2)) + t1
assert isinstance(result['value'], np.ndarray)
assert result['value'].shape == ()
np.testing.assert_array_equal(result['value'], t2['value'])


def test_zero_dimensional_array_delta_rejects_invalid_dtype():
from deepdiff.delta import DeltaError

delta = Delta({'values_changed': {'root': {'new_value': 2}},
'_numpy_paths': {'root': 'invalid_dtype'}}, raise_errors=True)
with pytest.raises(DeltaError, match='not a valid numpy type'):
delta + np.array(1)