From 28c508e85c4883b57f68d7c26e36acbbc0532746 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Nov 2018 16:29:50 -0800 Subject: [PATCH 01/11] de-duplicate tests --- pandas/tests/scalar/test_nat.py | 2 - .../scalar/timestamp/test_comparisons.py | 94 ++++++++----------- 2 files changed, 39 insertions(+), 57 deletions(-) diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index b978ccf4a2f6a..b49da5ed4eb24 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -11,8 +11,6 @@ from pandas.util import testing as tm from pandas._libs.tslib import iNaT -from pandas.compat import callable - @pytest.mark.parametrize('nat, idx', [(Timestamp('NaT'), DatetimeIndex), (Timedelta('NaT'), TimedeltaIndex), diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index 50e72c11abc4b..5f3d82d73d46b 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -12,6 +12,12 @@ from pandas import Timestamp +utc_objs = ['utc', utc, tzutc()] +if not PY2: + from datetime import timezone + utc_objs.append(timezone.utc) + + class TestTimestampComparison(object): def test_comparison_object_array(self): # GH#15183 @@ -69,7 +75,7 @@ def test_comparison(self): assert other >= val def test_compare_invalid(self): - # GH 8058 + # GH#8058 val = Timestamp('20130101 12:01:02') assert not val == 'foo' assert not val == 10.0 @@ -89,65 +95,43 @@ def test_compare_invalid(self): assert val != np.float64(1) assert val != np.int64(1) - def test_cant_compare_tz_naive_w_aware(self): - # see gh-1404 + @pytest.mark.parametrize('utc_obj', utc_objs) + def test_cant_compare_tz_naive_w_aware(self, utc_obj): + # see GH#1404 a = Timestamp('3/12/2012') - b = Timestamp('3/12/2012', tz='utc') - - pytest.raises(Exception, a.__eq__, b) - pytest.raises(Exception, a.__ne__, b) - pytest.raises(Exception, a.__lt__, b) - pytest.raises(Exception, a.__gt__, b) - pytest.raises(Exception, b.__eq__, a) - pytest.raises(Exception, b.__ne__, a) - pytest.raises(Exception, b.__lt__, a) - pytest.raises(Exception, b.__gt__, a) - - if PY2: - pytest.raises(Exception, a.__eq__, b.to_pydatetime()) - pytest.raises(Exception, a.to_pydatetime().__eq__, b) - else: - assert not a == b.to_pydatetime() - assert not a.to_pydatetime() == b + b = Timestamp('3/12/2012', tz=utc_obj) - def test_cant_compare_tz_naive_w_aware_explicit_pytz(self): - # see gh-1404 - a = Timestamp('3/12/2012') - b = Timestamp('3/12/2012', tz=utc) - - pytest.raises(Exception, a.__eq__, b) - pytest.raises(Exception, a.__ne__, b) - pytest.raises(Exception, a.__lt__, b) - pytest.raises(Exception, a.__gt__, b) - pytest.raises(Exception, b.__eq__, a) - pytest.raises(Exception, b.__ne__, a) - pytest.raises(Exception, b.__lt__, a) - pytest.raises(Exception, b.__gt__, a) - - if PY2: - pytest.raises(Exception, a.__eq__, b.to_pydatetime()) - pytest.raises(Exception, a.to_pydatetime().__eq__, b) - else: - assert not a == b.to_pydatetime() - assert not a.to_pydatetime() == b - - def test_cant_compare_tz_naive_w_aware_dateutil(self): - # see gh-1404 - a = Timestamp('3/12/2012') - b = Timestamp('3/12/2012', tz=tzutc()) + with pytest.raises(TypeError): + a == b + with pytest.raises(TypeError): + a != b + with pytest.raises(TypeError): + a < b + with pytest.raises(TypeError): + a <= b + with pytest.raises(TypeError): + a > b + with pytest.raises(TypeError): + a >= b - pytest.raises(Exception, a.__eq__, b) - pytest.raises(Exception, a.__ne__, b) - pytest.raises(Exception, a.__lt__, b) - pytest.raises(Exception, a.__gt__, b) - pytest.raises(Exception, b.__eq__, a) - pytest.raises(Exception, b.__ne__, a) - pytest.raises(Exception, b.__lt__, a) - pytest.raises(Exception, b.__gt__, a) + with pytest.raises(TypeError): + b == a + with pytest.raises(TypeError): + b != a + with pytest.raises(TypeError): + b < a + with pytest.raises(TypeError): + b <= a + with pytest.raises(TypeError): + b > a + with pytest.raises(TypeError): + b >= a if PY2: - pytest.raises(Exception, a.__eq__, b.to_pydatetime()) - pytest.raises(Exception, a.to_pydatetime().__eq__, b) + with pytest.raises(TypeError): + a == b.to_pydatetime() + with pytest.raises(TypeError): + a.to_pydatetime() == b else: assert not a == b.to_pydatetime() assert not a.to_pydatetime() == b From e273d7d83ecc607935e9e9a4696d0ba3e9f4b934 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Nov 2018 17:23:27 -0800 Subject: [PATCH 02/11] cleanup Period test organization --- pandas/tests/scalar/period/test_period.py | 865 +++++++++++----------- 1 file changed, 440 insertions(+), 425 deletions(-) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index 6d5686463f2ae..502fbf23a4688 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -16,281 +16,7 @@ from pandas._libs.tslibs.parsing import DateParseError -class TestPeriodProperties(object): - "Test properties such as year, month, weekday, etc...." - - @pytest.mark.parametrize('freq', ['A', 'M', 'D', 'H']) - def test_is_leap_year(self, freq): - # GH 13727 - p = Period('2000-01-01 00:00:00', freq=freq) - assert p.is_leap_year - assert isinstance(p.is_leap_year, bool) - - p = Period('1999-01-01 00:00:00', freq=freq) - assert not p.is_leap_year - - p = Period('2004-01-01 00:00:00', freq=freq) - assert p.is_leap_year - - p = Period('2100-01-01 00:00:00', freq=freq) - assert not p.is_leap_year - - def test_quarterly_negative_ordinals(self): - p = Period(ordinal=-1, freq='Q-DEC') - assert p.year == 1969 - assert p.quarter == 4 - assert isinstance(p, Period) - - p = Period(ordinal=-2, freq='Q-DEC') - assert p.year == 1969 - assert p.quarter == 3 - assert isinstance(p, Period) - - p = Period(ordinal=-2, freq='M') - assert p.year == 1969 - assert p.month == 11 - assert isinstance(p, Period) - - @pytest.mark.parametrize('month', MONTHS) - def test_period_cons_quarterly(self, month): - # bugs in scikits.timeseries - freq = 'Q-%s' % month - exp = Period('1989Q3', freq=freq) - assert '1989Q3' in str(exp) - stamp = exp.to_timestamp('D', how='end') - p = Period(stamp, freq=freq) - assert p == exp - - stamp = exp.to_timestamp('3D', how='end') - p = Period(stamp, freq=freq) - assert p == exp - - @pytest.mark.parametrize('month', MONTHS) - def test_period_cons_annual(self, month): - # bugs in scikits.timeseries - freq = 'A-%s' % month - exp = Period('1989', freq=freq) - stamp = exp.to_timestamp('D', how='end') + timedelta(days=30) - p = Period(stamp, freq=freq) - - with tm.assert_produces_warning(FutureWarning): - assert p == exp + 1 - assert isinstance(p, Period) - - @pytest.mark.parametrize('day', DAYS) - @pytest.mark.parametrize('num', range(10, 17)) - def test_period_cons_weekly(self, num, day): - daystr = '2011-02-%d' % num - freq = 'W-%s' % day - - result = Period(daystr, freq=freq) - expected = Period(daystr, freq='D').asfreq(freq) - assert result == expected - assert isinstance(result, Period) - - def test_period_from_ordinal(self): - p = Period('2011-01', freq='M') - res = Period._from_ordinal(p.ordinal, freq='M') - assert p == res - assert isinstance(res, Period) - - def test_period_cons_nat(self): - p = Period('NaT', freq='M') - assert p is NaT - - p = Period('nat', freq='W-SUN') - assert p is NaT - - p = Period(iNaT, freq='D') - assert p is NaT - - p = Period(iNaT, freq='3D') - assert p is NaT - - p = Period(iNaT, freq='1D1H') - assert p is NaT - - p = Period('NaT') - assert p is NaT - - p = Period(iNaT) - assert p is NaT - - def test_period_cons_mult(self): - p1 = Period('2011-01', freq='3M') - p2 = Period('2011-01', freq='M') - assert p1.ordinal == p2.ordinal - - assert p1.freq == offsets.MonthEnd(3) - assert p1.freqstr == '3M' - - assert p2.freq == offsets.MonthEnd() - assert p2.freqstr == 'M' - - with tm.assert_produces_warning(FutureWarning): - result = p1 + 1 - assert result.ordinal == (p2 + 3).ordinal - - assert result.freq == p1.freq - assert result.freqstr == '3M' - - with tm.assert_produces_warning(FutureWarning): - result = p1 - 1 - assert result.ordinal == (p2 - 3).ordinal - assert result.freq == p1.freq - assert result.freqstr == '3M' - - msg = ('Frequency must be positive, because it' - ' represents span: -3M') - with pytest.raises(ValueError, match=msg): - Period('2011-01', freq='-3M') - - msg = ('Frequency must be positive, because it' ' represents span: 0M') - with pytest.raises(ValueError, match=msg): - Period('2011-01', freq='0M') - - def test_period_cons_combined(self): - p = [(Period('2011-01', freq='1D1H'), - Period('2011-01', freq='1H1D'), - Period('2011-01', freq='H')), - (Period(ordinal=1, freq='1D1H'), - Period(ordinal=1, freq='1H1D'), - Period(ordinal=1, freq='H'))] - - for p1, p2, p3 in p: - assert p1.ordinal == p3.ordinal - assert p2.ordinal == p3.ordinal - - assert p1.freq == offsets.Hour(25) - assert p1.freqstr == '25H' - - assert p2.freq == offsets.Hour(25) - assert p2.freqstr == '25H' - - assert p3.freq == offsets.Hour() - assert p3.freqstr == 'H' - - with tm.assert_produces_warning(FutureWarning): - result = p1 + 1 - assert result.ordinal == (p3 + 25).ordinal - assert result.freq == p1.freq - assert result.freqstr == '25H' - - with tm.assert_produces_warning(FutureWarning): - result = p2 + 1 - assert result.ordinal == (p3 + 25).ordinal - assert result.freq == p2.freq - assert result.freqstr == '25H' - - with tm.assert_produces_warning(FutureWarning): - result = p1 - 1 - assert result.ordinal == (p3 - 25).ordinal - assert result.freq == p1.freq - assert result.freqstr == '25H' - - with tm.assert_produces_warning(FutureWarning): - result = p2 - 1 - assert result.ordinal == (p3 - 25).ordinal - assert result.freq == p2.freq - assert result.freqstr == '25H' - - msg = ('Frequency must be positive, because it' - ' represents span: -25H') - with pytest.raises(ValueError, match=msg): - Period('2011-01', freq='-1D1H') - with pytest.raises(ValueError, match=msg): - Period('2011-01', freq='-1H1D') - with pytest.raises(ValueError, match=msg): - Period(ordinal=1, freq='-1D1H') - with pytest.raises(ValueError, match=msg): - Period(ordinal=1, freq='-1H1D') - - msg = ('Frequency must be positive, because it' - ' represents span: 0D') - with pytest.raises(ValueError, match=msg): - Period('2011-01', freq='0D0H') - with pytest.raises(ValueError, match=msg): - Period(ordinal=1, freq='0D0H') - - # You can only combine together day and intraday offsets - msg = ('Invalid frequency: 1W1D') - with pytest.raises(ValueError, match=msg): - Period('2011-01', freq='1W1D') - msg = ('Invalid frequency: 1D1W') - with pytest.raises(ValueError, match=msg): - Period('2011-01', freq='1D1W') - - @pytest.mark.parametrize('tzstr', ['Europe/Brussels', - 'Asia/Tokyo', 'US/Pacific']) - def test_timestamp_tz_arg(self, tzstr): - p = Period('1/1/2005', freq='M').to_timestamp(tz=tzstr) - exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) - exp_zone = pytz.timezone(tzstr).normalize(p) - - assert p == exp - assert p.tz == exp_zone.tzinfo - assert p.tz == exp.tz - - p = Period('1/1/2005', freq='3H').to_timestamp(tz=tzstr) - exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) - exp_zone = pytz.timezone(tzstr).normalize(p) - - assert p == exp - assert p.tz == exp_zone.tzinfo - assert p.tz == exp.tz - - p = Period('1/1/2005', freq='A').to_timestamp(freq='A', tz=tzstr) - exp = Timestamp('31/12/2005', tz='UTC').tz_convert(tzstr) - exp_zone = pytz.timezone(tzstr).normalize(p) - - assert p == exp - assert p.tz == exp_zone.tzinfo - assert p.tz == exp.tz - - p = Period('1/1/2005', freq='A').to_timestamp(freq='3H', tz=tzstr) - exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) - exp_zone = pytz.timezone(tzstr).normalize(p) - - assert p == exp - assert p.tz == exp_zone.tzinfo - assert p.tz == exp.tz - - @pytest.mark.parametrize('tzstr', ['dateutil/Europe/Brussels', - 'dateutil/Asia/Tokyo', - 'dateutil/US/Pacific']) - def test_timestamp_tz_arg_dateutil(self, tzstr): - from pandas._libs.tslibs.timezones import dateutil_gettz - from pandas._libs.tslibs.timezones import maybe_get_tz - tz = maybe_get_tz(tzstr) - p = Period('1/1/2005', freq='M').to_timestamp(tz=tz) - exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) - assert p == exp - assert p.tz == dateutil_gettz(tzstr.split('/', 1)[1]) - assert p.tz == exp.tz - - p = Period('1/1/2005', freq='M').to_timestamp(freq='3H', tz=tz) - exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) - assert p == exp - assert p.tz == dateutil_gettz(tzstr.split('/', 1)[1]) - assert p.tz == exp.tz - - def test_timestamp_tz_arg_dateutil_from_string(self): - from pandas._libs.tslibs.timezones import dateutil_gettz - p = Period('1/1/2005', - freq='M').to_timestamp(tz='dateutil/Europe/Brussels') - assert p.tz == dateutil_gettz('Europe/Brussels') - - def test_timestamp_mult(self): - p = Period('2011-01', freq='M') - assert p.to_timestamp(how='S') == Timestamp('2011-01-01') - expected = Timestamp('2011-02-01') - Timedelta(1, 'ns') - assert p.to_timestamp(how='E') == expected - - p = Period('2011-01', freq='3M') - assert p.to_timestamp(how='S') == Timestamp('2011-01-01') - expected = Timestamp('2011-04-01') - Timedelta(1, 'ns') - assert p.to_timestamp(how='E') == expected - +class TestPeriodConstruction(object): def test_construction(self): i1 = Period('1/1/2005', freq='M') i2 = Period('Jan 2005') @@ -509,35 +235,253 @@ def test_period_constructor_offsets(self): pytest.raises(ValueError, Period, '2007-1-1', freq='X') - def test_freq_str(self): - i1 = Period('1982', freq='Min') - assert i1.freq == offsets.Minute() - assert i1.freqstr == 'T' + def test_invalid_arguments(self): + with pytest.raises(ValueError): + Period(datetime.now()) + with pytest.raises(ValueError): + Period(datetime.now().date()) + + with pytest.raises(ValueError): + Period(1.6, freq='D') + with pytest.raises(ValueError): + Period(ordinal=1.6, freq='D') + with pytest.raises(ValueError): + Period(ordinal=2, value=1, freq='D') + + with pytest.raises(ValueError): + Period(month=1) + + with pytest.raises(ValueError): + Period('-2000', 'A') + with pytest.raises(DateParseError): + Period('0', 'A') + with pytest.raises(DateParseError): + Period('1/1/-2000', 'A') - def test_period_deprecated_freq(self): - cases = {"M": ["MTH", "MONTH", "MONTHLY", "Mth", "month", "monthly"], - "B": ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY", "bus"], - "D": ["DAY", "DLY", "DAILY", "Day", "Dly", "Daily"], - "H": ["HR", "HOUR", "HRLY", "HOURLY", "hr", "Hour", "HRly"], - "T": ["minute", "MINUTE", "MINUTELY", "minutely"], - "S": ["sec", "SEC", "SECOND", "SECONDLY", "second"], - "L": ["MILLISECOND", "MILLISECONDLY", "millisecond"], - "U": ["MICROSECOND", "MICROSECONDLY", "microsecond"], - "N": ["NANOSECOND", "NANOSECONDLY", "nanosecond"]} + def test_constructor_corner(self): + expected = Period('2007-01', freq='2M') + assert Period(year=2007, month=1, freq='2M') == expected - msg = pd._libs.tslibs.frequencies.INVALID_FREQ_ERR_MSG - for exp, freqs in iteritems(cases): - for freq in freqs: - with pytest.raises(ValueError, match=msg): - Period('2016-03-01 09:00', freq=freq) - with pytest.raises(ValueError, match=msg): - Period(ordinal=1, freq=freq) + assert Period(None) is NaT - # check supported freq-aliases still works - p1 = Period('2016-03-01 09:00', freq=exp) - p2 = Period(ordinal=1, freq=exp) - assert isinstance(p1, Period) - assert isinstance(p2, Period) + p = Period('2007-01-01', freq='D') + + result = Period(p, freq='A') + exp = Period('2007', freq='A') + assert result == exp + + def test_constructor_infer_freq(self): + p = Period('2007-01-01') + assert p.freq == 'D' + + p = Period('2007-01-01 07') + assert p.freq == 'H' + + p = Period('2007-01-01 07:10') + assert p.freq == 'T' + + p = Period('2007-01-01 07:10:15') + assert p.freq == 'S' + + p = Period('2007-01-01 07:10:15.123') + assert p.freq == 'L' + + p = Period('2007-01-01 07:10:15.123000') + assert p.freq == 'L' + + p = Period('2007-01-01 07:10:15.123400') + assert p.freq == 'U' + + def test_multiples(self): + result1 = Period('1989', freq='2A') + result2 = Period('1989', freq='A') + assert result1.ordinal == result2.ordinal + assert result1.freqstr == '2A-DEC' + assert result2.freqstr == 'A-DEC' + assert result1.freq == offsets.YearEnd(2) + assert result2.freq == offsets.YearEnd() + + with tm.assert_produces_warning(FutureWarning): + assert (result1 + 1).ordinal == result1.ordinal + 2 + assert (1 + result1).ordinal == result1.ordinal + 2 + assert (result1 - 1).ordinal == result2.ordinal - 2 + assert (-1 + result1).ordinal == result2.ordinal - 2 + + @pytest.mark.parametrize('month', MONTHS) + def test_period_cons_quarterly(self, month): + # bugs in scikits.timeseries + freq = 'Q-%s' % month + exp = Period('1989Q3', freq=freq) + assert '1989Q3' in str(exp) + stamp = exp.to_timestamp('D', how='end') + p = Period(stamp, freq=freq) + assert p == exp + + stamp = exp.to_timestamp('3D', how='end') + p = Period(stamp, freq=freq) + assert p == exp + + @pytest.mark.parametrize('month', MONTHS) + def test_period_cons_annual(self, month): + # bugs in scikits.timeseries + freq = 'A-%s' % month + exp = Period('1989', freq=freq) + stamp = exp.to_timestamp('D', how='end') + timedelta(days=30) + p = Period(stamp, freq=freq) + + with tm.assert_produces_warning(FutureWarning): + assert p == exp + 1 + assert isinstance(p, Period) + + @pytest.mark.parametrize('day', DAYS) + @pytest.mark.parametrize('num', range(10, 17)) + def test_period_cons_weekly(self, num, day): + daystr = '2011-02-%d' % num + freq = 'W-%s' % day + + result = Period(daystr, freq=freq) + expected = Period(daystr, freq='D').asfreq(freq) + assert result == expected + assert isinstance(result, Period) + + def test_period_from_ordinal(self): + p = Period('2011-01', freq='M') + res = Period._from_ordinal(p.ordinal, freq='M') + assert p == res + assert isinstance(res, Period) + + def test_period_cons_nat(self): + p = Period('NaT', freq='M') + assert p is NaT + + p = Period('nat', freq='W-SUN') + assert p is NaT + + p = Period(iNaT, freq='D') + assert p is NaT + + p = Period(iNaT, freq='3D') + assert p is NaT + + p = Period(iNaT, freq='1D1H') + assert p is NaT + + p = Period('NaT') + assert p is NaT + + p = Period(iNaT) + assert p is NaT + + def test_period_cons_mult(self): + p1 = Period('2011-01', freq='3M') + p2 = Period('2011-01', freq='M') + assert p1.ordinal == p2.ordinal + + assert p1.freq == offsets.MonthEnd(3) + assert p1.freqstr == '3M' + + assert p2.freq == offsets.MonthEnd() + assert p2.freqstr == 'M' + + with tm.assert_produces_warning(FutureWarning): + result = p1 + 1 + assert result.ordinal == (p2 + 3).ordinal + + assert result.freq == p1.freq + assert result.freqstr == '3M' + + with tm.assert_produces_warning(FutureWarning): + result = p1 - 1 + assert result.ordinal == (p2 - 3).ordinal + assert result.freq == p1.freq + assert result.freqstr == '3M' + + msg = ('Frequency must be positive, because it' + ' represents span: -3M') + with pytest.raises(ValueError, match=msg): + Period('2011-01', freq='-3M') + + msg = ('Frequency must be positive, because it' ' represents span: 0M') + with pytest.raises(ValueError, match=msg): + Period('2011-01', freq='0M') + + def test_period_cons_combined(self): + p = [(Period('2011-01', freq='1D1H'), + Period('2011-01', freq='1H1D'), + Period('2011-01', freq='H')), + (Period(ordinal=1, freq='1D1H'), + Period(ordinal=1, freq='1H1D'), + Period(ordinal=1, freq='H'))] + + for p1, p2, p3 in p: + assert p1.ordinal == p3.ordinal + assert p2.ordinal == p3.ordinal + + assert p1.freq == offsets.Hour(25) + assert p1.freqstr == '25H' + + assert p2.freq == offsets.Hour(25) + assert p2.freqstr == '25H' + + assert p3.freq == offsets.Hour() + assert p3.freqstr == 'H' + + with tm.assert_produces_warning(FutureWarning): + result = p1 + 1 + assert result.ordinal == (p3 + 25).ordinal + assert result.freq == p1.freq + assert result.freqstr == '25H' + + with tm.assert_produces_warning(FutureWarning): + result = p2 + 1 + assert result.ordinal == (p3 + 25).ordinal + assert result.freq == p2.freq + assert result.freqstr == '25H' + + with tm.assert_produces_warning(FutureWarning): + result = p1 - 1 + assert result.ordinal == (p3 - 25).ordinal + assert result.freq == p1.freq + assert result.freqstr == '25H' + + with tm.assert_produces_warning(FutureWarning): + result = p2 - 1 + assert result.ordinal == (p3 - 25).ordinal + assert result.freq == p2.freq + assert result.freqstr == '25H' + + msg = ('Frequency must be positive, because it' + ' represents span: -25H') + with pytest.raises(ValueError, match=msg): + Period('2011-01', freq='-1D1H') + with pytest.raises(ValueError, match=msg): + Period('2011-01', freq='-1H1D') + with pytest.raises(ValueError, match=msg): + Period(ordinal=1, freq='-1D1H') + with pytest.raises(ValueError, match=msg): + Period(ordinal=1, freq='-1H1D') + + msg = ('Frequency must be positive, because it' + ' represents span: 0D') + with pytest.raises(ValueError, match=msg): + Period('2011-01', freq='0D0H') + with pytest.raises(ValueError, match=msg): + Period(ordinal=1, freq='0D0H') + + # You can only combine together day and intraday offsets + msg = ('Invalid frequency: 1W1D') + with pytest.raises(ValueError, match=msg): + Period('2011-01', freq='1W1D') + msg = ('Invalid frequency: 1D1W') + with pytest.raises(ValueError, match=msg): + Period('2011-01', freq='1D1W') + + +class TestPeriodMethods(object): + def test_round_trip(self): + p = Period('2000Q1') + new_p = tm.round_trip_pickle(p) + assert new_p == p def test_hash(self): assert (hash(Period('2011-01', freq='M')) == @@ -552,40 +496,79 @@ def test_hash(self): assert (hash(Period('2011-01', freq='M')) != hash(Period('2011-02', freq='M'))) - def test_repr(self): - p = Period('Jan-2000') - assert '2000-01' in repr(p) + # -------------------------------------------------------------- + # to_timestamp - p = Period('2000-12-15') - assert '2000-12-15' in repr(p) + @pytest.mark.parametrize('tzstr', ['Europe/Brussels', + 'Asia/Tokyo', 'US/Pacific']) + def test_to_timestamp_tz_arg(self, tzstr): + p = Period('1/1/2005', freq='M').to_timestamp(tz=tzstr) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) + exp_zone = pytz.timezone(tzstr).normalize(p) - def test_repr_nat(self): - p = Period('nat', freq='M') - assert repr(NaT) in repr(p) + assert p == exp + assert p.tz == exp_zone.tzinfo + assert p.tz == exp.tz - def test_millisecond_repr(self): - p = Period('2000-01-01 12:15:02.123') + p = Period('1/1/2005', freq='3H').to_timestamp(tz=tzstr) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) + exp_zone = pytz.timezone(tzstr).normalize(p) - assert repr(p) == "Period('2000-01-01 12:15:02.123', 'L')" + assert p == exp + assert p.tz == exp_zone.tzinfo + assert p.tz == exp.tz - def test_microsecond_repr(self): - p = Period('2000-01-01 12:15:02.123567') + p = Period('1/1/2005', freq='A').to_timestamp(freq='A', tz=tzstr) + exp = Timestamp('31/12/2005', tz='UTC').tz_convert(tzstr) + exp_zone = pytz.timezone(tzstr).normalize(p) - assert repr(p) == "Period('2000-01-01 12:15:02.123567', 'U')" + assert p == exp + assert p.tz == exp_zone.tzinfo + assert p.tz == exp.tz - def test_strftime(self): - p = Period('2000-1-1 12:34:12', freq='S') - res = p.strftime('%Y-%m-%d %H:%M:%S') - assert res == '2000-01-01 12:34:12' - assert isinstance(res, text_type) # GH3363 + p = Period('1/1/2005', freq='A').to_timestamp(freq='3H', tz=tzstr) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) + exp_zone = pytz.timezone(tzstr).normalize(p) - def test_sub_delta(self): - left, right = Period('2011', freq='A'), Period('2007', freq='A') - result = left - right - assert result == 4 * right.freq + assert p == exp + assert p.tz == exp_zone.tzinfo + assert p.tz == exp.tz - with pytest.raises(period.IncompatibleFrequency): - left - Period('2007-01', freq='M') + @pytest.mark.parametrize('tzstr', ['dateutil/Europe/Brussels', + 'dateutil/Asia/Tokyo', + 'dateutil/US/Pacific']) + def test_to_timestamp_tz_arg_dateutil(self, tzstr): + from pandas._libs.tslibs.timezones import dateutil_gettz + from pandas._libs.tslibs.timezones import maybe_get_tz + tz = maybe_get_tz(tzstr) + p = Period('1/1/2005', freq='M').to_timestamp(tz=tz) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) + assert p == exp + assert p.tz == dateutil_gettz(tzstr.split('/', 1)[1]) + assert p.tz == exp.tz + + p = Period('1/1/2005', freq='M').to_timestamp(freq='3H', tz=tz) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) + assert p == exp + assert p.tz == dateutil_gettz(tzstr.split('/', 1)[1]) + assert p.tz == exp.tz + + def test_to_timestamp_tz_arg_dateutil_from_string(self): + from pandas._libs.tslibs.timezones import dateutil_gettz + p = Period('1/1/2005', + freq='M').to_timestamp(tz='dateutil/Europe/Brussels') + assert p.tz == dateutil_gettz('Europe/Brussels') + + def test_to_timestamp_mult(self): + p = Period('2011-01', freq='M') + assert p.to_timestamp(how='S') == Timestamp('2011-01-01') + expected = Timestamp('2011-02-01') - Timedelta(1, 'ns') + assert p.to_timestamp(how='E') == expected + + p = Period('2011-01', freq='3M') + assert p.to_timestamp(how='S') == Timestamp('2011-01-01') + expected = Timestamp('2011-04-01') - Timedelta(1, 'ns') + assert p.to_timestamp(how='E') == expected def test_to_timestamp(self): p = Period('1982', freq='A') @@ -648,6 +631,103 @@ def _ex(p): result = p.to_timestamp('5S', how='start') assert result == expected + # -------------------------------------------------------------- + # Rendering: __repr__, strftime, etc + + def test_repr(self): + p = Period('Jan-2000') + assert '2000-01' in repr(p) + + p = Period('2000-12-15') + assert '2000-12-15' in repr(p) + + def test_repr_nat(self): + p = Period('nat', freq='M') + assert repr(NaT) in repr(p) + + def test_millisecond_repr(self): + p = Period('2000-01-01 12:15:02.123') + + assert repr(p) == "Period('2000-01-01 12:15:02.123', 'L')" + + def test_microsecond_repr(self): + p = Period('2000-01-01 12:15:02.123567') + + assert repr(p) == "Period('2000-01-01 12:15:02.123567', 'U')" + + def test_strftime(self): + # GH#3363 + p = Period('2000-1-1 12:34:12', freq='S') + res = p.strftime('%Y-%m-%d %H:%M:%S') + assert res == '2000-01-01 12:34:12' + assert isinstance(res, text_type) + + +class TestPeriodProperties(object): + "Test properties such as year, month, weekday, etc...." + + @pytest.mark.parametrize('freq', ['A', 'M', 'D', 'H']) + def test_is_leap_year(self, freq): + # GH 13727 + p = Period('2000-01-01 00:00:00', freq=freq) + assert p.is_leap_year + assert isinstance(p.is_leap_year, bool) + + p = Period('1999-01-01 00:00:00', freq=freq) + assert not p.is_leap_year + + p = Period('2004-01-01 00:00:00', freq=freq) + assert p.is_leap_year + + p = Period('2100-01-01 00:00:00', freq=freq) + assert not p.is_leap_year + + def test_quarterly_negative_ordinals(self): + p = Period(ordinal=-1, freq='Q-DEC') + assert p.year == 1969 + assert p.quarter == 4 + assert isinstance(p, Period) + + p = Period(ordinal=-2, freq='Q-DEC') + assert p.year == 1969 + assert p.quarter == 3 + assert isinstance(p, Period) + + p = Period(ordinal=-2, freq='M') + assert p.year == 1969 + assert p.month == 11 + assert isinstance(p, Period) + + def test_freq_str(self): + i1 = Period('1982', freq='Min') + assert i1.freq == offsets.Minute() + assert i1.freqstr == 'T' + + def test_period_deprecated_freq(self): + cases = {"M": ["MTH", "MONTH", "MONTHLY", "Mth", "month", "monthly"], + "B": ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY", "bus"], + "D": ["DAY", "DLY", "DAILY", "Day", "Dly", "Daily"], + "H": ["HR", "HOUR", "HRLY", "HOURLY", "hr", "Hour", "HRly"], + "T": ["minute", "MINUTE", "MINUTELY", "minutely"], + "S": ["sec", "SEC", "SECOND", "SECONDLY", "second"], + "L": ["MILLISECOND", "MILLISECONDLY", "millisecond"], + "U": ["MICROSECOND", "MICROSECONDLY", "microsecond"], + "N": ["NANOSECOND", "NANOSECONDLY", "nanosecond"]} + + msg = pd._libs.tslibs.frequencies.INVALID_FREQ_ERR_MSG + for exp, freqs in iteritems(cases): + for freq in freqs: + with pytest.raises(ValueError, match=msg): + Period('2016-03-01 09:00', freq=freq) + with pytest.raises(ValueError, match=msg): + Period(ordinal=1, freq=freq) + + # check supported freq-aliases still works + p1 = Period('2016-03-01 09:00', freq=exp) + p2 = Period(ordinal=1, freq=exp) + assert isinstance(p1, Period) + assert isinstance(p2, Period) + def test_start_time(self): freq_lst = ['A', 'Q', 'M', 'D', 'H', 'T', 'S'] xp = datetime(2012, 1, 1) @@ -854,72 +934,6 @@ def test_properties_secondly(self): assert Period(freq='Min', year=2012, month=2, day=1, hour=0, minute=0, second=0).days_in_month == 29 - def test_constructor_corner(self): - expected = Period('2007-01', freq='2M') - assert Period(year=2007, month=1, freq='2M') == expected - - pytest.raises(ValueError, Period, datetime.now()) - pytest.raises(ValueError, Period, datetime.now().date()) - pytest.raises(ValueError, Period, 1.6, freq='D') - pytest.raises(ValueError, Period, ordinal=1.6, freq='D') - pytest.raises(ValueError, Period, ordinal=2, value=1, freq='D') - assert Period(None) is NaT - pytest.raises(ValueError, Period, month=1) - - p = Period('2007-01-01', freq='D') - - result = Period(p, freq='A') - exp = Period('2007', freq='A') - assert result == exp - - def test_constructor_infer_freq(self): - p = Period('2007-01-01') - assert p.freq == 'D' - - p = Period('2007-01-01 07') - assert p.freq == 'H' - - p = Period('2007-01-01 07:10') - assert p.freq == 'T' - - p = Period('2007-01-01 07:10:15') - assert p.freq == 'S' - - p = Period('2007-01-01 07:10:15.123') - assert p.freq == 'L' - - p = Period('2007-01-01 07:10:15.123000') - assert p.freq == 'L' - - p = Period('2007-01-01 07:10:15.123400') - assert p.freq == 'U' - - def test_badinput(self): - pytest.raises(ValueError, Period, '-2000', 'A') - pytest.raises(DateParseError, Period, '0', 'A') - pytest.raises(DateParseError, Period, '1/1/-2000', 'A') - - def test_multiples(self): - result1 = Period('1989', freq='2A') - result2 = Period('1989', freq='A') - assert result1.ordinal == result2.ordinal - assert result1.freqstr == '2A-DEC' - assert result2.freqstr == 'A-DEC' - assert result1.freq == offsets.YearEnd(2) - assert result2.freq == offsets.YearEnd() - - with tm.assert_produces_warning(FutureWarning): - assert (result1 + 1).ordinal == result1.ordinal + 2 - assert (1 + result1).ordinal == result1.ordinal + 2 - assert (result1 - 1).ordinal == result2.ordinal - 2 - assert (-1 + result1).ordinal == result2.ordinal - 2 - - def test_round_trip(self): - - p = Period('2000Q1') - new_p = tm.round_trip_pickle(p) - assert new_p == p - class TestPeriodField(object): @@ -1013,38 +1027,49 @@ def test_period_nat_comp(self): assert not left >= right -class TestMethods(object): +class TestArithmetic(object): + + def test_sub_delta(self): + left, right = Period('2011', freq='A'), Period('2007', freq='A') + result = left - right + assert result == 4 * right.freq + + with pytest.raises(period.IncompatibleFrequency): + left - Period('2007-01', freq='M') - def test_add(self): - dt1 = Period(freq='D', year=2008, month=1, day=1) - dt2 = Period(freq='D', year=2008, month=1, day=2) + def test_add_integer(self): + per1 = Period(freq='D', year=2008, month=1, day=1) + per1 = Period(freq='D', year=2008, month=1, day=2) with tm.assert_produces_warning(FutureWarning): - assert dt1 + 1 == dt2 - assert 1 + dt1 == dt2 + assert per1 + 1 == per2 + assert 1 + per1 == per2 - def test_add_pdnat(self): + def test_add_sub_nat(self): + # GH#13071 p = Period('2011-01', freq='M') assert p + NaT is NaT assert NaT + p is NaT + assert p - NaT is NaT + assert NaT - p is NaT p = Period('NaT', freq='M') assert p + NaT is NaT assert NaT + p is NaT + assert p - NaT is NaT + assert NaT - p is NaT - def test_add_raises(self): - # GH 4731 - dt1 = Period(freq='D', year=2008, month=1, day=1) - dt2 = Period(freq='D', year=2008, month=1, day=2) - msg = r"unsupported operand type\(s\)" - with pytest.raises(TypeError, match=msg): - dt1 + "str" + def test_add_invalid(self): + # GH#4731 + per1 = Period(freq='D', year=2008, month=1, day=1) + per2 = Period(freq='D', year=2008, month=1, day=2) msg = r"unsupported operand type\(s\)" with pytest.raises(TypeError, match=msg): - "str" + dt1 - + per1 + "str" + with pytest.raises(TypeError, match=msg): + "str" + per1 with pytest.raises(TypeError, match=msg): - dt1 + dt2 + per1 + per2 boxes = [lambda x: x, lambda x: pd.Series([x]), lambda x: pd.Index([x])] ids = ['identity', 'Series', 'Index'] @@ -1071,16 +1096,16 @@ def test_add_timestamp_raises(self, rbox, lbox): lbox(per) + rbox(per) def test_sub(self): - dt1 = Period('2011-01-01', freq='D') - dt2 = Period('2011-01-15', freq='D') + per1 = Period('2011-01-01', freq='D') + per2 = Period('2011-01-15', freq='D') - off = dt1.freq - assert dt1 - dt2 == -14 * off - assert dt2 - dt1 == 14 * off + off = per1.freq + assert per1 - per2 == -14 * off + assert per2 - per1 == 14 * off msg = r"Input has different freq=M from Period\(freq=D\)" with pytest.raises(period.IncompatibleFrequency, match=msg): - dt1 - Period('2011-02', freq='M') + per1 - Period('2011-02', freq='M') def test_add_offset(self): # freq is DateOffset @@ -1298,16 +1323,6 @@ def test_add_offset_nat(self): else: assert o + p is NaT - def test_sub_pdnat(self): - # GH 13071 - p = Period('2011-01', freq='M') - assert p - NaT is NaT - assert NaT - p is NaT - - p = Period('NaT', freq='M') - assert p - NaT is NaT - assert NaT - p is NaT - def test_sub_offset(self): # freq is DateOffset for freq in ['A', '2A', '3A']: From b3665f66e74105a0f6c1a03846c9058549d4e032 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Nov 2018 17:40:30 -0800 Subject: [PATCH 03/11] de-duplicate and parametrize-over-box many tests --- pandas/core/arrays/datetimes.py | 6 +- pandas/tests/arithmetic/test_datetime64.py | 128 ++++++++++++------ .../tests/scalar/timedelta/test_arithmetic.py | 68 ++++------ 3 files changed, 121 insertions(+), 81 deletions(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 926228f267049..461e64b44db26 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -111,7 +111,7 @@ def wrapper(self, other): # string that cannot be parsed to Timestamp return ops.invalid_comparison(self, other, op) - result = meth(self, other) + result = op(self.asi8, other.view('i8')) if isna(other): result.fill(nat_result) elif lib.is_scalar(other): @@ -222,6 +222,10 @@ def __new__(cls, values, freq=None, tz=None, dtype=None): # if dtype has an embedded tz, capture it tz = dtl.validate_tz_from_dtype(dtype, tz) + if isinstance(values, ABCSeries): + # extract to ndarray or DatetimeIndex + values = values._values + if isinstance(values, DatetimeArrayMixin): # extract nanosecond unix timestamps values = values.asi8 diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index b25e9a9a485c2..0fb50a15819e9 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -25,6 +25,8 @@ from pandas import ( Timestamp, Timedelta, Period, Series, date_range, NaT, DatetimeIndex, TimedeltaIndex) +from pandas.core.arrays import ( + DatetimeArrayMixin as DatetimeArray, TimedeltaArrayMixin as TimedeltaArray) # ------------------------------------------------------------------ @@ -158,12 +160,16 @@ 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): + def test_dt64ser_cmp_date_invalid(self, box_with_datetime): # GH#19800 datetime.date comparison raises to # match DatetimeIndex/Timestamp. This also matches the behavior # of stdlib datetime.datetime - ser = pd.Series(pd.date_range('20010101', periods=10), name='dates') + box = box_with_datetime + + ser = pd.date_range('20010101', periods=10) date = ser.iloc[0].to_pydatetime().date() + + ser = tm.box_expected(ser, box) assert not (ser == date).any() assert (ser != date).all() with pytest.raises(TypeError): @@ -225,22 +231,37 @@ def test_timestamp_compare_series(self, left, right): result = right_f(pd.Timestamp("nat"), s_nat) tm.assert_series_equal(result, expected) - def test_timestamp_equality(self): + def test_dt64arr_timestamp_equality(self, box_with_datetime): # GH#11034 + box = box_with_datetime + xbox = box if box not in [pd.Index, DatetimeArray] else np.ndarray + ser = pd.Series([pd.Timestamp('2000-01-29 01:59:00'), 'NaT']) + ser = tm.box_expected(ser, box) + result = ser != ser - tm.assert_series_equal(result, pd.Series([False, True])) + expected = tm.box_expected([False, True], xbox) + tm.assert_equal(result, expected) + result = ser != ser[0] - tm.assert_series_equal(result, pd.Series([False, True])) + expected = tm.box_expected([False, True], xbox) + tm.assert_equal(result, expected) + result = ser != ser[1] - tm.assert_series_equal(result, pd.Series([True, True])) + expected = tm.box_expected([True, True], xbox) + tm.assert_equal(result, expected) result = ser == ser - tm.assert_series_equal(result, pd.Series([True, False])) + expected = tm.box_expected([True, False], xbox) + tm.assert_equal(result, expected) + result = ser == ser[0] - tm.assert_series_equal(result, pd.Series([True, False])) + expected = tm.box_expected([True, False], xbox) + tm.assert_equal(result, expected) + result = ser == ser[1] - tm.assert_series_equal(result, pd.Series([False, False])) + expected = tm.box_expected([False, False], xbox) + tm.assert_equal(result, expected) class TestDatetimeIndexComparisons(object): @@ -629,7 +650,7 @@ def test_dti_cmp_object_dtype(self): # Arithmetic class TestFrameArithmetic(object): - def test_dt64arr_sub_dtscalar(self, box): + def test_dt64arr_sub_timestamp(self, box): # GH#8554, GH#22163 DataFrame op should _not_ return dt64 dtype idx = pd.date_range('2013-01-01', periods=3) idx = tm.box_expected(idx, box) @@ -643,28 +664,39 @@ def test_dt64arr_sub_dtscalar(self, box): result = idx - ts tm.assert_equal(result, expected) - def test_df_sub_datetime64_not_ns(self): + def test_dt64arr_sub_datetime64_not_ns(self, box): # GH#7996, GH#22163 ensure non-nano datetime64 is converted to nano - df = pd.DataFrame(pd.date_range('20130101', periods=3)) + # for DataFrame operation + + dti = pd.date_range('20130101', periods=3) + dtarr = tm.box_expected(dti, box) + dt64 = np.datetime64('2013-01-01') assert dt64.dtype == 'datetime64[D]' - res = df - dt64 - expected = pd.DataFrame([pd.Timedelta(days=0), pd.Timedelta(days=1), - pd.Timedelta(days=2)]) - tm.assert_frame_equal(res, expected) + + expected = pd.TimedeltaIndex(['0 Days', '1 Day', '2 Days']) + expected = tm.box_expected(expected, box) + + result = dtarr - dt64 + tm.assert_equal(result, expected) class TestTimestampSeriesArithmetic(object): - def test_timestamp_sub_series(self): - ser = pd.Series(pd.date_range('2014-03-17', periods=2, freq='D', - tz='US/Eastern')) + def test_dt64arr_sub_timestamp(self, box): + ser = pd.date_range('2014-03-17', periods=2, freq='D', + tz='US/Eastern') ts = ser[0] + # FIXME: transpose raises ValueError + ser = tm.box_expected(ser, box, transpose=False) + delta_series = pd.Series([np.timedelta64(0, 'D'), np.timedelta64(1, 'D')]) - tm.assert_series_equal(ser - ts, delta_series) - tm.assert_series_equal(ts - ser, -delta_series) + expected = tm.box_expected(delta_series, box, transpose=False) + + tm.assert_equal(ser - ts, expected) + tm.assert_equal(ts - ser, -expected) def test_dt64ser_sub_datetime_dtype(self): ts = Timestamp(datetime(1993, 1, 7, 13, 30, 00)) @@ -722,20 +754,23 @@ def check(get_ser, test_ser): if op_str not in ['__add__', '__radd__', '__sub__', '__rsub__']: check(dt2, td2) - @pytest.mark.parametrize('klass', [Series, pd.Index]) - def test_sub_datetime64_not_ns(self, klass): - # GH#7996 + def test_sub_datetime64_not_ns(self, box): + # GH#7996 operation with non-nano datetime64 scalar dt64 = np.datetime64('2013-01-01') assert dt64.dtype == 'datetime64[D]' - obj = klass(date_range('20130101', periods=3)) - res = obj - dt64 - expected = klass([Timedelta(days=0), Timedelta(days=1), - Timedelta(days=2)]) - tm.assert_equal(res, expected) + obj = date_range('20130101', periods=3) + obj = tm.box_expected(obj, box) - res = dt64 - obj - tm.assert_equal(res, -expected) + expected = TimedeltaIndex([Timedelta(days=0), Timedelta(days=1), + Timedelta(days=2)]) + expected = tm.box_expected(expected, box) + + result = obj - dt64 + tm.assert_equal(result, expected) + + result = dt64 - obj + tm.assert_equal(result, -expected) def test_sub_single_tz(self): # GH12290 @@ -1438,34 +1473,45 @@ def test_sub_dti_dti(self): result = dti2 - dti1 tm.assert_index_equal(result, expected) - @pytest.mark.parametrize('freq', [None, 'D']) - def test_sub_period(self, freq, box_with_datetime): + @pytest.mark.parametrize('dti_freq', [None, 'D']) + def test_dt64arr_add_sub_period(self, dti_freq, box_with_datetime): # GH#13078 # not supported, check TypeError p = pd.Period('2011-01-01', freq='D') - idx = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=freq) + idx = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=dti_freq) idx = tm.box_expected(idx, box_with_datetime) + with pytest.raises(TypeError): + idx + p + with pytest.raises(TypeError): + p + idx with pytest.raises(TypeError): idx - p - with pytest.raises(TypeError): p - idx - @pytest.mark.parametrize('op', [operator.add, ops.radd, - operator.sub, ops.rsub]) @pytest.mark.parametrize('pi_freq', ['D', 'W', 'Q', 'H']) @pytest.mark.parametrize('dti_freq', [None, 'D']) - def test_dti_sub_pi(self, dti_freq, pi_freq, op, box): + def test_dti_add_sub_pi(self, dti_freq, pi_freq, + box_with_datetime, box_with_period): # GH#20049 subtracting PeriodIndex should raise TypeError + box = box_with_datetime + dti = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=dti_freq) pi = dti.to_period(pi_freq) - dti = tm.box_expected(dti, box) - # TODO: Also box pi? + dtarr = tm.box_expected(dti, box) + parr = tm.box_expected(pi, box_with_period) + + with pytest.raises(TypeError): + dtarr + parr + with pytest.raises(TypeError): + parr + dtarr + with pytest.raises(TypeError): + dtarr - parr with pytest.raises(TypeError): - op(dti, pi) + parr - dtarr # ------------------------------------------------------------------- # TODO: Most of this block is moved from series or frame tests, needs diff --git a/pandas/tests/scalar/timedelta/test_arithmetic.py b/pandas/tests/scalar/timedelta/test_arithmetic.py index 79fa49b564ad6..b9af31db1d6e6 100644 --- a/pandas/tests/scalar/timedelta/test_arithmetic.py +++ b/pandas/tests/scalar/timedelta/test_arithmetic.py @@ -80,11 +80,6 @@ def test_td_add_datetimelike_scalar(self, op): result = op(td, NaT) assert result is NaT - with pytest.raises(TypeError): - op(td, 2) - with pytest.raises(TypeError): - op(td, 2.0) - @pytest.mark.parametrize('op', [operator.add, ops.radd]) def test_td_add_td(self, op): td = Timedelta(10, unit='d') @@ -125,25 +120,41 @@ def test_td_sub_td(self): def test_td_sub_pytimedelta(self): td = Timedelta(10, unit='d') expected = Timedelta(0, unit='ns') + result = td - td.to_pytimedelta() assert isinstance(result, Timedelta) assert result == expected + result = td.to_pytimedelta() - td + assert isinstance(result, Timedelta) + assert result == expected + def test_td_sub_timedelta64(self): td = Timedelta(10, unit='d') expected = Timedelta(0, unit='ns') + result = td - td.to_timedelta64() assert isinstance(result, Timedelta) assert result == expected + result = td.to_timedelta64() - td + assert isinstance(result, Timedelta) + assert result == expected + def test_td_sub_nat(self): + # In this context pd.NaT is treated as timedelta-like td = Timedelta(10, unit='d') result = td - NaT assert result is NaT def test_td_sub_td64_nat(self): td = Timedelta(10, unit='d') - result = td - np.timedelta64('NaT') + td_nat = np.timedelta64('NaT') + + result = td - td_nat + assert result is NaT + + result = td_nat - td assert result is NaT def test_td_sub_offset(self): @@ -152,28 +163,17 @@ def test_td_sub_offset(self): assert isinstance(result, Timedelta) assert result == Timedelta(239, unit='h') - def test_td_sub_numeric_raises(self): - td = td = Timedelta(10, unit='d') - with pytest.raises(TypeError): - td - 2 - with pytest.raises(TypeError): - td - 2.0 - - def test_td_rsub_pytimedelta(self): + def test_td_add_sub_numeric_raises(self): td = Timedelta(10, unit='d') - expected = Timedelta(0, unit='ns') - - result = td.to_pytimedelta() - td - assert isinstance(result, Timedelta) - assert result == expected - - def test_td_rsub_timedelta64(self): - td = Timedelta(10, unit='d') - expected = Timedelta(0, unit='ns') - - result = td.to_timedelta64() - td - assert isinstance(result, Timedelta) - assert result == expected + for other in [2, 2.0, np.int64(2), np.float64(2)]: + with pytest.raises(TypeError): + td + other + with pytest.raises(TypeError): + other + td + with pytest.raises(TypeError): + td - other + with pytest.raises(TypeError): + other - td def test_td_rsub_nat(self): td = Timedelta(10, unit='d') @@ -183,23 +183,11 @@ def test_td_rsub_nat(self): result = np.datetime64('NaT') - td assert result is NaT - def test_td_rsub_td64_nat(self): - td = Timedelta(10, unit='d') - result = np.timedelta64('NaT') - td - assert result is NaT - def test_td_rsub_offset(self): result = pd.offsets.Hour(1) - Timedelta(10, unit='d') assert isinstance(result, Timedelta) assert result == Timedelta(-239, unit='h') - def test_td_rsub_numeric_raises(self): - td = td = Timedelta(10, unit='d') - with pytest.raises(TypeError): - 2 - td - with pytest.raises(TypeError): - 2.0 - td - def test_td_sub_timedeltalike_object_dtype_array(self): # GH 21980 arr = np.array([Timestamp('20130101 9:01'), @@ -686,6 +674,8 @@ def test_rdivmod_invalid(self): with pytest.raises(TypeError): divmod(np.array([22, 24]), td) + # ---------------------------------------------------------------- + @pytest.mark.parametrize('op', [ operator.mul, ops.rmul, From e1b9c8d326929051d25d4ffa8b1b7a41323325e3 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Nov 2018 17:53:21 -0800 Subject: [PATCH 04/11] decrease verbosity --- pandas/tests/arithmetic/test_datetime64.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 0fb50a15819e9..cce0d6b3ec931 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -1496,12 +1496,10 @@ def test_dt64arr_add_sub_period(self, dti_freq, box_with_datetime): def test_dti_add_sub_pi(self, dti_freq, pi_freq, box_with_datetime, box_with_period): # GH#20049 subtracting PeriodIndex should raise TypeError - box = box_with_datetime - dti = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=dti_freq) pi = dti.to_period(pi_freq) - dtarr = tm.box_expected(dti, box) + dtarr = tm.box_expected(dti, box_with_datetime) parr = tm.box_expected(pi, box_with_period) with pytest.raises(TypeError): From f4775d255c9ca49891fdc7a5c9b3e9233ded8f90 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Nov 2018 17:59:33 -0800 Subject: [PATCH 05/11] flake8 fixup --- pandas/tests/arithmetic/test_datetime64.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index cce0d6b3ec931..a4850a28a77e4 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -693,7 +693,7 @@ def test_dt64arr_sub_timestamp(self, box): delta_series = pd.Series([np.timedelta64(0, 'D'), np.timedelta64(1, 'D')]) - expected = tm.box_expected(delta_series, box, transpose=False) + expected = tm.box_expected(delta_series, box, transpose=False) tm.assert_equal(ser - ts, expected) tm.assert_equal(ts - ser, -expected) From d355e9dcd9b17b891fe36e85874d2f01e8c765b8 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Nov 2018 19:21:15 -0800 Subject: [PATCH 06/11] typo fixup --- pandas/tests/scalar/period/test_period.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index 502fbf23a4688..55509308a6112 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -1039,7 +1039,7 @@ def test_sub_delta(self): def test_add_integer(self): per1 = Period(freq='D', year=2008, month=1, day=1) - per1 = Period(freq='D', year=2008, month=1, day=2) + per2 = Period(freq='D', year=2008, month=1, day=2) with tm.assert_produces_warning(FutureWarning): assert per1 + 1 == per2 assert 1 + per1 == per2 From fadb45adc300661ffefb39515c07ed64267a81ae Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Nov 2018 20:40:53 -0800 Subject: [PATCH 07/11] fixup remove unused import --- pandas/tests/arithmetic/test_datetime64.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index a4850a28a77e4..cd28e60b62562 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -25,8 +25,7 @@ from pandas import ( Timestamp, Timedelta, Period, Series, date_range, NaT, DatetimeIndex, TimedeltaIndex) -from pandas.core.arrays import ( - DatetimeArrayMixin as DatetimeArray, TimedeltaArrayMixin as TimedeltaArray) +from pandas.core.arrays import DatetimeArrayMixin as DatetimeArray # ------------------------------------------------------------------ From 497d33acfd9645f35a27cc33195a5879a954bfe1 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 14 Nov 2018 09:39:25 -0800 Subject: [PATCH 08/11] make utc_objs into a fixture --- pandas/conftest.py | 16 ++++++++++++++++ .../tests/scalar/timestamp/test_comparisons.py | 14 ++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index 03e09175bdb09..479471332a274 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -1,10 +1,12 @@ import importlib import os +from dateutil.tz import tzutc import hypothesis from hypothesis import strategies as st import numpy as np import pytest +from pytz import utc from pandas.compat import PY3 import pandas.util._test_decorators as td @@ -243,6 +245,20 @@ def datetime_tz_utc(): return timezone.utc +utc_objs = ['utc', utc, tzutc()] +if PY3: + from datetime import timezone + utc_objs.append(timezone.utc) + + +@pytest.fixture(params=utc_objs) +def utc_fixture(request): + """ + Fixture to provide variants of UTC timezone strings and tzinfo objects + """ + return request.param + + @pytest.fixture(params=['inner', 'outer', 'left', 'right']) def join_type(request): """ diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index 5f3d82d73d46b..67c73b073d7d6 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -5,19 +5,10 @@ import pytest import numpy as np -from dateutil.tz import tzutc -from pytz import utc - from pandas.compat import long, PY2 from pandas import Timestamp -utc_objs = ['utc', utc, tzutc()] -if not PY2: - from datetime import timezone - utc_objs.append(timezone.utc) - - class TestTimestampComparison(object): def test_comparison_object_array(self): # GH#15183 @@ -95,11 +86,10 @@ def test_compare_invalid(self): assert val != np.float64(1) assert val != np.int64(1) - @pytest.mark.parametrize('utc_obj', utc_objs) - def test_cant_compare_tz_naive_w_aware(self, utc_obj): + def test_cant_compare_tz_naive_w_aware(self, utc_objs): # see GH#1404 a = Timestamp('3/12/2012') - b = Timestamp('3/12/2012', tz=utc_obj) + b = Timestamp('3/12/2012', tz=utc_objs) with pytest.raises(TypeError): a == b From a27f1096bf06c1c79de9414ec566c6d8cf3e5849 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 14 Nov 2018 09:41:45 -0800 Subject: [PATCH 09/11] imports to the top --- pandas/tests/scalar/period/test_period.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index 55509308a6112..14b26bce42d87 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -14,6 +14,7 @@ from pandas._libs.tslibs import iNaT, period as libperiod from pandas._libs.tslibs.ccalendar import DAYS, MONTHS from pandas._libs.tslibs.parsing import DateParseError +from pandas._libs.tslibs.timezones import dateutil_gettz, maybe_get_tz class TestPeriodConstruction(object): @@ -538,8 +539,6 @@ def test_to_timestamp_tz_arg(self, tzstr): 'dateutil/Asia/Tokyo', 'dateutil/US/Pacific']) def test_to_timestamp_tz_arg_dateutil(self, tzstr): - from pandas._libs.tslibs.timezones import dateutil_gettz - from pandas._libs.tslibs.timezones import maybe_get_tz tz = maybe_get_tz(tzstr) p = Period('1/1/2005', freq='M').to_timestamp(tz=tz) exp = Timestamp('1/1/2005', tz='UTC').tz_convert(tzstr) @@ -554,7 +553,6 @@ def test_to_timestamp_tz_arg_dateutil(self, tzstr): assert p.tz == exp.tz def test_to_timestamp_tz_arg_dateutil_from_string(self): - from pandas._libs.tslibs.timezones import dateutil_gettz p = Period('1/1/2005', freq='M').to_timestamp(tz='dateutil/Europe/Brussels') assert p.tz == dateutil_gettz('Europe/Brussels') From c24b65f500a135ec0518551fc276c108216b4fe7 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 14 Nov 2018 10:32:07 -0800 Subject: [PATCH 10/11] fixup typo --- pandas/tests/scalar/timestamp/test_comparisons.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index 67c73b073d7d6..f293f8f161010 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -86,10 +86,10 @@ def test_compare_invalid(self): assert val != np.float64(1) assert val != np.int64(1) - def test_cant_compare_tz_naive_w_aware(self, utc_objs): + def test_cant_compare_tz_naive_w_aware(self, utc_fixture): # see GH#1404 a = Timestamp('3/12/2012') - b = Timestamp('3/12/2012', tz=utc_objs) + b = Timestamp('3/12/2012', tz=utc_fixture) with pytest.raises(TypeError): a == b From 89fe6d99b471f93a0f40053326e4129e5d511ae3 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 14 Nov 2018 16:15:16 -0800 Subject: [PATCH 11/11] fix location of tz check --- pandas/core/arrays/datetimes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 461e64b44db26..f83e6d0a99a04 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -209,9 +209,6 @@ def _simple_new(cls, values, freq=None, tz=None, **kwargs): return result def __new__(cls, values, freq=None, tz=None, dtype=None): - if tz is None and hasattr(values, 'tz'): - # e.g. DatetimeIndex - tz = values.tz if freq is None and hasattr(values, "freq"): # i.e. DatetimeArray, DatetimeIndex @@ -228,7 +225,10 @@ def __new__(cls, values, freq=None, tz=None, dtype=None): if isinstance(values, DatetimeArrayMixin): # extract nanosecond unix timestamps + if tz is None: + tz = values.tz values = values.asi8 + if values.dtype == 'i8': values = values.view('M8[ns]')