From 8abbf39608ad7b871f881f5357307ee156b7098c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 18 Dec 2017 14:39:47 -0800 Subject: [PATCH 1/2] Timestamp comparisons for object arrays, closes #15183 --- pandas/_libs/tslibs/timestamps.pyx | 5 ++++- pandas/tests/scalar/test_timedelta.py | 21 +++++++++++++++++++++ pandas/tests/scalar/test_timestamp.py | 25 +++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 478611fe9cab9..8b0719bc8b93f 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -17,7 +17,7 @@ from cpython.datetime cimport (datetime, PyDateTime_IMPORT from util cimport (is_datetime64_object, is_timedelta64_object, - is_integer_object, is_string_object, + is_integer_object, is_string_object, is_array, INT64_MAX) cimport ccalendar @@ -108,6 +108,9 @@ cdef class _Timestamp(datetime): raise TypeError('Cannot compare type %r with type %r' % (type(self).__name__, type(other).__name__)) + elif is_array(other): + # avoid recursion error GH#15183 + return PyObject_RichCompare(np.array([self]), other, op) return PyObject_RichCompare(other, self, reverse_ops[op]) else: if op == Py_EQ: diff --git a/pandas/tests/scalar/test_timedelta.py b/pandas/tests/scalar/test_timedelta.py index 001f6c1fdbef4..86794ad9dc4b9 100644 --- a/pandas/tests/scalar/test_timedelta.py +++ b/pandas/tests/scalar/test_timedelta.py @@ -163,6 +163,27 @@ def test_binary_ops_with_timedelta(self): pytest.raises(TypeError, lambda: td * td) +class TestTimedeltaComparison(object): + def test_comparison_object_array(self): + # analogous to GH#15183 + td = Timedelta('2 days') + other = Timedelta('3 hours') + + arr = np.array([other, td], dtype=object) + res = arr == td + expected = np.array([False, True], dtype=bool) + assert (res == expected).all() + + # 2D case + arr = np.array([[other, td], + [td, other]], + dtype=object) + res = arr != td + expected = np.array([[True, False], [False, True]], dtype=bool) + assert res.shape == expected.shape + assert (res == expected).all() + + class TestTimedeltas(object): _multiprocess_can_split_ = True diff --git a/pandas/tests/scalar/test_timestamp.py b/pandas/tests/scalar/test_timestamp.py index 9c649a42fb8ee..422300f031099 100644 --- a/pandas/tests/scalar/test_timestamp.py +++ b/pandas/tests/scalar/test_timestamp.py @@ -969,6 +969,31 @@ def test_timestamp(self): class TestTimestampComparison(object): + def test_comparison_object_array(self): + # GH#15183 + ts = Timestamp('2011-01-03 00:00:00-0500', tz='US/Eastern') + other = Timestamp('2011-01-01 00:00:00-0500', tz='US/Eastern') + naive = Timestamp('2011-01-01 00:00:00') + + arr = np.array([other, ts], dtype=object) + res = arr == ts + expected = np.array([False, True], dtype=bool) + assert (res == expected).all() + + # 2D case + arr = np.array([[other, ts], + [ts, other]], + dtype=object) + res = arr != ts + expected = np.array([[True, False], [False, True]], dtype=bool) + assert res.shape == expected.shape + assert (res == expected).all() + + # tzaware mismatch + arr = np.array([naive], dtype=object) + with pytest.raises(TypeError): + arr < ts + def test_comparison(self): # 5-18-2012 00:00:00.000 stamp = long(1337299200000000000) From 3820a2f96134ca70a8c59584d5893cce3242b197 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 18 Dec 2017 14:43:41 -0800 Subject: [PATCH 2/2] whitespace fixup, add whatsnew --- doc/source/whatsnew/v0.22.0.txt | 1 + pandas/tests/scalar/test_timedelta.py | 2 +- pandas/tests/scalar/test_timestamp.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.22.0.txt b/doc/source/whatsnew/v0.22.0.txt index ae6d0816abc41..9f517e628512c 100644 --- a/doc/source/whatsnew/v0.22.0.txt +++ b/doc/source/whatsnew/v0.22.0.txt @@ -346,3 +346,4 @@ Other ^^^^^ - Improved error message when attempting to use a Python keyword as an identifier in a ``numexpr`` backed query (:issue:`18221`) +- Bug in :class:`Timestamp` where comparison with an array of ``Timestamp`` objects would result in a ``RecursionError`` (:issue:`15183`) diff --git a/pandas/tests/scalar/test_timedelta.py b/pandas/tests/scalar/test_timedelta.py index 86794ad9dc4b9..c260700c9473b 100644 --- a/pandas/tests/scalar/test_timedelta.py +++ b/pandas/tests/scalar/test_timedelta.py @@ -177,7 +177,7 @@ def test_comparison_object_array(self): # 2D case arr = np.array([[other, td], [td, other]], - dtype=object) + dtype=object) res = arr != td expected = np.array([[True, False], [False, True]], dtype=bool) assert res.shape == expected.shape diff --git a/pandas/tests/scalar/test_timestamp.py b/pandas/tests/scalar/test_timestamp.py index 422300f031099..19c09701f6106 100644 --- a/pandas/tests/scalar/test_timestamp.py +++ b/pandas/tests/scalar/test_timestamp.py @@ -983,7 +983,7 @@ def test_comparison_object_array(self): # 2D case arr = np.array([[other, ts], [ts, other]], - dtype=object) + dtype=object) res = arr != ts expected = np.array([[True, False], [False, True]], dtype=bool) assert res.shape == expected.shape