Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a6d264c
Fix assert_equal with check_dim_order=False for Datasets with mixed d…
max-sixty Sep 7, 2025
d3f685d
Merge branch 'main' into 10704
max-sixty Sep 7, 2025
4e6e976
Merge branch 'main' into 10704
max-sixty Sep 8, 2025
96299cc
Address review feedback: avoid sorting non-sortable dimension names
max-sixty Sep 8, 2025
87c2065
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 8, 2025
dad4370
Add full test coverage for maybe_transpose_dims changes
max-sixty Sep 8, 2025
5567340
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 8, 2025
358032b
Fix DataTree handling in maybe_transpose_dims
max-sixty Sep 8, 2025
317a9ea
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 8, 2025
6246928
Trigger CI re-run for flaky Zarr test
max-sixty Sep 8, 2025
e22aaf6
Merge branch 'main' into 10704
max-sixty Sep 15, 2025
e42e782
Merge branch 'main' into 10704
max-sixty Oct 8, 2025
84cf5c3
Merge branch 'main' into 10704
max-sixty Nov 25, 2025
51084f3
Merge branch 'main' into 10704
max-sixty Dec 16, 2025
219d5c1
Simplify DataTree handling in maybe_transpose_dims
max-sixty Dec 16, 2025
d243e16
Fix RTD builds by restricting test-nightly to linux/macOS platforms
max-sixty Dec 16, 2025
3bfba9a
Merge remote-tracking branch 'origin/rtd' into 10704
max-sixty Dec 16, 2025
e5757f9
Merge remote-tracking branch 'origin/main' into 10704
max-sixty Dec 29, 2025
a688f4c
Remove redundant numpy imports in test functions
max-sixty Dec 29, 2025
7bfb7f2
Merge branch 'main' into 10704
mathause Apr 20, 2026
bfa4a98
Merge branch 'main' into 10704
mathause Apr 22, 2026
64fe918
Merge branch 'main' into 10704
mathause Apr 24, 2026
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
34 changes: 22 additions & 12 deletions xarray/testing/assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,33 @@ def assert_isomorphic(a: DataTree, b: DataTree):


def maybe_transpose_dims(a, b, check_dim_order: bool):
"""Helper for assert_equal/allclose/identical"""
"""Helper for assert_equal/allclose/identical

Returns (a, b) tuple with dimensions transposed to canonical order if needed.
"""

__tracebackhide__ = True

if check_dim_order:
return a, b

def _maybe_transpose_dims(a, b):
if not isinstance(a, Variable | DataArray | Dataset):
return b
if set(a.dims) == set(b.dims):
# Ensure transpose won't fail if a dimension is missing
# If this is the case, the difference will be caught by the caller
return b.transpose(*a.dims)
return b

if check_dim_order:
return b
return a, b

# Find common dimensions and transpose both to canonical order
common_dims = set(a.dims) & set(b.dims)
if common_dims:
# Use order from the intersection, with ellipsis for any unique dims
canonical_order = list(common_dims) + [...]
# For Datasets, we need to transpose both to the same order
# For Variable/DataArray, we could just transpose b, but for consistency
# and simplicity we transpose both
return a.transpose(*canonical_order), b.transpose(*canonical_order)
return a, b

if isinstance(a, DataTree):
# map_over_datasets supports tuple returns and unpacks them automatically
return map_over_datasets(_maybe_transpose_dims, a, b)

