From e3ac3c978f86fed0ab4e80b24ae48ba8fb415aca Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Fri, 30 Nov 2018 18:32:31 +0100 Subject: [PATCH 01/13] BUG: GH24011 --- pandas/_libs/tslibs/timestamps.pyx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index f414cd161e562..79129338f3cf4 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -240,12 +240,7 @@ cdef class _Timestamp(datetime): return PyObject_RichCompare(np.array([self]), other, op) return PyObject_RichCompare(other, self, reverse_ops[op]) else: - if op == Py_EQ: - return False - elif op == Py_NE: - return True - raise TypeError('Cannot compare type %r with type %r' % - (type(self).__name__, type(other).__name__)) + return NotImplemented self._assert_tzawareness_compat(other) return cmp_scalar(self.value, ots.value, op) From f5eee0aacc82d89bbcbc55ef5b6d8815ecc50b99 Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Fri, 30 Nov 2018 18:41:21 +0100 Subject: [PATCH 02/13] TST: GH24011 --- pandas/tests/tslibs/test_tslib.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pandas/tests/tslibs/test_tslib.py b/pandas/tests/tslibs/test_tslib.py index 17bd46cd235da..f610a8b430400 100644 --- a/pandas/tests/tslibs/test_tslib.py +++ b/pandas/tests/tslibs/test_tslib.py @@ -21,3 +21,24 @@ def test_normalize_date(): actual = tslibs.normalize_date(value) assert actual == datetime(2007, 10, 1) + + +def test_rich_comparison_with_unsupported_type(): + # See https://github.com/pandas-dev/pandas/issues/24011 + class Inf: + def __lt__(self, o): return False + def __le__(self, o): return isinstance(o, Inf) + def __gt__(self, o): return not isinstance(o, Inf) + def __ge__(self, o): return True + def __eq__(self, o): return isinstance(o, Inf) + def __ne__(self, o): return not self == o # Required for Python 2 + def __repr__(self): return '+inf' + + timestamp = tslibs.Timestamp('2018-11-30') + + # Comparison works if compared in *that* order, because magic method is called on Inf + assert Inf() > timestamp + assert not (Inf() < timestamp) + + # ... but used to not work when magic method is called on Timestamp + assert timestamp < Inf() \ No newline at end of file From 3ab8be2abf6489ad666cddcaa04a9465f2953af5 Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Fri, 30 Nov 2018 18:46:45 +0100 Subject: [PATCH 03/13] CLN: Remove trailing spaces & compound operations --- pandas/tests/tslibs/test_tslib.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/pandas/tests/tslibs/test_tslib.py b/pandas/tests/tslibs/test_tslib.py index f610a8b430400..034bb89775478 100644 --- a/pandas/tests/tslibs/test_tslib.py +++ b/pandas/tests/tslibs/test_tslib.py @@ -25,20 +25,29 @@ def test_normalize_date(): def test_rich_comparison_with_unsupported_type(): # See https://github.com/pandas-dev/pandas/issues/24011 + class Inf: - def __lt__(self, o): return False - def __le__(self, o): return isinstance(o, Inf) - def __gt__(self, o): return not isinstance(o, Inf) - def __ge__(self, o): return True - def __eq__(self, o): return isinstance(o, Inf) - def __ne__(self, o): return not self == o # Required for Python 2 - def __repr__(self): return '+inf' + def __lt__(self, o): + return False + + def __le__(self, o): + return isinstance(o, Inf) + + def __gt__(self, o): + return not isinstance(o, Inf) + + def __ge__(self, o): + return True + + def __eq__(self, o): + return isinstance(o, Inf) timestamp = tslibs.Timestamp('2018-11-30') - # Comparison works if compared in *that* order, because magic method is called on Inf + # Comparison works if compared in *that* order, because + # magic method is called on Inf assert Inf() > timestamp assert not (Inf() < timestamp) # ... but used to not work when magic method is called on Timestamp - assert timestamp < Inf() \ No newline at end of file + assert timestamp < Inf() From 490b97f3da88965d03a5a54211073dc001d5fa59 Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Sun, 2 Dec 2018 20:01:28 +0100 Subject: [PATCH 04/13] Changes following code review 1 (#24021) --- pandas/_libs/tslibs/timestamps.pyx | 10 +---- .../scalar/timestamp/test_comparisons.py | 40 +++++++++++++++++++ pandas/tests/tslibs/test_tslib.py | 30 -------------- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 79129338f3cf4..cebdf8d102329 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -226,15 +226,7 @@ cdef class _Timestamp(datetime): if is_datetime64_object(other): other = Timestamp(other) else: - if op == Py_EQ: - return False - elif op == Py_NE: - return True - - # only allow ==, != ops - raise TypeError('Cannot compare type %r with type %r' % - (type(self).__name__, - type(other).__name__)) + return NotImplemented elif is_array(other): # avoid recursion error GH#15183 return PyObject_RichCompare(np.array([self]), other, op) diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index 74dd52c48153f..0f2157fab13a0 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -166,3 +166,43 @@ def test_timestamp_compare_with_early_datetime(self): assert stamp >= datetime(1600, 1, 1) assert stamp < datetime(2700, 1, 1) assert stamp <= datetime(2700, 1, 1) + + + +def test_rich_comparison_with_unsupported_type(): + # See https://github.com/pandas-dev/pandas/issues/24011 + + class Inf(object): + def __lt__(self, o): + return False + + def __le__(self, o): + return isinstance(o, Inf) + + def __gt__(self, o): + return not isinstance(o, Inf) + + def __ge__(self, o): + return True + + def __eq__(self, o): + return isinstance(o, Inf) + + timestamp = Timestamp('2018-11-30') + + # Comparison works if compared in *that* order, because + # magic method is called on Inf + assert Inf() > timestamp + assert not (Inf() < timestamp) + assert Inf() != timestamp + assert not (Inf() == timestamp) + assert Inf() >= timestamp + assert not (Inf() <= timestamp) + + # ... but used to not work when magic method is called on Timestamp + assert not (timestamp > Inf()) + assert timestamp < Inf() + assert timestamp != Inf() + assert not (timestamp == Inf()) + assert timestamp <= Inf() + assert not (timestamp >= Inf()) \ No newline at end of file diff --git a/pandas/tests/tslibs/test_tslib.py b/pandas/tests/tslibs/test_tslib.py index 034bb89775478..17bd46cd235da 100644 --- a/pandas/tests/tslibs/test_tslib.py +++ b/pandas/tests/tslibs/test_tslib.py @@ -21,33 +21,3 @@ def test_normalize_date(): actual = tslibs.normalize_date(value) assert actual == datetime(2007, 10, 1) - - -def test_rich_comparison_with_unsupported_type(): - # See https://github.com/pandas-dev/pandas/issues/24011 - - class Inf: - def __lt__(self, o): - return False - - def __le__(self, o): - return isinstance(o, Inf) - - def __gt__(self, o): - return not isinstance(o, Inf) - - def __ge__(self, o): - return True - - def __eq__(self, o): - return isinstance(o, Inf) - - timestamp = tslibs.Timestamp('2018-11-30') - - # Comparison works if compared in *that* order, because - # magic method is called on Inf - assert Inf() > timestamp - assert not (Inf() < timestamp) - - # ... but used to not work when magic method is called on Timestamp - assert timestamp < Inf() From 1be836df8dd44f730a0eed9c6c41130f2b70740d Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Sun, 2 Dec 2018 20:10:52 +0100 Subject: [PATCH 05/13] Fix pep8 --- pandas/tests/scalar/timestamp/test_comparisons.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index 0f2157fab13a0..02e8356a646e3 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -168,7 +168,6 @@ def test_timestamp_compare_with_early_datetime(self): assert stamp <= datetime(2700, 1, 1) - def test_rich_comparison_with_unsupported_type(): # See https://github.com/pandas-dev/pandas/issues/24011 @@ -205,4 +204,4 @@ def __eq__(self, o): assert timestamp != Inf() assert not (timestamp == Inf()) assert timestamp <= Inf() - assert not (timestamp >= Inf()) \ No newline at end of file + assert not (timestamp >= Inf()) From 1882a09c96567d7ec75312724015101f6f208198 Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Thu, 28 Feb 2019 09:21:47 +0100 Subject: [PATCH 06/13] what's new entry --- doc/source/whatsnew/v0.25.0.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 170e7f14da397..354fb375c82ed 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -33,6 +33,9 @@ Backwards incompatible API changes .. _whatsnew_0250.api_breaking.utc_offset_indexing: +- Rich comparisons involving :class:`~Timestamp.Timestamp` return ``NotImplemented`` instead of raising ``TypeError`` with unsupported objects (:issue:`24011`) + + Indexing with date strings with UTC offsets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 117da8daf490629a2f27c0c3c4c73f7957c839c1 Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Mon, 4 Mar 2019 10:59:44 +0100 Subject: [PATCH 07/13] CLN: Doc & test for GH24011 --- doc/source/whatsnew/v0.25.0.rst | 2 +- .../scalar/timestamp/test_comparisons.py | 27 +++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 354fb375c82ed..9fc2d7fbcf06a 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -33,7 +33,7 @@ Backwards incompatible API changes .. _whatsnew_0250.api_breaking.utc_offset_indexing: -- Rich comparisons involving :class:`~Timestamp.Timestamp` return ``NotImplemented`` instead of raising ``TypeError`` with unsupported objects (:issue:`24011`) +- Comparing :class:`~Timestamp.Timestamp` with unsupported objects now returns ``NotImplemented`` instead of raising ``TypeError``. This implies that unsupported rich comparisons are delegated to the other object, and is now consistent with Python 3 behavior for ``datetime`` objects (:issue:`24011`) Indexing with date strings with UTC offsets diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index 02e8356a646e3..7bd97fd9ec64e 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -169,7 +169,8 @@ def test_timestamp_compare_with_early_datetime(self): def test_rich_comparison_with_unsupported_type(): - # See https://github.com/pandas-dev/pandas/issues/24011 + # Comparisons with unsupported objects should return NotImplemented + # (it previously raised TypeError) class Inf(object): def __lt__(self, o): @@ -187,21 +188,13 @@ def __ge__(self, o): def __eq__(self, o): return isinstance(o, Inf) + inf = Inf() timestamp = Timestamp('2018-11-30') - # Comparison works if compared in *that* order, because - # magic method is called on Inf - assert Inf() > timestamp - assert not (Inf() < timestamp) - assert Inf() != timestamp - assert not (Inf() == timestamp) - assert Inf() >= timestamp - assert not (Inf() <= timestamp) - - # ... but used to not work when magic method is called on Timestamp - assert not (timestamp > Inf()) - assert timestamp < Inf() - assert timestamp != Inf() - assert not (timestamp == Inf()) - assert timestamp <= Inf() - assert not (timestamp >= Inf()) + for left, right in [(inf, timestamp), (timestamp, inf)]: + assert left > right or right < left + assert left >= right or right <= left + assert not (left == right) + assert left != right + + \ No newline at end of file From a6c9144c50c97dfe9c8833d9e1eb1f1ad5fdb77a Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Tue, 5 Mar 2019 10:13:51 +0100 Subject: [PATCH 08/13] CLN: Link to NotImplemented & Typo --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 9fc2d7fbcf06a..53786cff566bd 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -33,7 +33,7 @@ Backwards incompatible API changes .. _whatsnew_0250.api_breaking.utc_offset_indexing: -- Comparing :class:`~Timestamp.Timestamp` with unsupported objects now returns ``NotImplemented`` instead of raising ``TypeError``. This implies that unsupported rich comparisons are delegated to the other object, and is now consistent with Python 3 behavior for ``datetime`` objects (:issue:`24011`) +- Comparing :class:`Timestamp` with unsupported objects now returns ::py:obj:`NotImplemented` instead of raising ``TypeError``. This implies that unsupported rich comparisons are delegated to the other object, and are now consistent with Python 3 behavior for ``datetime`` objects (:issue:`24011`) Indexing with date strings with UTC offsets From f332f5629e78c22cc2018b967593b3c667a8e927 Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Tue, 5 Mar 2019 10:28:44 +0100 Subject: [PATCH 09/13] TST: Fix test for GH24011 --- 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 7bd97fd9ec64e..336979ab4b542 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -192,8 +192,8 @@ def __eq__(self, o): timestamp = Timestamp('2018-11-30') for left, right in [(inf, timestamp), (timestamp, inf)]: - assert left > right or right < left - assert left >= right or right <= left + assert left > right or left < right + assert left >= right or left <= right assert not (left == right) assert left != right From f9cbefeae2661eff332eeca503c4b361b7bec10b Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Tue, 5 Mar 2019 13:33:21 +0100 Subject: [PATCH 10/13] CLN: Typo --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 53786cff566bd..73af79389d254 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -33,7 +33,7 @@ Backwards incompatible API changes .. _whatsnew_0250.api_breaking.utc_offset_indexing: -- Comparing :class:`Timestamp` with unsupported objects now returns ::py:obj:`NotImplemented` instead of raising ``TypeError``. This implies that unsupported rich comparisons are delegated to the other object, and are now consistent with Python 3 behavior for ``datetime`` objects (:issue:`24011`) +- Comparing :class:`Timestamp` with unsupported objects now returns :py:obj:`NotImplemented` instead of raising ``TypeError``. This implies that unsupported rich comparisons are delegated to the other object, and are now consistent with Python 3 behavior for ``datetime`` objects (:issue:`24011`) Indexing with date strings with UTC offsets From 314aedd3fe0962b1c1099249e3b0d4f20aba1c11 Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Fri, 8 Mar 2019 12:25:51 +0100 Subject: [PATCH 11/13] CLN: Flake8 --- pandas/tests/scalar/timestamp/test_comparisons.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index 336979ab4b542..af563ae19316d 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -169,7 +169,7 @@ def test_timestamp_compare_with_early_datetime(self): def test_rich_comparison_with_unsupported_type(): - # Comparisons with unsupported objects should return NotImplemented + # Comparisons with unsupported objects should return NotImplemented # (it previously raised TypeError) class Inf(object): @@ -196,5 +196,3 @@ def __eq__(self, o): assert left >= right or left <= right assert not (left == right) assert left != right - - \ No newline at end of file From 8e57a75a878de3485e915338adccf0b5ffc9b2cf Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Mon, 11 Mar 2019 09:25:32 +0100 Subject: [PATCH 12/13] CLN: Move entry --- doc/source/whatsnew/v0.25.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 73af79389d254..3d1b2717a1a80 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -33,7 +33,7 @@ Backwards incompatible API changes .. _whatsnew_0250.api_breaking.utc_offset_indexing: -- Comparing :class:`Timestamp` with unsupported objects now returns :py:obj:`NotImplemented` instead of raising ``TypeError``. This implies that unsupported rich comparisons are delegated to the other object, and are now consistent with Python 3 behavior for ``datetime`` objects (:issue:`24011`) + Indexing with date strings with UTC offsets @@ -74,7 +74,7 @@ Other API Changes - :class:`DatetimeTZDtype` will now standardize pytz timezones to a common timezone instance (:issue:`24713`) - ``Timestamp`` and ``Timedelta`` scalars now implement the :meth:`to_numpy` method as aliases to :meth:`Timestamp.to_datetime64` and :meth:`Timedelta.to_timedelta64`, respectively. (:issue:`24653`) - :meth:`Timestamp.strptime` will now rise a ``NotImplementedError`` (:issue:`25016`) -- +- Comparing :class:`Timestamp` with unsupported objects now returns :py:obj:`NotImplemented` instead of raising ``TypeError``. This implies that unsupported rich comparisons are delegated to the other object, and are now consistent with Python 3 behavior for ``datetime`` objects (:issue:`24011`) .. _whatsnew_0250.deprecations: From bfda6c3300e5f2a3fbdc910782003d2bb0206566 Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Mon, 1 Apr 2019 14:13:33 +0200 Subject: [PATCH 13/13] CLN: White lines & issue ref --- doc/source/whatsnew/v0.25.0.rst | 2 -- pandas/tests/scalar/timestamp/test_comparisons.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index a3dd578c206dc..61cff302e5f4d 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -45,8 +45,6 @@ Backwards incompatible API changes .. _whatsnew_0250.api_breaking.utc_offset_indexing: - - Indexing with date strings with UTC offsets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index 04728b7540e0e..2821c0a578752 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -160,7 +160,7 @@ def test_timestamp_compare_with_early_datetime(self): def test_rich_comparison_with_unsupported_type(): # Comparisons with unsupported objects should return NotImplemented - # (it previously raised TypeError) + # (it previously raised TypeError, see #24011) class Inf(object): def __lt__(self, o):