diff --git a/pandas/_libs/testing.pyx b/pandas/_libs/testing.pyx index 7a2fa471b9ba8..1b55b9d3819e9 100644 --- a/pandas/_libs/testing.pyx +++ b/pandas/_libs/testing.pyx @@ -99,7 +99,9 @@ cpdef assert_almost_equal(a, b, return True a_is_ndarray = is_array(a) + a_has_size_and_shape = hasattr(a, "size") and hasattr(a, "shape") b_is_ndarray = is_array(b) + b_has_size_and_shape = hasattr(b, "size") and hasattr(b, "shape") if obj is None: if a_is_ndarray or b_is_ndarray: @@ -119,7 +121,7 @@ cpdef assert_almost_equal(a, b, f"Can't compare objects without length, one or both is invalid: ({a}, {b})" ) - if a_is_ndarray and b_is_ndarray: + if (a_is_ndarray and b_is_ndarray) or (a_has_size_and_shape and b_has_size_and_shape): na, nb = a.size, b.size if a.shape != b.shape: from pandas._testing import raise_assert_detail diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 0f4cef772458f..ccd8d2d782930 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -60,6 +60,52 @@ def coerce(request): return request.param +class MockNumpyLikeArray: + """ + A class which is numpy-like (e.g. Pint's Quantity) but not actually numpy + + The key is that it is not actually a numpy array so + ``util.is_array(mock_numpy_like_array_instance)`` returns ``False``. Other + important properties are that the class defines a :meth:`__iter__` method + (so that ``isinstance(abc.Iterable)`` returns ``True``) and has a + :meth:`ndim` property which can be used as a check for whether it is a + scalar or not. + """ + + def __init__(self, values): + self._values = values + + def __iter__(self): + iter_values = iter(self._values) + + def it_outer(): + yield from iter_values + + return it_outer() + + def __len__(self): + return len(self._values) + + def __array__(self, t=None): + return self._values + + @property + def ndim(self): + return self._values.ndim + + @property + def dtype(self): + return self._values.dtype + + @property + def size(self): + return self._values.size + + @property + def shape(self): + return self._values.shape + + # collect all objects to be tested for list-like-ness; use tuples of objects, # whether they are list-like or not (special casing for sets), and their ID ll_params = [ @@ -166,6 +212,17 @@ class DtypeList(list): assert not inference.is_array_like(123) +@pytest.mark.parametrize( + "eg", + ( + np.array(2), + MockNumpyLikeArray(np.array(2)), + ), +) +def test_assert_almost_equal(eg): + tm.assert_almost_equal(eg, eg) + + @pytest.mark.parametrize( "inner", [