Skip to content

Handle zero-dimensional NumPy array comparisons and replacements - #626

Open
jackwalkerlabs wants to merge 1 commit into
qlustered:devfrom
jackwalkerlabs:codex/compare-zero-dimensional-arrays
Open

jackwalkerlabs wants to merge 1 commit into
qlustered:devfrom
jackwalkerlabs:codex/compare-zero-dimensional-arrays

Conversation

@jackwalkerlabs

Copy link
Copy Markdown

Compare zero-dimensional NumPy arrays as scalar values instead of trying to iterate them. Handle scalar/array shape changes through the normal value/type comparison path, and restore the recorded NumPy dtype when a Delta replaces a whole array.

Fixes #463.

Validation: all 13 initial regression cases fail on the base. Final broader suite: 1289 passed, 44 skipped, one deselected. Coverage includes equal/changed integers, floats, strings and booleans; nested arrays; ignore-order, precision, epsilon and NaN options; numeric/bool Delta round trips, scalar/vector shape changes, nested replacements, and invalid dtype rejection. The excluded RLIMIT_AS security test fails on this macOS host on the base too. Existing source lint violations remain; git diff --check passes. String-array Delta serialization still has the existing unsupported str_ dtype limitation; string comparison itself is covered.

AI disclosure: Implemented, reviewed, and tested by OpenAI Codex under the account owner’s authorization; no human code review is claimed.

@seperman seperman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review: #626 Handle zero-dimensional NumPy array comparisons and replacements

Summary: _diff_numpy_array now converts zero-dimensional arrays to Python values with tolist() and passes them back through _diff. So DeepDiff(np.array(1), np.array(2)) reports values_changed instead of raising "iteration over a 0-d array". Delta._do_post_process also restores the recorded dtype when a whole-array path is replaced through values_changed/type_changes. This fixes the reproduction in #463.

Overlap with other PRs

  • I found no other open or merged PR touching _diff_numpy_array or the numpy post-processing in Delta.
  • The related open issue #332 (DeepHash fails on 0-d arrays) is not covered. DeepHash has no ndarray special case, so a 0-d array still goes to _prep_iterable, and enumerate() raises. In practice DeepDiff([np.array(1)], [np.array(2)], ignore_order=True) likely still raises. The new ignore_order tests use top-level arrays or dict values, which are never hashed.

Security

  • _numpy_paths values from a delta, which may be untrusted, are only resolved through numpy_dtype_string_to_type. That is a fixed list of numeric, bool and datetime64 types (helper.py:102-104, 438-439), and unknown names go to _raise_or_log. This is the same pattern as the existing _do_pre_process, and nothing new is constructed from input.

No security concerns found in static review.

Correctness

  • Regression (deepdiff/diff.py:2019-2021): the new branch assumes both sides are ndarrays. _diff only checks isinstance(level.t1, np_ndarray), and when type groups are ignored, t2 can be a plain Python number. get_type(np.array(5)) is np.int64, which is in the numbers group, so DeepDiff(np.array(5), 5, ignore_numeric_type_changes=True) reaches _diff_numpy_array.
    • On dev, the np.array_equal fast path returns no diff.
    • With this change, level.t1.ndim == 0 is true and level.t2.tolist() raises AttributeError.
    • When the values differ, dev already failed at .shape.
  • Passing the same level back through _diff (diff.py:2022) runs _count_diff(), _use_custom_operator() and _skip_this() a second time. The effects are small: one extra count toward max_diffs, and custom operators and include/exclude callbacks now also see the unwrapped Python values. Still worth a comment, or calling the scalar handling more directly.
  • Delta (deepdiff/delta.py:504-512): this only applies when a _numpy_paths key is itself in values_changed/type_changes. All existing numpy Delta cases in tests/test_delta.py report at child paths such as root[2], so I don't expect them to change. Arrays with different dtypes are reported as type_changes in _diff before _diff_numpy_array runs, so _numpy_paths isn't recorded for them. That is consistent.
  • For 0-d string or object arrays, _numpy_paths records str_/object_, which are not in numpy_dtype_str_to_type. Applying the delta then logs NOT_VALID_NUMPY_TYPE, or raises DeltaError with raise_errors=True, and leaves a plain Python value instead of an ndarray. The description mentions a str_ limitation for serialization; this also affects applying the delta.
  • Minor: with ignore_numeric_type_changes, the recorded dtype is t2's, so t2 - delta restores t2's dtype. The existing pre-processing has the same limitation.

Tests

  • tests/test_diff_numpy.py:179-226 covers changed and equal values; significant_digits, math_epsilon, NaN and numeric-type options; Delta round-trips including scalar↔vector shape changes; nested arrays; and an invalid dtype. These should all fail on dev: DeepDiff raises, and dev's post-processing never reads _numpy_paths, so the invalid-dtype test fails too.
  • Not covered:
    • a list of 0-d arrays with ignore_order=True (the DeepHash path)
    • an ndarray against a Python scalar under ignore_numeric_type_changes
    • t2 - delta (reverse application)

Suggestions

  • Guard the new branch for non-array t2, e.g. only call .tolist() on values that have it, or check getattr(level.t2, 'ndim', None). Add a test for DeepDiff(np.array(5), 5, ignore_numeric_type_changes=True).
  • Consider handling 0-d arrays in DeepHash as well (#332), so ignore_order on containers of 0-d arrays works.
  • For dtypes outside the allowlist, consider skipping the restore with a debug message instead of logging an error.

AI-assisted static review; the code was not executed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants