diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index fdb1ee754a7e6..887c8da6305dd 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -1041,7 +1041,11 @@ def _arith_method(self, other, op): rstop = op(left.stop, right) res_name = ops.get_op_result_name(self, other) - result = type(self)(rstart, rstop, rstep, name=res_name) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + # The constructor validation can lead to a DeprecationWarning + # from numpy, e.g. with RangeIndex + np.datetime64("now") + result = type(self)(rstart, rstop, rstep, name=res_name) # for compat with numpy / Int64Index # even if we can represent as a RangeIndex, return diff --git a/pandas/tests/arithmetic/conftest.py b/pandas/tests/arithmetic/conftest.py index 55cbfaf76d5a7..01b447aa855a3 100644 --- a/pandas/tests/arithmetic/conftest.py +++ b/pandas/tests/arithmetic/conftest.py @@ -107,15 +107,15 @@ def numeric_idx(request): @pytest.fixture( params=[ - pd.Timedelta("5m4s").to_pytimedelta(), - pd.Timedelta("5m4s"), - pd.Timedelta("5m4s").to_timedelta64(), + pd.Timedelta("10m7s").to_pytimedelta(), + pd.Timedelta("10m7s"), + pd.Timedelta("10m7s").to_timedelta64(), ], ids=lambda x: type(x).__name__, ) def scalar_td(request): """ - Several variants of Timedelta scalars representing 5 minutes and 4 seconds + Several variants of Timedelta scalars representing 10 minutes and 7 seconds. """ return request.param diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 6a5d88cc8d4a6..8194f47541e4c 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -814,6 +814,9 @@ def test_dt64arr_add_timedeltalike_scalar( result = rng + two_hours tm.assert_equal(result, expected) + result = two_hours + rng + tm.assert_equal(result, expected) + rng += two_hours tm.assert_equal(rng, expected) @@ -834,34 +837,6 @@ def test_dt64arr_sub_timedeltalike_scalar( rng -= two_hours tm.assert_equal(rng, expected) - # TODO: redundant with test_dt64arr_add_timedeltalike_scalar - def test_dt64arr_add_td64_scalar(self, box_with_array): - # scalar timedeltas/np.timedelta64 objects - # operate with np.timedelta64 correctly - ser = Series([Timestamp("20130101 9:01"), Timestamp("20130101 9:02")]) - - expected = Series( - [Timestamp("20130101 9:01:01"), Timestamp("20130101 9:02:01")] - ) - - dtarr = tm.box_expected(ser, box_with_array) - expected = tm.box_expected(expected, box_with_array) - - result = dtarr + np.timedelta64(1, "s") - tm.assert_equal(result, expected) - result = np.timedelta64(1, "s") + dtarr - tm.assert_equal(result, expected) - - expected = Series( - [Timestamp("20130101 9:01:00.005"), Timestamp("20130101 9:02:00.005")] - ) - expected = tm.box_expected(expected, box_with_array) - - result = dtarr + np.timedelta64(5, "ms") - tm.assert_equal(result, expected) - result = np.timedelta64(5, "ms") + dtarr - tm.assert_equal(result, expected) - def test_dt64arr_add_sub_td64_nat(self, box_with_array, tz_naive_fixture): # GH#23320 special handling for timedelta64("NaT") tz = tz_naive_fixture @@ -918,6 +893,9 @@ def test_dt64arr_add_sub_td64ndarray(self, tz_naive_fixture, box_with_array): Timestamp("2013-01-01"), Timestamp("2013-01-01").to_pydatetime(), Timestamp("2013-01-01").to_datetime64(), + # GH#7996, GH#22163 ensure non-nano datetime64 is converted to nano + # for DataFrame operation + np.datetime64("2013-01-01", "D"), ], ) def test_dt64arr_sub_dtscalar(self, box_with_array, ts): @@ -931,25 +909,11 @@ def test_dt64arr_sub_dtscalar(self, box_with_array, ts): result = idx - ts tm.assert_equal(result, expected) - def test_dt64arr_sub_datetime64_not_ns(self, box_with_array): - # GH#7996, GH#22163 ensure non-nano datetime64 is converted to nano - # for DataFrame operation - dt64 = np.datetime64("2013-01-01") - assert dt64.dtype == "datetime64[D]" - - dti = date_range("20130101", periods=3)._with_freq(None) - dtarr = tm.box_expected(dti, box_with_array) - - expected = TimedeltaIndex(["0 Days", "1 Day", "2 Days"]) - expected = tm.box_expected(expected, box_with_array) - - result = dtarr - dt64 - tm.assert_equal(result, expected) - - result = dt64 - dtarr + result = ts - idx + tm.assert_equal(result, -expected) tm.assert_equal(result, -expected) - def test_dt64arr_sub_timestamp(self, box_with_array): + def test_dt64arr_sub_timestamp_tzaware(self, box_with_array): ser = date_range("2014-03-17", periods=2, freq="D", tz="US/Eastern") ser = ser._with_freq(None) ts = ser[0] @@ -1024,25 +988,73 @@ def test_dt64arr_aware_sub_dt64ndarray_raises( # ------------------------------------------------------------- # Addition of datetime-like others (invalid) - def test_dt64arr_add_dt64ndarray_raises(self, tz_naive_fixture, box_with_array): - + def test_dt64arr_add_dtlike_raises(self, tz_naive_fixture, box_with_array): + # GH#22163 ensure DataFrame doesn't cast Timestamp to i8 + # GH#9631 tz = tz_naive_fixture - dti = date_range("2016-01-01", periods=3, tz=tz) - dt64vals = dti.values + dti = date_range("2016-01-01", periods=3, tz=tz) + if tz is None: + dti2 = dti.tz_localize("US/Eastern") + else: + dti2 = dti.tz_localize(None) dtarr = tm.box_expected(dti, box_with_array) - assert_cannot_add(dtarr, dt64vals) - def test_dt64arr_add_timestamp_raises(self, box_with_array): - # GH#22163 ensure DataFrame doesn't cast Timestamp to i8 - idx = DatetimeIndex(["2011-01-01", "2011-01-02"]) - ts = idx[0] - idx = tm.box_expected(idx, box_with_array) - assert_cannot_add(idx, ts) + assert_cannot_add(dtarr, dti.values) + assert_cannot_add(dtarr, dti) + assert_cannot_add(dtarr, dtarr) + assert_cannot_add(dtarr, dti[0]) + assert_cannot_add(dtarr, dti[0].to_pydatetime()) + assert_cannot_add(dtarr, dti[0].to_datetime64()) + assert_cannot_add(dtarr, dti2[0]) + assert_cannot_add(dtarr, dti2[0].to_pydatetime()) + assert_cannot_add(dtarr, np.datetime64("2011-01-01", "D")) # ------------------------------------------------------------- # Other Invalid Addition/Subtraction + # Note: freq here includes both Tick and non-Tick offsets; this is + # relevant because historically integer-addition was allowed if we had + # a freq. + @pytest.mark.parametrize("freq", ["H", "D", "W", "M", "MS", "Q", "B", None]) + @pytest.mark.parametrize("dtype", [None, "uint8"]) + def test_dt64arr_addsub_intlike( + self, dtype, box_with_array, freq, tz_naive_fixture + ): + # GH#19959, GH#19123, GH#19012 + tz = tz_naive_fixture + if box_with_array is pd.DataFrame: + # alignment headaches + return + + if freq is None: + dti = DatetimeIndex(["NaT", "2017-04-05 06:07:08"], tz=tz) + else: + dti = date_range("2016-01-01", periods=2, freq=freq, tz=tz) + + obj = box_with_array(dti) + other = np.array([4, -1], dtype=dtype) + + msg = "|".join( + [ + "Addition/subtraction of integers", + "cannot subtract DatetimeArray from", + # IntegerArray + "can only perform ops with numeric values", + "unsupported operand type.*Categorical", + ] + ) + assert_invalid_addsub_type(obj, 1, msg) + assert_invalid_addsub_type(obj, np.int64(2), msg) + assert_invalid_addsub_type(obj, np.array(3, dtype=np.int64), msg) + assert_invalid_addsub_type(obj, other, msg) + assert_invalid_addsub_type(obj, np.array(other), msg) + assert_invalid_addsub_type(obj, pd.array(other), msg) + assert_invalid_addsub_type(obj, pd.Categorical(other), msg) + assert_invalid_addsub_type(obj, pd.Index(other), msg) + assert_invalid_addsub_type(obj, pd.core.indexes.api.NumericIndex(other), msg) + assert_invalid_addsub_type(obj, Series(other), msg) + @pytest.mark.parametrize( "other", [ @@ -1101,48 +1113,49 @@ def test_dt64arr_addsub_time_objects_raises(self, box_with_array, tz_naive_fixtu obj1 = tm.box_expected(obj1, box_with_array) obj2 = tm.box_expected(obj2, box_with_array) + msg = "|".join( + [ + "unsupported operand", + "cannot subtract DatetimeArray from ndarray", + ] + ) + with warnings.catch_warnings(record=True): # pandas.errors.PerformanceWarning: Non-vectorized DateOffset being # applied to Series or DatetimeIndex # we aren't testing that here, so ignore. warnings.simplefilter("ignore", PerformanceWarning) - # If `x + y` raises, then `y + x` should raise here as well + assert_invalid_addsub_type(obj1, obj2, msg=msg) - msg = ( - r"unsupported operand type\(s\) for -: " - "'(Timestamp|DatetimeArray)' and 'datetime.time'" - ) - with pytest.raises(TypeError, match=msg): - obj1 - obj2 + # ------------------------------------------------------------- + # Other invalid operations - msg = "|".join( - [ - "cannot subtract DatetimeArray from ndarray", - "ufunc (subtract|'subtract') cannot use operands with types " - r"dtype\('O'\) and dtype\('