return _maybe_transpose_dims(a, b)
Expand Down Expand Up @@ -140,7 +150,7 @@ def assert_equal(a, b, check_dim_order: bool = True):
assert type(a) is type(b) or (
isinstance(a, Coordinates) and isinstance(b, Coordinates)
)
b = maybe_transpose_dims(a, b, check_dim_order)
a, b = maybe_transpose_dims(a, b, check_dim_order)
if isinstance(a, Variable | DataArray):
assert a.equals(b), formatting.diff_array_repr(a, b, "equals")
elif isinstance(a, Dataset):
Expand Down Expand Up @@ -228,7 +238,7 @@ def assert_allclose(
"""
__tracebackhide__ = True
assert type(a) is type(b)
b = maybe_transpose_dims(a, b, check_dim_order)
a, b = maybe_transpose_dims(a, b, check_dim_order)

equiv = functools.partial(
_data_allclose_or_equiv, rtol=rtol, atol=atol, decode_bytes=decode_bytes
Expand Down
115 changes: 115 additions & 0 deletions xarray/tests/test_assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,26 @@ def test_assert_equal_transpose_datatree() -> None:

xr.testing.assert_equal(a, b, check_dim_order=False)

# Test with mixed dimension orders in datasets (the tricky case)
ds_mixed = xr.Dataset(
{
"foo": xr.DataArray(np.zeros([4, 5]), dims=("a", "b")),
"bar": xr.DataArray(np.ones([5, 4]), dims=("b", "a")),
}
)
ds_mixed2 = xr.Dataset(
{
"foo": xr.DataArray(np.zeros([5, 4]), dims=("b", "a")),
"bar": xr.DataArray(np.ones([4, 5]), dims=("a", "b")),
}
)

tree1 = xr.DataTree.from_dict({"node": ds_mixed})
tree2 = xr.DataTree.from_dict({"node": ds_mixed2})

# Should work with check_dim_order=False
xr.testing.assert_equal(tree1, tree2, check_dim_order=False)


@pytest.mark.filterwarnings("error")
@pytest.mark.parametrize(
Expand Down Expand Up @@ -241,6 +261,101 @@ def __array__(
assert len(w) == 0


def test_assert_equal_dataset_check_dim_order():
"""Test for issue #10704 - check_dim_order=False with Datasets containing mixed dimension orders."""
# Dataset with variables having different dimension orders
dataset_1 = xr.Dataset(
{
"foo": xr.DataArray(np.zeros([4, 5]), dims=("a", "b")),
"bar": xr.DataArray(np.ones([5, 4]), dims=("b", "a")),
}
)

dataset_2 = xr.Dataset(
{
"foo": xr.DataArray(np.zeros([5, 4]), dims=("b", "a")),
"bar": xr.DataArray(np.ones([4, 5]), dims=("a", "b")),
}
)

# These should be equal when ignoring dimension order
xr.testing.assert_equal(dataset_1, dataset_2, check_dim_order=False)
xr.testing.assert_allclose(dataset_1, dataset_2, check_dim_order=False)

# Should also work when comparing dataset to itself
xr.testing.assert_equal(dataset_1, dataset_1, check_dim_order=False)
xr.testing.assert_allclose(dataset_1, dataset_1, check_dim_order=False)

# But should fail with check_dim_order=True
with pytest.raises(AssertionError):
xr.testing.assert_equal(dataset_1, dataset_2, check_dim_order=True)
with pytest.raises(AssertionError):
xr.testing.assert_allclose(dataset_1, dataset_2, check_dim_order=True)

# Test with non-sortable dimension names (int and str)
dataset_mixed_1 = xr.Dataset(
{
"foo": xr.DataArray(np.zeros([4, 5]), dims=(1, "b")),
"bar": xr.DataArray(np.ones([5, 4]), dims=("b", 1)),
}
)

dataset_mixed_2 = xr.Dataset(
{
"foo": xr.DataArray(np.zeros([5, 4]), dims=("b", 1)),
"bar": xr.DataArray(np.ones([4, 5]), dims=(1, "b")),
}
)

# Should work with mixed types when ignoring dimension order
xr.testing.assert_equal(dataset_mixed_1, dataset_mixed_2, check_dim_order=False)
xr.testing.assert_equal(dataset_mixed_1, dataset_mixed_1, check_dim_order=False)


def test_assert_equal_no_common_dims():
"""Test assert_equal when objects have no common dimensions."""
# DataArrays with completely different dimensions
da1 = xr.DataArray(np.zeros([4, 5]), dims=("x", "y"))
da2 = xr.DataArray(np.zeros([3, 2]), dims=("a", "b"))

# Should fail even with check_dim_order=False since dims are different
with pytest.raises(AssertionError):
xr.testing.assert_equal(da1, da2, check_dim_order=False)

# Datasets with no common dimensions
ds1 = xr.Dataset(
{
"foo": xr.DataArray(np.zeros([4]), dims=("x",)),
"bar": xr.DataArray(np.ones([5]), dims=("y",)),
}
)
ds2 = xr.Dataset(
{
"foo": xr.DataArray(np.zeros([3]), dims=("a",)),
"bar": xr.DataArray(np.ones([2]), dims=("b",)),
}
)

# Should fail since dimensions are completely different
with pytest.raises(AssertionError):
xr.testing.assert_equal(ds1, ds2, check_dim_order=False)


def test_assert_equal_variable_transpose():
"""Test assert_equal with transposed Variable objects."""
# Variables with transposed dimensions
var1 = xr.Variable(("x", "y"), np.zeros([4, 5]))
var2 = xr.Variable(("y", "x"), np.zeros([5, 4]))

# Should fail with check_dim_order=True
with pytest.raises(AssertionError):
xr.testing.assert_equal(var1, var2, check_dim_order=True)

# Should pass with check_dim_order=False
xr.testing.assert_equal(var1, var2, check_dim_order=False)
xr.testing.assert_allclose(var1, var2, check_dim_order=False)


class CustomIndex(Index):
"""Custom index without equals() implementation for testing."""

Expand Down
Loading