From 236122a9dfcaba89ec1232e72e7e1c2be30daac9 Mon Sep 17 00:00:00 2001 From: mturzanska Date: Mon, 2 Oct 2017 20:50:55 -0400 Subject: [PATCH 1/8] Tweak exception messages for datetimelike addition --- pandas/core/indexes/datetimelike.py | 2 +- pandas/core/indexes/datetimes.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index c3232627fce74..1d5a1ce11c0e0 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -656,7 +656,7 @@ def __add__(self, other): return self._add_delta(other) elif is_integer(other): return self.shift(other) - elif isinstance(other, (Timestamp, datetime)): + elif isinstance(other, (Timestamp, datetime, np.datetime64)): return self._add_datelike(other) else: # pragma: no cover return NotImplemented diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 9127864eab8a1..ef3af2acf46a7 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -747,7 +747,9 @@ def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike if other is libts.NaT: return self._nat_new(box=True) - raise TypeError("cannot add a datelike to a DatetimeIndex") + raise TypeError("cannot add {0} and {1}" + .format(type(self).__name__, + type(other).__name__)) def _sub_datelike(self, other): # subtract a datetime from myself, yielding a TimedeltaIndex From 708d1f8efbe19fb2d6314cbd4ce083fb772f71c4 Mon Sep 17 00:00:00 2001 From: mturzanska Date: Mon, 2 Oct 2017 20:55:01 -0400 Subject: [PATCH 2/8] Fix and extend tests --- pandas/tests/indexes/datetimes/test_ops.py | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py index 86e65feec04f3..068df653c186e 100644 --- a/pandas/tests/indexes/datetimes/test_ops.py +++ b/pandas/tests/indexes/datetimes/test_ops.py @@ -434,13 +434,35 @@ def test_add_iadd(self): tm.assert_index_equal(rng, expected) idx = DatetimeIndex(['2011-01-01', '2011-01-02']) - msg = "cannot add a datelike to a DatetimeIndex" + msg = "cannot add DatetimeIndex and Timestamp" with tm.assert_raises_regex(TypeError, msg): idx + Timestamp('2011-01-01') with tm.assert_raises_regex(TypeError, msg): Timestamp('2011-01-01') + idx + def test_add_dti_ts(self): + dti = DatetimeIndex(['2011-01-01', '2011-01-02']) + ts = Timestamp('2011-01-01') + msg = 'cannot add DatetimeIndex and Timestamp' + + with tm.assert_raises_regex(TypeError, msg): + dti + ts + + with tm.assert_raises_regex(TypeError, msg): + ts + dti + + def test_add_dti_dt64(self): + dti = DatetimeIndex(['2011-01-01', '2011-01-02']) + dt64 = np.datetime64('2005-02-25') + msg = 'cannot add DatetimeIndex and datetime64' + + with tm.assert_raises_regex(TypeError, msg): + dti + dt64 + + with tm.assert_raises_regex(TypeError, msg): + dt64 + dti + def test_add_dti_dti(self): # previously performed setop (deprecated in 0.16.0), now raises # TypeError (GH14164) From 3afe6b5a6bf9ae4487a1cf72c6ee7e97b1a4cb47 Mon Sep 17 00:00:00 2001 From: mturzanska Date: Tue, 3 Oct 2017 19:22:50 -0400 Subject: [PATCH 3/8] Further simplify __add__ Let the exception be raised from _add_datelike --- pandas/core/indexes/datetimelike.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 1d5a1ce11c0e0..981e478aae18b 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -621,7 +621,9 @@ def _convert_scalar_indexer(self, key, kind=None): ._convert_scalar_indexer(key, kind=kind)) def _add_datelike(self, other): - raise AbstractMethodError(self) + raise TypeError("cannot add {0} and {1}" + .format(type(self).__name__, + type(other).__name__)) def _sub_datelike(self, other): raise AbstractMethodError(self) @@ -647,16 +649,12 @@ def __add__(self, other): return other._add_delta(self) raise TypeError("cannot add TimedeltaIndex and {typ}" .format(typ=type(other))) - elif isinstance(other, Index): - raise TypeError("cannot add {typ1} and {typ2}" - .format(typ1=type(self).__name__, - typ2=type(other).__name__)) elif isinstance(other, (DateOffset, timedelta, np.timedelta64, Timedelta)): return self._add_delta(other) elif is_integer(other): return self.shift(other) - elif isinstance(other, (Timestamp, datetime, np.datetime64)): + elif isinstance(other, (Index, Timestamp, datetime, np.datetime64)): return self._add_datelike(other) else: # pragma: no cover return NotImplemented From 957b79f992e19b880159fa8dcbde0ffb25473e7f Mon Sep 17 00:00:00 2001 From: mturzanska Date: Tue, 3 Oct 2017 19:26:30 -0400 Subject: [PATCH 4/8] Include error message in the test --- pandas/tests/indexes/datetimes/test_ops.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py index 068df653c186e..edd6432e5c0a9 100644 --- a/pandas/tests/indexes/datetimes/test_ops.py +++ b/pandas/tests/indexes/datetimes/test_ops.py @@ -469,17 +469,18 @@ def test_add_dti_dti(self): dti = date_range('20130101', periods=3) dti_tz = date_range('20130101', periods=3).tz_localize('US/Eastern') + msg = 'cannot add DatetimeIndex and DatetimeIndex' - with pytest.raises(TypeError): + with tm.assert_raises_regex(TypeError, msg): dti + dti - with pytest.raises(TypeError): + with tm.assert_raises_regex(TypeError, msg): dti_tz + dti_tz - with pytest.raises(TypeError): + with tm.assert_raises_regex(TypeError, msg): dti_tz + dti - with pytest.raises(TypeError): + with tm.assert_raises_regex(TypeError, msg): dti + dti_tz def test_difference(self): From 435a9b3cdb0381446542534bc332767e9a8f838f Mon Sep 17 00:00:00 2001 From: mturzanska Date: Tue, 3 Oct 2017 19:40:03 -0400 Subject: [PATCH 5/8] Make flake8 happy --- pandas/core/indexes/datetimelike.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 981e478aae18b..d5b4525e8a1eb 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -654,7 +654,8 @@ def __add__(self, other): return self._add_delta(other) elif is_integer(other): return self.shift(other) - elif isinstance(other, (Index, Timestamp, datetime, np.datetime64)): + elif isinstance(other, (Index, Timestamp, datetime, + np.datetime64)): return self._add_datelike(other) else: # pragma: no cover return NotImplemented From 1f78070c7591c427b300ec5f7d66f9f7de8dc0ad Mon Sep 17 00:00:00 2001 From: mturzanska Date: Thu, 5 Oct 2017 20:47:23 -0400 Subject: [PATCH 6/8] Address comments from code review Combine multiple tests using parametrize Add issue number in the comment --- pandas/tests/indexes/datetimes/test_ops.py | 49 ++++++---------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py index edd6432e5c0a9..6b423e4e79966 100644 --- a/pandas/tests/indexes/datetimes/test_ops.py +++ b/pandas/tests/indexes/datetimes/test_ops.py @@ -441,47 +441,22 @@ def test_add_iadd(self): with tm.assert_raises_regex(TypeError, msg): Timestamp('2011-01-01') + idx - def test_add_dti_ts(self): - dti = DatetimeIndex(['2011-01-01', '2011-01-02']) - ts = Timestamp('2011-01-01') - msg = 'cannot add DatetimeIndex and Timestamp' - - with tm.assert_raises_regex(TypeError, msg): - dti + ts - - with tm.assert_raises_regex(TypeError, msg): - ts + dti + @pytest.mark.parametrize('addend', [ + date_range('20130101', periods=3).tz_localize('US/Eastern'), + DatetimeIndex(['2011-01-01', '2011-01-02']), + np.datetime64('2005-02-25'), + Timestamp('2011-01-01'), + ]) + def test_add_datetimelike(self, addend): + # issue #9631 - def test_add_dti_dt64(self): dti = DatetimeIndex(['2011-01-01', '2011-01-02']) - dt64 = np.datetime64('2005-02-25') - msg = 'cannot add DatetimeIndex and datetime64' - - with tm.assert_raises_regex(TypeError, msg): - dti + dt64 - + msg = 'cannot add DatetimeIndex and {0}'.format( + type(addend).__name__) with tm.assert_raises_regex(TypeError, msg): - dt64 + dti - - def test_add_dti_dti(self): - # previously performed setop (deprecated in 0.16.0), now raises - # TypeError (GH14164) - - dti = date_range('20130101', periods=3) - dti_tz = date_range('20130101', periods=3).tz_localize('US/Eastern') - msg = 'cannot add DatetimeIndex and DatetimeIndex' - - with tm.assert_raises_regex(TypeError, msg): - dti + dti - - with tm.assert_raises_regex(TypeError, msg): - dti_tz + dti_tz - - with tm.assert_raises_regex(TypeError, msg): - dti_tz + dti - + dti + addend with tm.assert_raises_regex(TypeError, msg): - dti + dti_tz + addend + dti def test_difference(self): for tz in self.tz: From 4a69723ecf55cd95a2a4dc61adc64ca473551ca2 Mon Sep 17 00:00:00 2001 From: mturzanska Date: Fri, 6 Oct 2017 18:34:27 -0400 Subject: [PATCH 7/8] Bring back test_add_dti_dti Prettify test_add_dti_dti Add datetime object to test_add_datetimelike params --- pandas/tests/indexes/datetimes/test_ops.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py index 6b423e4e79966..3dd6ed16cf9b3 100644 --- a/pandas/tests/indexes/datetimes/test_ops.py +++ b/pandas/tests/indexes/datetimes/test_ops.py @@ -442,9 +442,10 @@ def test_add_iadd(self): Timestamp('2011-01-01') + idx @pytest.mark.parametrize('addend', [ - date_range('20130101', periods=3).tz_localize('US/Eastern'), + date_range('20110101', periods=3).tz_localize('US/Eastern'), + datetime(2011, 1, 1), DatetimeIndex(['2011-01-01', '2011-01-02']), - np.datetime64('2005-02-25'), + np.datetime64('2011-01-01'), Timestamp('2011-01-01'), ]) def test_add_datetimelike(self, addend): @@ -458,6 +459,20 @@ def test_add_datetimelike(self, addend): with tm.assert_raises_regex(TypeError, msg): addend + dti + def test_add_dti_dti(self): + # previously performed setop (deprecated in 0.16.0) + # now raises TypeError (GH14164) + + dti_tz = date_range('20110101', periods=3) \ + .tz_localize('US/Eastern') + dti = date_range('20110101', periods=3) + combinations = product((dti_tz, dti), repeat=2) + + msg = 'cannot add DatetimeIndex and DatetimeIndex' + for addend_1, addend_2 in combinations: + with tm.assert_raises_regex(TypeError, msg): + addend_1 + addend_2 + def test_difference(self): for tz in self.tz: # diff From 2014d303e8cfce9068230b878a016ea759428a5b Mon Sep 17 00:00:00 2001 From: mturzanska Date: Sat, 7 Oct 2017 12:29:20 -0400 Subject: [PATCH 8/8] Extend tests for tz-aware DatetimeIndex Rename test functions --- pandas/tests/indexes/datetimes/test_ops.py | 33 +++++++++++++--------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py index 3dd6ed16cf9b3..f0f9b8ff1fc6d 100644 --- a/pandas/tests/indexes/datetimes/test_ops.py +++ b/pandas/tests/indexes/datetimes/test_ops.py @@ -442,13 +442,14 @@ def test_add_iadd(self): Timestamp('2011-01-01') + idx @pytest.mark.parametrize('addend', [ - date_range('20110101', periods=3).tz_localize('US/Eastern'), datetime(2011, 1, 1), DatetimeIndex(['2011-01-01', '2011-01-02']), + DatetimeIndex(['2011-01-01', '2011-01-02']) + .tz_localize('US/Eastern'), np.datetime64('2011-01-01'), Timestamp('2011-01-01'), ]) - def test_add_datetimelike(self, addend): + def test_add_datetimelike_and_dti(self, addend): # issue #9631 dti = DatetimeIndex(['2011-01-01', '2011-01-02']) @@ -459,19 +460,25 @@ def test_add_datetimelike(self, addend): with tm.assert_raises_regex(TypeError, msg): addend + dti - def test_add_dti_dti(self): - # previously performed setop (deprecated in 0.16.0) - # now raises TypeError (GH14164) + @pytest.mark.parametrize('addend', [ + datetime(2011, 1, 1), + DatetimeIndex(['2011-01-01', '2011-01-02']), + DatetimeIndex(['2011-01-01', '2011-01-02']) + .tz_localize('US/Eastern'), + np.datetime64('2011-01-01'), + Timestamp('2011-01-01'), + ]) + def test_add_datetimelike_and_dti_tz(self, addend): + # issue #9631 - dti_tz = date_range('20110101', periods=3) \ + dti_tz = DatetimeIndex(['2011-01-01', '2011-01-02']) \ .tz_localize('US/Eastern') - dti = date_range('20110101', periods=3) - combinations = product((dti_tz, dti), repeat=2) - - msg = 'cannot add DatetimeIndex and DatetimeIndex' - for addend_1, addend_2 in combinations: - with tm.assert_raises_regex(TypeError, msg): - addend_1 + addend_2 + msg = 'cannot add DatetimeIndex and {0}'.format( + type(addend).__name__) + with tm.assert_raises_regex(TypeError, msg): + dti_tz + addend + with tm.assert_raises_regex(TypeError, msg): + addend + dti_tz def test_difference(self): for tz in self.tz: