summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/test.rst17
-rw-r--r--Lib/datetime.py22
-rw-r--r--Lib/test/datetimetester.py74
-rw-r--r--Lib/test/support/__init__.py36
-rw-r--r--Lib/test/test_ipaddress.py33
-rw-r--r--Misc/NEWS.d/next/Library/2019-07-28-22-25-25.bpo-37685._3bN9f.rst2
-rw-r--r--Modules/_datetimemodule.c7
7 files changed, 107 insertions, 84 deletions
diff --git a/Doc/library/test.rst b/Doc/library/test.rst
index 7d62a94..da6a85d 100644
--- a/Doc/library/test.rst
+++ b/Doc/library/test.rst
@@ -356,11 +356,28 @@ The :mod:`test.support` module defines the following constants:
Check for presence of docstrings.
+
.. data:: TEST_HTTP_URL
Define the URL of a dedicated HTTP server for the network tests.
+.. data:: ALWAYS_EQ
+
+ Object that is equal to anything. Used to test mixed type comparison.
+
+
+.. data:: LARGEST
+
+ Object that is greater than anything (except itself).
+ Used to test mixed type comparison.
+
+
+.. data:: SMALLEST
+
+ Object that is less than anything (except itself).
+ Used to test mixed type comparison.
+
The :mod:`test.support` module defines the following functions:
diff --git a/Lib/datetime.py b/Lib/datetime.py
index e35ee05..d4c7a1f 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -739,25 +739,25 @@ class timedelta:
if isinstance(other, timedelta):
return self._cmp(other) <= 0
else:
- _cmperror(self, other)
+ return NotImplemented
def __lt__(self, other):
if isinstance(other, timedelta):
return self._cmp(other) < 0
else:
- _cmperror(self, other)
+ return NotImplemented
def __ge__(self, other):
if isinstance(other, timedelta):
return self._cmp(other) >= 0
else:
- _cmperror(self, other)
+ return NotImplemented
def __gt__(self, other):
if isinstance(other, timedelta):
return self._cmp(other) > 0
else:
- _cmperror(self, other)
+ return NotImplemented
def _cmp(self, other):
assert isinstance(other, timedelta)
@@ -1316,25 +1316,25 @@ class time:
if isinstance(other, time):
return self._cmp(other) <= 0
else:
- _cmperror(self, other)
+ return NotImplemented
def __lt__(self, other):
if isinstance(other, time):
return self._cmp(other) < 0
else:
- _cmperror(self, other)
+ return NotImplemented
def __ge__(self, other):
if isinstance(other, time):
return self._cmp(other) >= 0
else:
- _cmperror(self, other)
+ return NotImplemented
def __gt__(self, other):
if isinstance(other, time):
return self._cmp(other) > 0
else:
- _cmperror(self, other)
+ return NotImplemented
def _cmp(self, other, allow_mixed=False):
assert isinstance(other, time)
@@ -2210,9 +2210,9 @@ class timezone(tzinfo):
return (self._offset, self._name)
def __eq__(self, other):
- if type(other) != timezone:
- return False
- return self._offset == other._offset
+ if isinstance(other, timezone):
+ return self._offset == other._offset
+ return NotImplemented
def __hash__(self):
return hash(self._offset)
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 37ddd4b..99b620c 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -2,11 +2,8 @@
See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
"""
-from test.support import is_resource_enabled
-
import itertools
import bisect
-
import copy
import decimal
import sys
@@ -22,6 +19,7 @@ from array import array
from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
from test import support
+from test.support import is_resource_enabled, ALWAYS_EQ, LARGEST, SMALLEST
import datetime as datetime_module
from datetime import MINYEAR, MAXYEAR
@@ -54,18 +52,6 @@ INF = float("inf")
NAN = float("nan")
-class ComparesEqualClass(object):
- """
- A class that is always equal to whatever you compare it to.
- """
-
- def __eq__(self, other):
- return True
-
- def __ne__(self, other):
- return False
-
-
#############################################################################
# module tests
@@ -353,6 +339,18 @@ class TestTimeZone(unittest.TestCase):
self.assertTrue(timezone(ZERO) != None)
self.assertFalse(timezone(ZERO) == None)
+ tz = timezone(ZERO)
+ self.assertTrue(tz == ALWAYS_EQ)
+ self.assertFalse(tz != ALWAYS_EQ)
+ self.assertTrue(tz < LARGEST)
+ self.assertFalse(tz > LARGEST)
+ self.assertTrue(tz <= LARGEST)
+ self.assertFalse(tz >= LARGEST)
+ self.assertFalse(tz < SMALLEST)
+ self.assertTrue(tz > SMALLEST)
+ self.assertFalse(tz <= SMALLEST)
+ self.assertTrue(tz >= SMALLEST)
+
def test_aware_datetime(self):
# test that timezone instances can be used by datetime
t = datetime(1, 1, 1)
@@ -414,10 +412,21 @@ class HarmlessMixedComparison:
# Comparison to objects of unsupported types should return
# NotImplemented which falls back to the right hand side's __eq__
- # method. In this case, ComparesEqualClass.__eq__ always returns True.
- # ComparesEqualClass.__ne__ always returns False.
- self.assertTrue(me == ComparesEqualClass())
- self.assertFalse(me != ComparesEqualClass())
+ # method. In this case, ALWAYS_EQ.__eq__ always returns True.
+ # ALWAYS_EQ.__ne__ always returns False.
+ self.assertTrue(me == ALWAYS_EQ)
+ self.assertFalse(me != ALWAYS_EQ)
+
+ # If the other class explicitly defines ordering
+ # relative to our class, it is allowed to do so
+ self.assertTrue(me < LARGEST)
+ self.assertFalse(me > LARGEST)
+ self.assertTrue(me <= LARGEST)
+ self.assertFalse(me >= LARGEST)
+ self.assertFalse(me < SMALLEST)
+ self.assertTrue(me > SMALLEST)
+ self.assertFalse(me <= SMALLEST)
+ self.assertTrue(me >= SMALLEST)
def test_harmful_mixed_comparison(self):
me = self.theclass(1, 1, 1)
@@ -1582,29 +1591,6 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
self.assertRaises(TypeError, lambda: our < their)
self.assertRaises(TypeError, lambda: their < our)
- # However, if the other class explicitly defines ordering
- # relative to our class, it is allowed to do so
-
- class LargerThanAnything:
- def __lt__(self, other):
- return False
- def __le__(self, other):
- return isinstance(other, LargerThanAnything)
- def __eq__(self, other):
- return isinstance(other, LargerThanAnything)
- def __gt__(self, other):
- return not isinstance(other, LargerThanAnything)
- def __ge__(self, other):
- return True
-
- their = LargerThanAnything()
- self.assertEqual(our == their, False)
- self.assertEqual(their == our, False)
- self.assertEqual(our != their, True)
- self.assertEqual(their != our, True)
- self.assertEqual(our < their, True)
- self.assertEqual(their < our, False)
-
def test_bool(self):
# All dates are considered true.
self.assertTrue(self.theclass.min)
@@ -3781,8 +3767,8 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
self.assertRaises(ValueError, base.replace, microsecond=1000000)
def test_mixed_compare(self):
- t1 = time(1, 2, 3)
- t2 = time(1, 2, 3)
+ t1 = self.theclass(1, 2, 3)
+ t2 = self.theclass(1, 2, 3)
self.assertEqual(t1, t2)
t2 = t2.replace(tzinfo=None)
self.assertEqual(t1, t2)
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index dbbbdb0..c82037e 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -113,6 +113,7 @@ __all__ = [
"run_with_locale", "swap_item",
"swap_attr", "Matcher", "set_memlimit", "SuppressCrashReport", "sortdict",
"run_with_tz", "PGO", "missing_compiler_executable", "fd_count",
+ "ALWAYS_EQ", "LARGEST", "SMALLEST"
]
class Error(Exception):
@@ -3103,6 +3104,41 @@ class FakePath:
return self.path
+class _ALWAYS_EQ:
+ """
+ Object that is equal to anything.
+ """
+ def __eq__(self, other):
+ return True
+ def __ne__(self, other):
+ return False
+
+ALWAYS_EQ = _ALWAYS_EQ()
+
+@functools.total_ordering
+class _LARGEST:
+ """
+ Object that is greater than anything (except itself).
+ """
+ def __eq__(self, other):
+ return isinstance(other, _LARGEST)
+ def __lt__(self, other):
+ return False
+
+LARGEST = _LARGEST()
+
+@functools.total_ordering
+class _SMALLEST:
+ """
+ Object that is less than anything (except itself).
+ """
+ def __eq__(self, other):
+ return isinstance(other, _SMALLEST)
+ def __gt__(self, other):
+ return False
+
+SMALLEST = _SMALLEST()
+
def maybe_get_event_loop_policy():
"""Return the global event loop policy if one is set, else return None."""
return asyncio.events._event_loop_policy
diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py
index 9e17ea0..de77111 100644
--- a/Lib/test/test_ipaddress.py
+++ b/Lib/test/test_ipaddress.py
@@ -12,6 +12,7 @@ import operator
import pickle
import ipaddress
import weakref
+from test.support import LARGEST, SMALLEST
class BaseTestCase(unittest.TestCase):
@@ -673,20 +674,6 @@ class FactoryFunctionErrors(BaseTestCase):
self.assertFactoryError(ipaddress.ip_network, "network")
-@functools.total_ordering
-class LargestObject:
- def __eq__(self, other):
- return isinstance(other, LargestObject)
- def __lt__(self, other):
- return False
-
-@functools.total_ordering
-class SmallestObject:
- def __eq__(self, other):
- return isinstance(other, SmallestObject)
- def __gt__(self, other):
- return False
-
class ComparisonTests(unittest.TestCase):
v4addr = ipaddress.IPv4Address(1)
@@ -775,8 +762,6 @@ class ComparisonTests(unittest.TestCase):
def test_foreign_type_ordering(self):
other = object()
- smallest = SmallestObject()
- largest = LargestObject()
for obj in self.objects:
with self.assertRaises(TypeError):
obj < other
@@ -786,14 +771,14 @@ class ComparisonTests(unittest.TestCase):
obj <= other
with self.assertRaises(TypeError):
obj >= other
- self.assertTrue(obj < largest)
- self.assertFalse(obj > largest)
- self.assertTrue(obj <= largest)
- self.assertFalse(obj >= largest)
- self.assertFalse(obj < smallest)
- self.assertTrue(obj > smallest)
- self.assertFalse(obj <= smallest)
- self.assertTrue(obj >= smallest)
+ self.assertTrue(obj < LARGEST)
+ self.assertFalse(obj > LARGEST)
+ self.assertTrue(obj <= LARGEST)
+ self.assertFalse(obj >= LARGEST)
+ self.assertFalse(obj < SMALLEST)
+ self.assertTrue(obj > SMALLEST)
+ self.assertFalse(obj <= SMALLEST)
+ self.assertTrue(obj >= SMALLEST)
def test_mixed_type_key(self):
# with get_mixed_type_key, you can sort addresses and network.
diff --git a/Misc/NEWS.d/next/Library/2019-07-28-22-25-25.bpo-37685._3bN9f.rst b/Misc/NEWS.d/next/Library/2019-07-28-22-25-25.bpo-37685._3bN9f.rst
new file mode 100644
index 0000000..ba60057
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-07-28-22-25-25.bpo-37685._3bN9f.rst
@@ -0,0 +1,2 @@
+Fixed comparisons of :class:`datetime.timedelta` and
+:class:`datetime.timezone`.
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 19d3d7e..b55922c 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -3741,11 +3741,8 @@ timezone_richcompare(PyDateTime_TimeZone *self,
{
if (op != Py_EQ && op != Py_NE)
Py_RETURN_NOTIMPLEMENTED;
- if (Py_TYPE(other) != &PyDateTime_TimeZoneType) {
- if (op == Py_EQ)
- Py_RETURN_FALSE;
- else
- Py_RETURN_TRUE;
+ if (!PyTZInfo_Check(other)) {
+ Py_RETURN_NOTIMPLEMENTED;
}
return delta_richcompare(self->offset, other->offset, op);
}