From 0ecb8c2d029bc4192f24d6da1bd2b213c0b3d6bc Mon Sep 17 00:00:00 2001 From: Zebedee Nicholls Date: Sat, 13 Feb 2021 19:18:29 +1100 Subject: [PATCH 1/2] Add failing test --- pandas/_libs/testing.pyx | 4 ++- pandas/tests/dtypes/test_inference.py | 52 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) 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..7f67c566ef1e8 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -60,6 +60,50 @@ 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(): + for element in iter_values: + yield element + + return it_outer() + + def __len__(self): + return len(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 +210,14 @@ 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", [ From ad5f714b794bc58c9c18aeb1ba8ff51b782b0527 Mon Sep 17 00:00:00 2001 From: Zebedee Nicholls Date: Sat, 13 Feb 2021 19:38:07 +1100 Subject: [PATCH 2/2] Add __array__ method to mock numpy-like --- pandas/tests/dtypes/test_inference.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 7f67c566ef1e8..ccd8d2d782930 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -79,14 +79,16 @@ def __iter__(self): iter_values = iter(self._values) def it_outer(): - for element in iter_values: - yield element + 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 @@ -210,10 +212,13 @@ class DtypeList(list): assert not inference.is_array_like(123) -@pytest.mark.parametrize("eg", ( - np.array(2), - MockNumpyLikeArray(np.array(2)), -)) +@pytest.mark.parametrize( + "eg", + ( + np.array(2), + MockNumpyLikeArray(np.array(2)), + ), +) def test_assert_almost_equal(eg): tm.assert_almost_equal(eg, eg)