22
33import collections .abc
44import copy
5+ import inspect
56from collections import defaultdict
6- from collections .abc import Hashable , Iterable , Iterator , Mapping , Sequence
7- from typing import TYPE_CHECKING , Any , Generic , TypeVar , cast
7+ from collections .abc import Callable , Hashable , Iterable , Iterator , Mapping , Sequence
8+ from typing import TYPE_CHECKING , Any , Generic , TypeVar , cast , overload
89
910import numpy as np
1011import pandas as pd
@@ -348,7 +349,15 @@ def reindex_like(self, other: Self) -> dict[Hashable, Any]:
348349 """
349350 raise NotImplementedError (f"{ self !r} doesn't support re-indexing labels" )
350351
351- def equals (self , other : Index ) -> bool :
352+ @overload
353+ def equals (self , other : Index ) -> bool : ...
354+
355+ @overload
356+ def equals (
357+ self , other : Index , * , exclude : frozenset [Hashable ] | None = None
358+ ) -> bool : ...
359+
360+ def equals (self , other : Index , ** kwargs ) -> bool :
352361 """Compare this index with another index of the same type.
353362
354363 Implementation is optional but required in order to support alignment.
@@ -357,11 +366,22 @@ def equals(self, other: Index) -> bool:
357366 ----------
358367 other : Index
359368 The other Index object to compare with this object.
369+ exclude : frozenset of hashable, optional
370+ Dimensions excluded from checking. It is None by default, (i.e.,
371+ when this method is not called in the context of alignment). For a
372+ n-dimensional index this option allows an Index to optionally ignore
373+ any dimension in ``exclude`` when comparing ``self`` with ``other``.
374+ For a 1-dimensional index this kwarg can be safely ignored, as this
375+ method is not called when all of the index's dimensions are also
376+ excluded from alignment (note: the index's dimensions correspond to
377+ the union of the dimensions of all coordinate variables associated
378+ with this index).
360379
361380 Returns
362381 -------
363382 is_equal : bool
364383 ``True`` if the indexes are equal, ``False`` otherwise.
384+
365385 """
366386 raise NotImplementedError ()
367387
@@ -863,7 +883,7 @@ def sel(
863883
864884 return IndexSelResult ({self .dim : indexer })
865885
866- def equals (self , other : Index ):
886+ def equals (self , other : Index , * , exclude : frozenset [ Hashable ] | None = None ):
867887 if not isinstance (other , PandasIndex ):
868888 return False
869889 return self .index .equals (other .index ) and self .dim == other .dim
@@ -1542,10 +1562,12 @@ def sel(
15421562
15431563 return IndexSelResult (results )
15441564
1545- def equals (self , other : Index ) -> bool :
1565+ def equals (
1566+ self , other : Index , * , exclude : frozenset [Hashable ] | None = None
1567+ ) -> bool :
15461568 if not isinstance (other , CoordinateTransformIndex ):
15471569 return False
1548- return self .transform .equals (other .transform )
1570+ return self .transform .equals (other .transform , exclude = exclude )
15491571
15501572 def rename (
15511573 self ,
@@ -1925,6 +1947,36 @@ def default_indexes(
19251947 return indexes
19261948
19271949
1950+ def _wrap_index_equals (
1951+ index : Index ,
1952+ ) -> Callable [[Index , frozenset [Hashable ]], bool ]:
1953+ # TODO: remove this Index.equals() wrapper (backward compatibility)
1954+
1955+ sig = inspect .signature (index .equals )
1956+
1957+ if len (sig .parameters ) == 1 :
1958+ index_cls_name = type (index ).__module__ + "." + type (index ).__qualname__
1959+ emit_user_level_warning (
1960+ f"the signature ``{ index_cls_name } .equals(self, other)`` is deprecated. "
1961+ f"Please update it to "
1962+ f"``{ index_cls_name } .equals(self, other, *, exclude=None)`` "
1963+ "or kindly ask the maintainers of ``{index_cls_name}`` to do it. "
1964+ "See documentation of xarray.Index.equals() for more info." ,
1965+ FutureWarning ,
1966+ )
1967+ exclude_kwarg = False
1968+ else :
1969+ exclude_kwarg = True
1970+
1971+ def equals_wrapper (other : Index , exclude : frozenset [Hashable ]) -> bool :
1972+ if exclude_kwarg :
1973+ return index .equals (other , exclude = exclude )
1974+ else :
1975+ return index .equals (other )
1976+
1977+ return equals_wrapper
1978+
1979+
19281980def indexes_equal (
19291981 index : Index ,
19301982 other_index : Index ,
@@ -1966,6 +2018,7 @@ def indexes_equal(
19662018
19672019def indexes_all_equal (
19682020 elements : Sequence [tuple [Index , dict [Hashable , Variable ]]],
2021+ exclude_dims : frozenset [Hashable ],
19692022) -> bool :
19702023 """Check if indexes are all equal.
19712024
@@ -1990,9 +2043,11 @@ def check_variables():
19902043
19912044 same_type = all (type (indexes [0 ]) is type (other_idx ) for other_idx in indexes [1 :])
19922045 if same_type :
2046+ index_equals_func = _wrap_index_equals (indexes [0 ])
19932047 try :
19942048 not_equal = any (
1995- not indexes [0 ].equals (other_idx ) for other_idx in indexes [1 :]
2049+ not index_equals_func (other_idx , exclude_dims )
2050+ for other_idx in indexes [1 :]
19962051 )
19972052 except NotImplementedError :
19982053 not_equal = check_variables ()
0 commit comments