diff --git a/pandas/tests/arithmetic/conftest.py b/pandas/tests/arithmetic/conftest.py index e5d9c85691b7a..2becf8d776bda 100644 --- a/pandas/tests/arithmetic/conftest.py +++ b/pandas/tests/arithmetic/conftest.py @@ -5,9 +5,6 @@ import pandas as pd from pandas.compat import long -from pandas.core.arrays import ( - PeriodArray, DatetimeArrayMixin as DatetimeArray, - TimedeltaArrayMixin as TimedeltaArray) import pandas.util.testing as tm @@ -174,44 +171,64 @@ def box_df_fail(request): return request.param -@pytest.fixture(params=[(pd.Index, False), - (pd.Series, False), +@pytest.fixture(params=[pd.Index, + pd.Series, (pd.DataFrame, False), - pytest.param((pd.DataFrame, True), - marks=pytest.mark.xfail(strict=True))], + (pd.DataFrame, True), + tm.to_array], ids=id_func) -def box_transpose_fail(request): +def box_and_transpose(request): """ - Fixture similar to `box` but testing both transpose cases for DataFrame, - with the tranpose=True case xfailed. + Like `box4`, but the DataFrame case with box transpose=True and + transpose=False """ # GH#23620 return request.param -@pytest.fixture(params=[pd.Index, pd.Series, pd.DataFrame, PeriodArray], +@pytest.fixture(params=[pd.Index, + pd.Series, + (pd.DataFrame, False), + pytest.param((pd.DataFrame, True), + marks=pytest.mark.xfail(strict=True)), + tm.to_array], ids=id_func) -def box_with_period(request): +def box_transpose_fail(request): """ - Like `box`, but specific to PeriodDtype for also testing PeriodArray + Like `box_and_transpose`, but xfailing the transposed DataFrame case """ + # GH#23620 return request.param -@pytest.fixture(params=[pd.Index, pd.Series, pd.DataFrame, DatetimeArray], - ids=id_func) -def box_with_datetime(request): - """ - Like `box`, but specific to datetime64 for also testing DatetimeArray - """ - return request.param +# Construct tuples of the form (box, tz) over all our box classes and +# the timezones in tz_naive_fixture. We then xfail the cases where the box +# is a DataFrame-with-transpose and the timezone is not tz-naive (i.e. None) +boxes = [pd.Index, + pd.Series, + (pd.DataFrame, False), + (pd.DataFrame, True), + tm.to_array] +# copied from pandas.conftest tz_naive_fixture +TIMEZONES = [None, 'UTC', 'US/Eastern', 'Asia/Tokyo', 'dateutil/US/Pacific', + 'dateutil/Asia/Singapore'] -@pytest.fixture(params=[pd.Index, pd.Series, pd.DataFrame, TimedeltaArray], - ids=id_func) -def box_with_timedelta(request): +params = [(x, y) for x in boxes for y in TIMEZONES] +for n in range(len(params)): + tup = params[n] + if isinstance(tup[0], tuple) and tup[0][1] is True and tup[1] is not None: + # i.e. (DataFrame, True, tzinfo) excluding the no-tzinfo case + param = pytest.param(tup, marks=pytest.mark.xfail(strict=True)) + params[n] = param + + +@pytest.fixture(params=params) +def box_transpose_and_tz(request): """ - Like `box`, but specific to timedelta64 for also testing TimedeltaArray + Fixture to test over Index, Series, DataFrame, and pandas Array, also + providing timezones, xfailing timezone-aware cases with transposed + DataFrame """ return request.param @@ -224,3 +241,8 @@ def box_with_array(request): classes """ return request.param + + +# aliases so we can use the same fixture twice in a test +box_with_array2 = box_with_array +box_and_transpose2 = box_and_transpose diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index cd28e60b62562..6e0142a3272c9 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -159,16 +159,14 @@ def test_dt64_ser_cmp_date_warning(self): assert "a TypeError will be raised" in str(m[0].message) @pytest.mark.skip(reason="GH#21359") - def test_dt64ser_cmp_date_invalid(self, box_with_datetime): + def test_dt64ser_cmp_date_invalid(self, box_and_transpose): # GH#19800 datetime.date comparison raises to # match DatetimeIndex/Timestamp. This also matches the behavior # of stdlib datetime.datetime - box = box_with_datetime - ser = pd.date_range('20010101', periods=10) date = ser.iloc[0].to_pydatetime().date() - ser = tm.box_expected(ser, box) + ser = tm.box_expected(ser, box_and_transpose) assert not (ser == date).any() assert (ser != date).all() with pytest.raises(TypeError): @@ -230,13 +228,12 @@ def test_timestamp_compare_series(self, left, right): result = right_f(pd.Timestamp("nat"), s_nat) tm.assert_series_equal(result, expected) - def test_dt64arr_timestamp_equality(self, box_with_datetime): + def test_dt64arr_timestamp_equality(self, box_with_array): # GH#11034 - box = box_with_datetime - xbox = box if box not in [pd.Index, DatetimeArray] else np.ndarray + xbox = box_with_array if box_with_array is not pd.Index else np.ndarray ser = pd.Series([pd.Timestamp('2000-01-29 01:59:00'), 'NaT']) - ser = tm.box_expected(ser, box) + ser = tm.box_expected(ser, box_with_array) result = ser != ser expected = tm.box_expected([False, True], xbox) @@ -1071,10 +1068,10 @@ def test_dti_add_sub_float(self, op, other): with pytest.raises(TypeError): op(dti, other) - def test_dti_add_timestamp_raises(self, box_with_datetime): + def test_dti_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']) - idx = tm.box_expected(idx, box_with_datetime) + idx = tm.box_expected(idx, box_with_array) msg = "cannot add" with pytest.raises(TypeError, match=msg): idx + Timestamp('2011-01-01') @@ -1186,21 +1183,17 @@ def test_dti_add_intarray_no_freq(self, box): # ------------------------------------------------------------- # Binary operations DatetimeIndex and timedelta-like - def test_dti_add_timedeltalike(self, tz_naive_fixture, two_hours, - box_with_datetime): + def test_dti_add_timedeltalike(self, two_hours, box_transpose_and_tz): # GH#22005, GH#22163 check DataFrame doesn't raise TypeError - box = box_with_datetime - - tz = tz_naive_fixture + box, tz = box_transpose_and_tz rng = pd.date_range('2000-01-01', '2000-02-01', tz=tz) - # FIXME: calling with transpose=True raises ValueError - rng = tm.box_expected(rng, box, transpose=False) + rng = tm.box_expected(rng, box) result = rng + two_hours expected = pd.date_range('2000-01-01 02:00', '2000-02-01 02:00', tz=tz) - expected = tm.box_expected(expected, box, transpose=False) + expected = tm.box_expected(expected, box) tm.assert_equal(result, expected) def test_dti_iadd_timedeltalike(self, tz_naive_fixture, two_hours): @@ -1227,18 +1220,16 @@ def test_dti_isub_timedeltalike(self, tz_naive_fixture, two_hours): rng -= two_hours tm.assert_index_equal(rng, expected) - def test_dt64arr_add_sub_td64_nat(self, box, tz_naive_fixture): + def test_dt64arr_add_sub_td64_nat(self, box_transpose_and_tz): # GH#23320 special handling for timedelta64("NaT") - tz = tz_naive_fixture + box, tz = box_transpose_and_tz dti = pd.date_range("1994-04-01", periods=9, tz=tz, freq="QS") other = np.timedelta64("NaT") expected = pd.DatetimeIndex(["NaT"] * 9, tz=tz) - # FIXME: fails with transpose=True due to tz-aware DataFrame - # transpose bug - obj = tm.box_expected(dti, box, transpose=False) - expected = tm.box_expected(expected, box, transpose=False) + obj = tm.box_expected(dti, box) + expected = tm.box_expected(expected, box) result = obj + other tm.assert_equal(result, expected) @@ -1473,13 +1464,13 @@ def test_sub_dti_dti(self): tm.assert_index_equal(result, expected) @pytest.mark.parametrize('dti_freq', [None, 'D']) - def test_dt64arr_add_sub_period(self, dti_freq, box_with_datetime): + def test_dt64arr_add_sub_period(self, dti_freq, box_and_transpose): # GH#13078 # not supported, check TypeError p = pd.Period('2011-01-01', freq='D') idx = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=dti_freq) - idx = tm.box_expected(idx, box_with_datetime) + idx = tm.box_expected(idx, box_and_transpose) with pytest.raises(TypeError): idx + p @@ -1493,13 +1484,13 @@ def test_dt64arr_add_sub_period(self, dti_freq, box_with_datetime): @pytest.mark.parametrize('pi_freq', ['D', 'W', 'Q', 'H']) @pytest.mark.parametrize('dti_freq', [None, 'D']) def test_dti_add_sub_pi(self, dti_freq, pi_freq, - box_with_datetime, box_with_period): + box_with_array, box_with_array2): # GH#20049 subtracting PeriodIndex should raise TypeError dti = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=dti_freq) pi = dti.to_period(pi_freq) - dtarr = tm.box_expected(dti, box_with_datetime) - parr = tm.box_expected(pi, box_with_period) + dtarr = tm.box_expected(dti, box_with_array) + parr = tm.box_expected(pi, box_with_array2) with pytest.raises(TypeError): dtarr + parr @@ -1828,24 +1819,22 @@ def test_dti_with_offset_series(self, tz_naive_fixture, names): res3 = dti - other tm.assert_series_equal(res3, expected_sub) - def test_dti_add_offset_tzaware(self, tz_aware_fixture, box_with_datetime): + def test_dti_add_offset_tzaware(self, box_transpose_and_tz): # GH#21610, GH#22163 ensure DataFrame doesn't return object-dtype - box = box_with_datetime + box, tz = box_transpose_and_tz - timezone = tz_aware_fixture - if timezone == 'US/Pacific': - dates = date_range('2012-11-01', periods=3, tz=timezone) + if tz == 'US/Pacific': + dates = date_range('2012-11-01', periods=3, tz=tz) offset = dates + pd.offsets.Hour(5) assert dates[0] + pd.offsets.Hour(5) == offset[0] dates = date_range('2010-11-01 00:00', - periods=3, tz=timezone, freq='H') + periods=3, tz=tz, freq='H') expected = DatetimeIndex(['2010-11-01 05:00', '2010-11-01 06:00', - '2010-11-01 07:00'], freq='H', tz=timezone) + '2010-11-01 07:00'], freq='H', tz=tz) - # FIXME: these raise ValueError with transpose=True - dates = tm.box_expected(dates, box, transpose=False) - expected = tm.box_expected(expected, box, transpose=False) + dates = tm.box_expected(dates, box) + expected = tm.box_expected(expected, box) # TODO: parametrize over the scalar being added? radd? sub? offset = dates + pd.offsets.Hour(5) diff --git a/pandas/tests/arithmetic/test_period.py b/pandas/tests/arithmetic/test_period.py index a26a11cb6be9e..23d5c593cab6a 100644 --- a/pandas/tests/arithmetic/test_period.py +++ b/pandas/tests/arithmetic/test_period.py @@ -556,15 +556,13 @@ def test_pi_sub_isub_offset(self): def test_pi_add_offset_n_gt1(self, box_transpose_fail): # GH#23215 # add offset to PeriodIndex with freq.n > 1 - box, transpose = box_transpose_fail - per = pd.Period('2016-01', freq='2M') pi = pd.PeriodIndex([per]) expected = pd.PeriodIndex(['2016-03'], freq='2M') - pi = tm.box_expected(pi, box, transpose=transpose) - expected = tm.box_expected(expected, box, transpose=transpose) + pi = tm.box_expected(pi, box_transpose_fail) + expected = tm.box_expected(expected, box_transpose_fail) result = pi + per.freq tm.assert_equal(result, expected) @@ -572,17 +570,14 @@ def test_pi_add_offset_n_gt1(self, box_transpose_fail): result = per.freq + pi tm.assert_equal(result, expected) - def test_pi_add_offset_n_gt1_not_divisible(self, box_with_period): + def test_pi_add_offset_n_gt1_not_divisible(self, box_transpose_fail): # GH#23215 # PeriodIndex with freq.n > 1 add offset with offset.n % freq.n != 0 - box = box_with_period - pi = pd.PeriodIndex(['2016-01'], freq='2M') expected = pd.PeriodIndex(['2016-04'], freq='2M') - # FIXME: with transposing these tests fail - pi = tm.box_expected(pi, box, transpose=False) - expected = tm.box_expected(expected, box, transpose=False) + pi = tm.box_expected(pi, box_transpose_fail) + expected = tm.box_expected(expected, box_transpose_fail) result = pi + to_offset('3M') tm.assert_equal(result, expected) @@ -798,14 +793,12 @@ def test_pi_add_sub_timedeltalike_freq_mismatch_monthly(self, def test_parr_add_sub_td64_nat(self, box_transpose_fail): # GH#23320 special handling for timedelta64("NaT") - box, transpose = box_transpose_fail - pi = pd.period_range("1994-04-01", periods=9, freq="19D") other = np.timedelta64("NaT") expected = pd.PeriodIndex(["NaT"] * 9, freq="19D") - obj = tm.box_expected(pi, box, transpose=transpose) - expected = tm.box_expected(expected, box, transpose=transpose) + obj = tm.box_expected(pi, box_transpose_fail) + expected = tm.box_expected(expected, box_transpose_fail) result = obj + other tm.assert_equal(result, expected) @@ -898,10 +891,10 @@ def test_pi_ops(self): tm.assert_index_equal(result, exp) @pytest.mark.parametrize('ng', ["str", 1.5]) - def test_pi_ops_errors(self, ng, box_with_period): + def test_pi_ops_errors(self, ng, box_with_array): idx = PeriodIndex(['2011-01', '2011-02', '2011-03', '2011-04'], freq='M', name='idx') - obj = tm.box_expected(idx, box_with_period) + obj = tm.box_expected(idx, box_with_array) msg = r"unsupported operand type\(s\)" with pytest.raises(TypeError, match=msg): diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 58c8b3b07f723..207bc6ed6f60a 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -413,19 +413,18 @@ def test_td64arr_sub_timestamp_raises(self, box): with pytest.raises(TypeError, match=msg): idx - Timestamp('2011-01-01') - def test_td64arr_add_timestamp(self, box, tz_naive_fixture): + def test_td64arr_add_timestamp(self, box_transpose_and_tz): # GH#23215 # TODO: parametrize over scalar datetime types? - tz = tz_naive_fixture + box, tz = box_transpose_and_tz + other = Timestamp('2011-01-01', tz=tz) idx = TimedeltaIndex(['1 day', '2 day']) expected = DatetimeIndex(['2011-01-02', '2011-01-03'], tz=tz) - # FIXME: fails with transpose=True because of tz-aware DataFrame - # transpose bug - idx = tm.box_expected(idx, box, transpose=False) - expected = tm.box_expected(expected, box, transpose=False) + idx = tm.box_expected(idx, box) + expected = tm.box_expected(expected, box) result = idx + other tm.assert_equal(result, expected) diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 1dc461e010e37..5ab0d659a0f05 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -1621,6 +1621,12 @@ def box_expected(expected, box_cls, transpose=True): # not a single-column, in order to operate against non-DataFrame # vectors of the same length. expected = expected.T + elif isinstance(box_cls, tuple): + # compat for passing transpose in the same fixture as box without + # unpacking the tuple in every test + assert len(box_cls) == 2 + box_cls, transpose = box_cls + return box_expected(expected, box_cls, transpose=transpose) elif box_cls is PeriodArray: # the PeriodArray constructor is not as flexible as period_array expected = period_array(expected)