From 4cc26686579f6db6c4c681156c89f0151bc24de6 Mon Sep 17 00:00:00 2001 From: moink Date: Fri, 25 Dec 2020 15:24:29 +0100 Subject: [PATCH 1/5] TST: GH30999 Add match=msg to all pytest.raises in tests/indexes. Reactivate half a test in pandas/tests/indexes/interval/test_astype.py --- pandas/tests/indexes/interval/test_astype.py | 19 +++++-------- pandas/tests/indexes/multi/test_indexing.py | 2 +- pandas/tests/indexes/multi/test_setops.py | 6 ++-- pandas/tests/indexes/period/test_indexing.py | 11 +++++-- pandas/tests/indexes/test_common.py | 15 ++++++++-- pandas/tests/indexes/test_numpy_compat.py | 30 ++++++++++++++------ 6 files changed, 53 insertions(+), 30 deletions(-) diff --git a/pandas/tests/indexes/interval/test_astype.py b/pandas/tests/indexes/interval/test_astype.py index 34ce810e32273..1e74ed31c47ce 100644 --- a/pandas/tests/indexes/interval/test_astype.py +++ b/pandas/tests/indexes/interval/test_astype.py @@ -1,3 +1,5 @@ +import re + import numpy as np import pytest @@ -153,22 +155,15 @@ def test_subtype_integer(self, subtype): with pytest.raises(ValueError, match=msg): index.insert(0, np.nan).astype(dtype) - @pytest.mark.xfail(reason="GH#15832") def test_subtype_integer_errors(self): # float64 -> uint64 fails with negative values index = interval_range(-10.0, 10.0) dtype = IntervalDtype("uint64") - with pytest.raises(ValueError): - index.astype(dtype) - - # float64 -> integer-like fails with non-integer valued floats - index = interval_range(0.0, 10.0, freq=0.25) - dtype = IntervalDtype("int64") - with pytest.raises(ValueError): - index.astype(dtype) - - dtype = IntervalDtype("uint64") - with pytest.raises(ValueError): + msg = re.escape( + "Cannot convert interval[float64] to interval[uint64]; subtypes are " + "incompatible" + ) + with pytest.raises(TypeError, match=msg): index.astype(dtype) @pytest.mark.parametrize("subtype", ["datetime64[ns]", "timedelta64[ns]"]) diff --git a/pandas/tests/indexes/multi/test_indexing.py b/pandas/tests/indexes/multi/test_indexing.py index 6bce89c520ce6..b6f131e5c1c9b 100644 --- a/pandas/tests/indexes/multi/test_indexing.py +++ b/pandas/tests/indexes/multi/test_indexing.py @@ -526,7 +526,7 @@ def test_get_loc_duplicates(self): xp = 0 assert rs == xp - with pytest.raises(KeyError): + with pytest.raises(KeyError, match="2"): index.get_loc(2) def test_get_loc_level(self): diff --git a/pandas/tests/indexes/multi/test_setops.py b/pandas/tests/indexes/multi/test_setops.py index e8d5baaef42d4..d5b29527ee08e 100644 --- a/pandas/tests/indexes/multi/test_setops.py +++ b/pandas/tests/indexes/multi/test_setops.py @@ -221,14 +221,12 @@ def test_difference_sort_incomparable(): tm.assert_index_equal(result, idx) -@pytest.mark.xfail(reason="Not implemented.") def test_difference_sort_incomparable_true(): - # TODO decide on True behaviour - # # sort=True, raises idx = pd.MultiIndex.from_product([[1, pd.Timestamp("2000"), 2], ["a", "b"]]) other = pd.MultiIndex.from_product([[3, pd.Timestamp("2000"), 4], ["c", "d"]]) - with pytest.raises(TypeError): + msg = "The 'sort' keyword only takes the values of None or False; True was passed." + with pytest.raises(ValueError, match=msg): idx.difference(other, sort=True) diff --git a/pandas/tests/indexes/period/test_indexing.py b/pandas/tests/indexes/period/test_indexing.py index c03c89f32f73e..9378a0ffc5f84 100644 --- a/pandas/tests/indexes/period/test_indexing.py +++ b/pandas/tests/indexes/period/test_indexing.py @@ -487,8 +487,15 @@ def test_get_indexer_mismatched_dtype_with_method(self, non_comparable_idx, meth other2 = other.astype(dtype) if dtype == "object" and isinstance(other, PeriodIndex): continue - # For object dtype we are liable to get a different exception message - with pytest.raises(TypeError): + # Two different error message patterns depending on dtypes + msg = "|".join( + re.escape(msg) + for msg in ( + f"Cannot compare dtypes {pi.dtype} and {other.dtype}", + " not supported between instances of ", + ) + ) + with pytest.raises(TypeError, match=msg): pi.get_indexer(other2, method=method) def test_get_indexer_non_unique(self): diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py index dce2e0172556a..bc1295cc0a0ce 100644 --- a/pandas/tests/indexes/test_common.py +++ b/pandas/tests/indexes/test_common.py @@ -39,7 +39,11 @@ def test_droplevel(self, index): if isinstance(index.name, tuple) and level is index.name: # GH 21121 : droplevel with tuple name continue - with pytest.raises(ValueError): + msg = ( + "Cannot remove 1 levels from an index with 1 levels: at least one " + "level must be left." + ) + with pytest.raises(ValueError, match=msg): index.droplevel(level) for level in "wrong", ["wrong"]: @@ -77,7 +81,11 @@ def test_constructor_unwraps_index(self, index): # FutureWarning from non-tuple sequence of nd indexing @pytest.mark.filterwarnings("ignore::FutureWarning") def test_getitem_error(self, index, itm): - with pytest.raises(IndexError): + msg = r"index 101 is out of bounds for axis 0 with size [\d]+|" + re.escape( + "only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) " + "and integer or boolean arrays are valid indices" + ) + with pytest.raises(IndexError, match=msg): index[itm] def test_to_flat_index(self, index): @@ -249,7 +257,8 @@ def test_searchsorted_monotonic(self, index): assert expected_right == ssm_right else: # non-monotonic should raise. - with pytest.raises(ValueError): + msg = "index must be monotonic increasing or decreasing" + with pytest.raises(ValueError, match=msg): index._searchsorted_monotonic(value, side="left") def test_pickle(self, index): diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py index 8bfb97ca494e6..4655914324ca8 100644 --- a/pandas/tests/indexes/test_numpy_compat.py +++ b/pandas/tests/indexes/test_numpy_compat.py @@ -49,8 +49,15 @@ def test_numpy_ufuncs_basic(index, func): # https://numpy.org/doc/stable/reference/ufuncs.html if isinstance(index, DatetimeIndexOpsMixin): - # raise TypeError or ValueError (PeriodIndex) - with pytest.raises(Exception): + # Two different error message patterns depending on the dtype + msg = ( + f"ufunc '{func.__name__}' not supported for the input types, and the " + "inputs could not be safely coerced to any supported types according to " + "the casting rule ''safe''" + f"|f ufunc does not support argument 0 of type .* which has no callable " + f"{func.__name__} method" + ) + with pytest.raises(TypeError, match=msg): with np.errstate(all="ignore"): func(index) elif isinstance(index, (Float64Index, Int64Index, UInt64Index)): @@ -66,7 +73,11 @@ def test_numpy_ufuncs_basic(index, func): if len(index) == 0: pass else: - with pytest.raises(Exception): + msg = ( + r"loop of ufunc does not support argument 0 of type .* which " + f"has no callable {func.__name__} method" + ) + with pytest.raises(TypeError, match=msg): with np.errstate(all="ignore"): func(index) @@ -77,6 +88,11 @@ def test_numpy_ufuncs_basic(index, func): def test_numpy_ufuncs_other(index, func, request): # test ufuncs of numpy, see: # https://numpy.org/doc/stable/reference/ufuncs.html + msg = ( + f"ufunc '{func.__name__}' not supported for the input types, and the " + "inputs could not be safely coerced to any supported types according " + "to the casting rule ''safe''" + ) if isinstance(index, (DatetimeIndex, TimedeltaIndex)): if isinstance(index, DatetimeIndex) and index.tz is not None: @@ -96,13 +112,11 @@ def test_numpy_ufuncs_other(index, func, request): result = func(index) assert isinstance(result, np.ndarray) else: - # raise TypeError or ValueError (PeriodIndex) - with pytest.raises(Exception): + with pytest.raises(TypeError, match=msg): func(index) elif isinstance(index, PeriodIndex): - # raise TypeError or ValueError (PeriodIndex) - with pytest.raises(Exception): + with pytest.raises(TypeError, match=msg): func(index) elif isinstance(index, (Float64Index, Int64Index, UInt64Index)): @@ -114,5 +128,5 @@ def test_numpy_ufuncs_other(index, func, request): if len(index) == 0: pass else: - with pytest.raises(Exception): + with pytest.raises(TypeError, match=msg): func(index) From 02cbc37ad326ba12f6c289a49bc1332cf34c2375 Mon Sep 17 00:00:00 2001 From: moink Date: Fri, 25 Dec 2020 20:49:57 +0100 Subject: [PATCH 2/5] TST: GH30999 Attempt to fix failing test test_numpy_ufuncs_basic --- pandas/tests/indexes/test_numpy_compat.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py index 4655914324ca8..ec5b62b056463 100644 --- a/pandas/tests/indexes/test_numpy_compat.py +++ b/pandas/tests/indexes/test_numpy_compat.py @@ -76,8 +76,9 @@ def test_numpy_ufuncs_basic(index, func): msg = ( r"loop of ufunc does not support argument 0 of type .* which " f"has no callable {func.__name__} method" + f"|object has no attribute {func.__name__}" ) - with pytest.raises(TypeError, match=msg): + with pytest.raises((TypeError, AttributeError), match=msg): with np.errstate(all="ignore"): func(index) From d968db18bee30d13cc42939a513d3dac58803bd6 Mon Sep 17 00:00:00 2001 From: moink Date: Sat, 26 Dec 2020 10:45:16 +0100 Subject: [PATCH 3/5] TST: GH30999 Change pytest.raises in test_numpy_ufuncs_basic to tm.external_error_raised --- pandas/tests/indexes/test_numpy_compat.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py index ec5b62b056463..be64bd8a29627 100644 --- a/pandas/tests/indexes/test_numpy_compat.py +++ b/pandas/tests/indexes/test_numpy_compat.py @@ -49,15 +49,7 @@ def test_numpy_ufuncs_basic(index, func): # https://numpy.org/doc/stable/reference/ufuncs.html if isinstance(index, DatetimeIndexOpsMixin): - # Two different error message patterns depending on the dtype - msg = ( - f"ufunc '{func.__name__}' not supported for the input types, and the " - "inputs could not be safely coerced to any supported types according to " - "the casting rule ''safe''" - f"|f ufunc does not support argument 0 of type .* which has no callable " - f"{func.__name__} method" - ) - with pytest.raises(TypeError, match=msg): + with tm.external_error_raised(TypeError): with np.errstate(all="ignore"): func(index) elif isinstance(index, (Float64Index, Int64Index, UInt64Index)): @@ -73,12 +65,7 @@ def test_numpy_ufuncs_basic(index, func): if len(index) == 0: pass else: - msg = ( - r"loop of ufunc does not support argument 0 of type .* which " - f"has no callable {func.__name__} method" - f"|object has no attribute {func.__name__}" - ) - with pytest.raises((TypeError, AttributeError), match=msg): + with tm.external_error_raised((TypeError, AttributeError)): with np.errstate(all="ignore"): func(index) From 4fd5b917c0ae112239bffbae162066356448ad5e Mon Sep 17 00:00:00 2001 From: moink Date: Sat, 26 Dec 2020 11:36:41 +0100 Subject: [PATCH 4/5] TST: GH30999 Allow first pytest.raises in test_numpy_ufuncs_basic to also be an AttributeError --- pandas/tests/indexes/test_numpy_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py index be64bd8a29627..41ace0b6b10a4 100644 --- a/pandas/tests/indexes/test_numpy_compat.py +++ b/pandas/tests/indexes/test_numpy_compat.py @@ -49,7 +49,7 @@ def test_numpy_ufuncs_basic(index, func): # https://numpy.org/doc/stable/reference/ufuncs.html if isinstance(index, DatetimeIndexOpsMixin): - with tm.external_error_raised(TypeError): + with tm.external_error_raised((TypeError, AttributeError)): with np.errstate(all="ignore"): func(index) elif isinstance(index, (Float64Index, Int64Index, UInt64Index)): From 5937097b35a0f9217607558d7808afe45befd64a Mon Sep 17 00:00:00 2001 From: moink Date: Sun, 27 Dec 2020 13:41:58 +0100 Subject: [PATCH 5/5] TST: GH30999 Revert change to test_subtype_integer_errors and change to tm.external_error_raised in test_numpy_ufuncs_other --- pandas/tests/indexes/interval/test_astype.py | 17 ++++++++++++++++- pandas/tests/indexes/test_numpy_compat.py | 12 +++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pandas/tests/indexes/interval/test_astype.py b/pandas/tests/indexes/interval/test_astype.py index 1e74ed31c47ce..c5dad36285759 100644 --- a/pandas/tests/indexes/interval/test_astype.py +++ b/pandas/tests/indexes/interval/test_astype.py @@ -155,6 +155,7 @@ def test_subtype_integer(self, subtype): with pytest.raises(ValueError, match=msg): index.insert(0, np.nan).astype(dtype) + @pytest.mark.xfail(reason="GH#15832") def test_subtype_integer_errors(self): # float64 -> uint64 fails with negative values index = interval_range(-10.0, 10.0) @@ -163,7 +164,21 @@ def test_subtype_integer_errors(self): "Cannot convert interval[float64] to interval[uint64]; subtypes are " "incompatible" ) - with pytest.raises(TypeError, match=msg): + with pytest.raises(ValueError, match=msg): + index.astype(dtype) + + # float64 -> integer-like fails with non-integer valued floats + index = interval_range(0.0, 10.0, freq=0.25) + dtype = IntervalDtype("int64") + msg = ( + "Casting float interval index to integer would result in duplicate " + "intervals in range" + ) + with pytest.raises(ValueError, match=msg): + index.astype(dtype) + + dtype = IntervalDtype("uint64") + with pytest.raises(ValueError, match=msg): index.astype(dtype) @pytest.mark.parametrize("subtype", ["datetime64[ns]", "timedelta64[ns]"]) diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py index 41ace0b6b10a4..47f7b7f37cf48 100644 --- a/pandas/tests/indexes/test_numpy_compat.py +++ b/pandas/tests/indexes/test_numpy_compat.py @@ -76,12 +76,6 @@ def test_numpy_ufuncs_basic(index, func): def test_numpy_ufuncs_other(index, func, request): # test ufuncs of numpy, see: # https://numpy.org/doc/stable/reference/ufuncs.html - msg = ( - f"ufunc '{func.__name__}' not supported for the input types, and the " - "inputs could not be safely coerced to any supported types according " - "to the casting rule ''safe''" - ) - if isinstance(index, (DatetimeIndex, TimedeltaIndex)): if isinstance(index, DatetimeIndex) and index.tz is not None: if func in [np.isfinite, np.isnan, np.isinf]: @@ -100,11 +94,11 @@ def test_numpy_ufuncs_other(index, func, request): result = func(index) assert isinstance(result, np.ndarray) else: - with pytest.raises(TypeError, match=msg): + with tm.external_error_raised(TypeError): func(index) elif isinstance(index, PeriodIndex): - with pytest.raises(TypeError, match=msg): + with tm.external_error_raised(TypeError): func(index) elif isinstance(index, (Float64Index, Int64Index, UInt64Index)): @@ -116,5 +110,5 @@ def test_numpy_ufuncs_other(index, func, request): if len(index) == 0: pass else: - with pytest.raises(TypeError, match=msg): + with tm.external_error_raised(TypeError): func(index)