From 7ac38b65398fe64f0d5831938a47038ef2de4906 Mon Sep 17 00:00:00 2001 From: Satheesh Kumar Mohan Date: Wed, 25 Mar 2020 17:05:28 +0530 Subject: [PATCH 1/2] add match to bare pyteset.raises() --- pandas/tests/arithmetic/test_timedelta64.py | 4 +- pandas/tests/arrays/interval/test_ops.py | 2 +- pandas/tests/arrays/test_datetimelike.py | 78 +++++++++++++++------ 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index b11fcfd20b8c4..beb16c9549cc4 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -650,9 +650,9 @@ def test_tdi_add_overflow(self): # See GH#14068 # preliminary test scalar analogue of vectorized tests below # TODO: Make raised error message more informative and test - with pytest.raises(OutOfBoundsDatetime): + with pytest.raises(OutOfBoundsDatetime, match="10155196800000000000"): pd.to_timedelta(106580, "D") + Timestamp("2000") - with pytest.raises(OutOfBoundsDatetime): + with pytest.raises(OutOfBoundsDatetime, match="10155196800000000000"): Timestamp("2000") + pd.to_timedelta(106580, "D") _NaT = int(pd.NaT) + 1 diff --git a/pandas/tests/arrays/interval/test_ops.py b/pandas/tests/arrays/interval/test_ops.py index b4de80dc00a4e..c213ef5df319b 100644 --- a/pandas/tests/arrays/interval/test_ops.py +++ b/pandas/tests/arrays/interval/test_ops.py @@ -57,7 +57,7 @@ def test_overlaps_interval_container(self, constructor, other_constructor): # TODO: modify this test when implemented interval_container = constructor.from_breaks(range(5)) other_container = other_constructor.from_breaks(range(5)) - with pytest.raises(NotImplementedError): + with pytest.raises(NotImplementedError, match=None): interval_container.overlaps(other_container) def test_overlaps_na(self, constructor, start_shift): diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index e505917da1dc4..7e8075506475c 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -108,14 +108,20 @@ def test_take_fill(self): result = arr.take([-1, 1], allow_fill=True, fill_value=pd.NaT) assert result[0] is pd.NaT - with pytest.raises(ValueError): - arr.take([0, 1], allow_fill=True, fill_value=2) + value = 2 + msg = f"'fill_value' should be a {self.dtype}. Got '{value}'" + with pytest.raises(ValueError, match=msg): + arr.take([0, 1], allow_fill=True, fill_value=value) - with pytest.raises(ValueError): - arr.take([0, 1], allow_fill=True, fill_value=2.0) + value = 2.0 + msg = f"'fill_value' should be a {self.dtype}. Got '{value}'" + with pytest.raises(ValueError, match=msg): + arr.take([0, 1], allow_fill=True, fill_value=value) - with pytest.raises(ValueError): - arr.take([0, 1], allow_fill=True, fill_value=pd.Timestamp.now().time) + value = pd.Timestamp.now().time + msg = f"'fill_value' should be a {self.dtype}. Got '{value}'" + with pytest.raises(ValueError, match=msg): + arr.take([0, 1], allow_fill=True, fill_value=value) def test_concat_same_type(self): data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9 @@ -139,7 +145,8 @@ def test_unbox_scalar(self): result = arr._unbox_scalar(pd.NaT) assert isinstance(result, int) - with pytest.raises(ValueError): + msg = f"'value' should be a {self.dtype.__name__}." + with pytest.raises(ValueError, match=msg): arr._unbox_scalar("foo") def test_check_compatible_with(self): @@ -261,6 +268,7 @@ def test_shift_fill_int_deprecated(self): class TestDatetimeArray(SharedTests): index_cls = pd.DatetimeIndex array_cls = DatetimeArray + dtype = pd.Timestamp def test_round(self, tz_naive_fixture): # GH#24064 @@ -452,23 +460,28 @@ def test_take_fill_valid(self, datetime_index, tz_naive_fixture): result = arr.take([-1, 1], allow_fill=True, fill_value=now) assert result[0] == now - with pytest.raises(ValueError): + msg = f"'fill_value' should be a {self.dtype}. Got '0 days 00:00:00'." + with pytest.raises(ValueError, match=msg): # fill_value Timedelta invalid arr.take([-1, 1], allow_fill=True, fill_value=now - now) - with pytest.raises(ValueError): + msg = f"'fill_value' should be a {self.dtype}. Got '2014Q1'." + with pytest.raises(ValueError, match=msg): # fill_value Period invalid arr.take([-1, 1], allow_fill=True, fill_value=pd.Period("2014Q1")) tz = None if dti.tz is not None else "US/Eastern" now = pd.Timestamp.now().tz_localize(tz) - with pytest.raises(TypeError): + msg = "Cannot compare tz-naive and tz-aware datetime-like objects" + with pytest.raises(TypeError, match=msg): # Timestamp with mismatched tz-awareness arr.take([-1, 1], allow_fill=True, fill_value=now) - with pytest.raises(ValueError): + value = pd.NaT.value + msg = f"'fill_value' should be a {self.dtype}. Got '{value}'." + with pytest.raises(ValueError, match=msg): # require NaT, not iNaT, as it could be confused with an integer - arr.take([-1, 1], allow_fill=True, fill_value=pd.NaT.value) + arr.take([-1, 1], allow_fill=True, fill_value=value) def test_concat_same_type_invalid(self, datetime_index): # different timezones @@ -520,6 +533,7 @@ def test_strftime_nat(self): class TestTimedeltaArray(SharedTests): index_cls = pd.TimedeltaIndex array_cls = TimedeltaArray + dtype = pd.Timedelta def test_from_tdi(self): tdi = pd.TimedeltaIndex(["1 Day", "3 Hours"]) @@ -618,18 +632,23 @@ def test_take_fill_valid(self, timedelta_index): assert result[0] == td1 now = pd.Timestamp.now() - with pytest.raises(ValueError): + value = now + msg = f"'fill_value' should be a {self.dtype}. Got '{value}'." + with pytest.raises(ValueError, match=msg): # fill_value Timestamp invalid - arr.take([0, 1], allow_fill=True, fill_value=now) + arr.take([0, 1], allow_fill=True, fill_value=value) - with pytest.raises(ValueError): + value = now.to_period("D") + msg = f"'fill_value' should be a {self.dtype}. Got '{value}'." + with pytest.raises(ValueError, match=msg): # fill_value Period invalid - arr.take([0, 1], allow_fill=True, fill_value=now.to_period("D")) + arr.take([0, 1], allow_fill=True, fill_value=value) class TestPeriodArray(SharedTests): index_cls = pd.PeriodIndex array_cls = PeriodArray + dtype = pd.Period def test_from_pi(self, period_index): pi = period_index @@ -665,10 +684,11 @@ def test_to_timestamp(self, how, period_index): def test_to_timestamp_out_of_bounds(self): # GH#19643 previously overflowed silently pi = pd.period_range("1500", freq="Y", periods=3) - with pytest.raises(OutOfBoundsDatetime): + msg = "Out of bounds nanosecond timestamp: 1500-01-01 00:00:00" + with pytest.raises(OutOfBoundsDatetime, match=msg): pi.to_timestamp() - with pytest.raises(OutOfBoundsDatetime): + with pytest.raises(OutOfBoundsDatetime, match=msg): pi._data.to_timestamp() @pytest.mark.parametrize("propname", PeriodArray._bool_ops) @@ -708,7 +728,8 @@ def test_array_interface(self, period_index): tm.assert_numpy_array_equal(result, arr.asi8) # to other dtypes - with pytest.raises(TypeError): + msg = r"float\(\) argument must be a string or a number, not 'Period'" + with pytest.raises(TypeError, match=msg): np.asarray(arr, dtype="float64") result = np.asarray(arr, dtype="S20") @@ -774,8 +795,25 @@ def test_casting_nat_setitem_array(array, casting_nats): ids=lambda x: type(x).__name__, ) def test_invalid_nat_setitem_array(array, non_casting_nats): + msg = ( + r"'value' should be a 'Timestamp', 'NaT', or array of those. " + r"Got 'timedelta64' instead.|" + r"'value' should be a 'Timedelta', 'NaT', or array of those. " + r"Got 'datetime64' instead.|" + r"'value' should be a 'Timedelta', 'NaT', or array of those. " + r"Got 'int' instead.|" + r"'value' should be a 'Timestamp', 'NaT', or array of those. " + r"Got 'int' instead.|" + r"'value' should be a 'Period', 'NaT', or array of those. " + r"Got 'datetime64' instead.|" + r"'value' should be a 'Period', 'NaT', or array of those. " + r"Got 'timedelta64' instead.|" + r"'value' should be a 'Period', 'NaT', or array of those. " + r"Got 'int' instead." + ) + for nat in non_casting_nats: - with pytest.raises(TypeError): + with pytest.raises(TypeError, match=msg): array[0] = nat From cea7cb425705b9f47e2536e12e20cb1ac333354a Mon Sep 17 00:00:00 2001 From: Satheesh Kumar Mohan Date: Sat, 28 Mar 2020 11:45:32 +0530 Subject: [PATCH 2/2] refactor using parametrize and regex --- pandas/tests/arrays/interval/test_ops.py | 2 +- pandas/tests/arrays/test_datetimelike.py | 41 +++++++----------------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/pandas/tests/arrays/interval/test_ops.py b/pandas/tests/arrays/interval/test_ops.py index c213ef5df319b..9c78c2a48b9ff 100644 --- a/pandas/tests/arrays/interval/test_ops.py +++ b/pandas/tests/arrays/interval/test_ops.py @@ -57,7 +57,7 @@ def test_overlaps_interval_container(self, constructor, other_constructor): # TODO: modify this test when implemented interval_container = constructor.from_breaks(range(5)) other_container = other_constructor.from_breaks(range(5)) - with pytest.raises(NotImplementedError, match=None): + with pytest.raises(NotImplementedError, match="^$"): interval_container.overlaps(other_container) def test_overlaps_na(self, constructor, start_shift): diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 7e8075506475c..3abb0bef11947 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -94,6 +94,16 @@ def test_take(self): tm.assert_index_equal(self.index_cls(result), expected) + @pytest.mark.parametrize("fill_value", [2, 2.0, pd.Timestamp.now().time]) + def test_take_fill_raises(self, fill_value): + data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9 + + arr = self.array_cls._simple_new(data, freq="D") + + msg = f"'fill_value' should be a {self.dtype}. Got '{fill_value}'" + with pytest.raises(ValueError, match=msg): + arr.take([0, 1], allow_fill=True, fill_value=fill_value) + def test_take_fill(self): data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9 @@ -108,21 +118,6 @@ def test_take_fill(self): result = arr.take([-1, 1], allow_fill=True, fill_value=pd.NaT) assert result[0] is pd.NaT - value = 2 - msg = f"'fill_value' should be a {self.dtype}. Got '{value}'" - with pytest.raises(ValueError, match=msg): - arr.take([0, 1], allow_fill=True, fill_value=value) - - value = 2.0 - msg = f"'fill_value' should be a {self.dtype}. Got '{value}'" - with pytest.raises(ValueError, match=msg): - arr.take([0, 1], allow_fill=True, fill_value=value) - - value = pd.Timestamp.now().time - msg = f"'fill_value' should be a {self.dtype}. Got '{value}'" - with pytest.raises(ValueError, match=msg): - arr.take([0, 1], allow_fill=True, fill_value=value) - def test_concat_same_type(self): data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9 @@ -796,20 +791,8 @@ def test_casting_nat_setitem_array(array, casting_nats): ) def test_invalid_nat_setitem_array(array, non_casting_nats): msg = ( - r"'value' should be a 'Timestamp', 'NaT', or array of those. " - r"Got 'timedelta64' instead.|" - r"'value' should be a 'Timedelta', 'NaT', or array of those. " - r"Got 'datetime64' instead.|" - r"'value' should be a 'Timedelta', 'NaT', or array of those. " - r"Got 'int' instead.|" - r"'value' should be a 'Timestamp', 'NaT', or array of those. " - r"Got 'int' instead.|" - r"'value' should be a 'Period', 'NaT', or array of those. " - r"Got 'datetime64' instead.|" - r"'value' should be a 'Period', 'NaT', or array of those. " - r"Got 'timedelta64' instead.|" - r"'value' should be a 'Period', 'NaT', or array of those. " - r"Got 'int' instead." + "'value' should be a '(Timestamp|Timedelta|Period)', 'NaT', or array of those. " + "Got '(timedelta64|datetime64|int)' instead." ) for nat in non_casting_nats: