mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Comparison for timedelta, time, date and datetime objects: __eq__ and
__ne__ no longer complain if they don't know how to compare to the other thing. If no meaningful way to compare is known, saying "not equal" is sensible. This allows things like if adatetime in some_sequence: and somedict[adatetime] = whatever to work as expected even if some_sequence contains non-datetime objects, or somedict non-datetime keys, because they only call __eq__. It still complains (raises TypeError) for mixed-type comparisons in contexts that require a total ordering, such as list.sort(), use as a key in a BTree-based data structure, and cmp().
This commit is contained in:
parent
275666fd50
commit
07534a607b
4 changed files with 155 additions and 58 deletions
|
@ -257,6 +257,11 @@ support certain additions and subtractions with \class{date} and
|
||||||
Comparisons of \class{timedelta} objects are supported with the
|
Comparisons of \class{timedelta} objects are supported with the
|
||||||
\class{timedelta} object representing the smaller duration considered
|
\class{timedelta} object representing the smaller duration considered
|
||||||
to be the smaller timedelta.
|
to be the smaller timedelta.
|
||||||
|
In order to stop mixed-type comparisons from falling back to the
|
||||||
|
default comparison by object address, when a \class{timedelta} object is
|
||||||
|
compared to an object of a different type, \exception{TypeError} is
|
||||||
|
raised unless the comparison is \code{==} or \code{!=}. The latter
|
||||||
|
cases return \constant{False} or \constant{True}, respectively.
|
||||||
|
|
||||||
\class{timedelta} objects are hashable (usable as dictionary keys),
|
\class{timedelta} objects are hashable (usable as dictionary keys),
|
||||||
support efficient pickling, and in Boolean contexts, a \class{timedelta}
|
support efficient pickling, and in Boolean contexts, a \class{timedelta}
|
||||||
|
@ -402,6 +407,10 @@ isn't also a \class{date} object. However, \code{NotImplemented}
|
||||||
is returned instead if the other comparand has a
|
is returned instead if the other comparand has a
|
||||||
\method{timetuple} attribute. This hook gives other kinds of
|
\method{timetuple} attribute. This hook gives other kinds of
|
||||||
date objects a chance at implementing mixed-type comparison.
|
date objects a chance at implementing mixed-type comparison.
|
||||||
|
If not, when a \class{date} object is
|
||||||
|
compared to an object of a different type, \exception{TypeError} is
|
||||||
|
raised unless the comparison is \code{==} or \code{!=}. The latter
|
||||||
|
cases return \constant{False} or \constant{True}, respectively.
|
||||||
|
|
||||||
\end{description}
|
\end{description}
|
||||||
|
|
||||||
|
@ -743,7 +752,11 @@ the other is aware, \exception{TypeError} is raised. If both
|
||||||
\code{NotImplemented} is returned instead if the other comparand
|
\code{NotImplemented} is returned instead if the other comparand
|
||||||
has a \method{timetuple} attribute. This hook gives other
|
has a \method{timetuple} attribute. This hook gives other
|
||||||
kinds of date objects a chance at implementing mixed-type
|
kinds of date objects a chance at implementing mixed-type
|
||||||
comparison.}
|
comparison. If not, when a \class{datetime} object is
|
||||||
|
compared to an object of a different type, \exception{TypeError}
|
||||||
|
is raised unless the comparison is \code{==} or \code{!=}. The
|
||||||
|
latter cases return \constant{False} or \constant{True},
|
||||||
|
respectively.}
|
||||||
|
|
||||||
\end{description}
|
\end{description}
|
||||||
|
|
||||||
|
@ -1023,6 +1036,11 @@ Supported operations:
|
||||||
comparands are aware and have different \member{tzinfo} members,
|
comparands are aware and have different \member{tzinfo} members,
|
||||||
the comparands are first adjusted by subtracting their UTC offsets
|
the comparands are first adjusted by subtracting their UTC offsets
|
||||||
(obtained from \code{self.utcoffset()}).
|
(obtained from \code{self.utcoffset()}).
|
||||||
|
In order to stop mixed-type comparisons from falling back to the
|
||||||
|
default comparison by object address, when a \class{time} object is
|
||||||
|
compared to an object of a different type, \exception{TypeError} is
|
||||||
|
raised unless the comparison is \code{==} or \code{!=}. The latter
|
||||||
|
cases return \constant{False} or \constant{True}, respectively.
|
||||||
|
|
||||||
\item
|
\item
|
||||||
hash, use as dict key
|
hash, use as dict key
|
||||||
|
|
|
@ -133,10 +133,52 @@ class TestTZInfo(unittest.TestCase):
|
||||||
self.assertEqual(derived.utcoffset(None), offset)
|
self.assertEqual(derived.utcoffset(None), offset)
|
||||||
self.assertEqual(derived.tzname(None), 'cookie')
|
self.assertEqual(derived.tzname(None), 'cookie')
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# Base clase for testing a particular aspect of timedelta, time, date and
|
||||||
|
# datetime comparisons.
|
||||||
|
|
||||||
|
class HarmlessMixedComparison(unittest.TestCase):
|
||||||
|
# Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
|
||||||
|
|
||||||
|
# Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
|
||||||
|
# legit constructor.
|
||||||
|
|
||||||
|
def test_harmless_mixed_comparison(self):
|
||||||
|
me = self.theclass(1, 1, 1)
|
||||||
|
|
||||||
|
self.failIf(me == ())
|
||||||
|
self.failUnless(me != ())
|
||||||
|
self.failIf(() == me)
|
||||||
|
self.failUnless(() != me)
|
||||||
|
|
||||||
|
self.failUnless(me in [1, 20L, [], me])
|
||||||
|
self.failIf(me not in [1, 20L, [], me])
|
||||||
|
|
||||||
|
self.failUnless([] in [me, 1, 20L, []])
|
||||||
|
self.failIf([] not in [me, 1, 20L, []])
|
||||||
|
|
||||||
|
def test_harmful_mixed_comparison(self):
|
||||||
|
me = self.theclass(1, 1, 1)
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, lambda: me < ())
|
||||||
|
self.assertRaises(TypeError, lambda: me <= ())
|
||||||
|
self.assertRaises(TypeError, lambda: me > ())
|
||||||
|
self.assertRaises(TypeError, lambda: me >= ())
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, lambda: () < me)
|
||||||
|
self.assertRaises(TypeError, lambda: () <= me)
|
||||||
|
self.assertRaises(TypeError, lambda: () > me)
|
||||||
|
self.assertRaises(TypeError, lambda: () >= me)
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, cmp, (), me)
|
||||||
|
self.assertRaises(TypeError, cmp, me, ())
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
# timedelta tests
|
# timedelta tests
|
||||||
|
|
||||||
class TestTimeDelta(unittest.TestCase):
|
class TestTimeDelta(HarmlessMixedComparison):
|
||||||
|
|
||||||
|
theclass = timedelta
|
||||||
|
|
||||||
def test_constructor(self):
|
def test_constructor(self):
|
||||||
eq = self.assertEqual
|
eq = self.assertEqual
|
||||||
|
@ -301,15 +343,18 @@ class TestTimeDelta(unittest.TestCase):
|
||||||
self.assertEqual(cmp(t1, t2), -1)
|
self.assertEqual(cmp(t1, t2), -1)
|
||||||
self.assertEqual(cmp(t2, t1), 1)
|
self.assertEqual(cmp(t2, t1), 1)
|
||||||
|
|
||||||
for badarg in 10, 10L, 34.5, "abc", {}, [], ():
|
badargs = 10, 10L, 34.5, "abc", {}, [], ()
|
||||||
self.assertRaises(TypeError, lambda: t1 == badarg)
|
for badarg in badargs:
|
||||||
self.assertRaises(TypeError, lambda: t1 != badarg)
|
self.assertEqual(t1 == badarg, False)
|
||||||
|
self.assertEqual(t1 != badarg, True)
|
||||||
|
self.assertEqual(badarg == t1, False)
|
||||||
|
self.assertEqual(badarg != t1, True)
|
||||||
|
|
||||||
|
for badarg in badargs:
|
||||||
self.assertRaises(TypeError, lambda: t1 <= badarg)
|
self.assertRaises(TypeError, lambda: t1 <= badarg)
|
||||||
self.assertRaises(TypeError, lambda: t1 < badarg)
|
self.assertRaises(TypeError, lambda: t1 < badarg)
|
||||||
self.assertRaises(TypeError, lambda: t1 > badarg)
|
self.assertRaises(TypeError, lambda: t1 > badarg)
|
||||||
self.assertRaises(TypeError, lambda: t1 >= badarg)
|
self.assertRaises(TypeError, lambda: t1 >= badarg)
|
||||||
self.assertRaises(TypeError, lambda: badarg == t1)
|
|
||||||
self.assertRaises(TypeError, lambda: badarg != t1)
|
|
||||||
self.assertRaises(TypeError, lambda: badarg <= t1)
|
self.assertRaises(TypeError, lambda: badarg <= t1)
|
||||||
self.assertRaises(TypeError, lambda: badarg < t1)
|
self.assertRaises(TypeError, lambda: badarg < t1)
|
||||||
self.assertRaises(TypeError, lambda: badarg > t1)
|
self.assertRaises(TypeError, lambda: badarg > t1)
|
||||||
|
@ -446,7 +491,7 @@ class TestDateOnly(unittest.TestCase):
|
||||||
dt2 = dt - delta
|
dt2 = dt - delta
|
||||||
self.assertEqual(dt2, dt - days)
|
self.assertEqual(dt2, dt - days)
|
||||||
|
|
||||||
class TestDate(unittest.TestCase):
|
class TestDate(HarmlessMixedComparison):
|
||||||
# Tests here should pass for both dates and datetimes, except for a
|
# Tests here should pass for both dates and datetimes, except for a
|
||||||
# few tests that TestDateTime overrides.
|
# few tests that TestDateTime overrides.
|
||||||
|
|
||||||
|
@ -853,15 +898,17 @@ class TestDate(unittest.TestCase):
|
||||||
self.assertEqual(cmp(t1, t2), -1)
|
self.assertEqual(cmp(t1, t2), -1)
|
||||||
self.assertEqual(cmp(t2, t1), 1)
|
self.assertEqual(cmp(t2, t1), 1)
|
||||||
|
|
||||||
for badarg in 10, 10L, 34.5, "abc", {}, [], ():
|
badargs = 10, 10L, 34.5, "abc", {}, [], ()
|
||||||
self.assertRaises(TypeError, lambda: t1 == badarg)
|
for badarg in badargs:
|
||||||
self.assertRaises(TypeError, lambda: t1 != badarg)
|
self.assertEqual(t1 == badarg, False)
|
||||||
self.assertRaises(TypeError, lambda: t1 <= badarg)
|
self.assertEqual(t1 != badarg, True)
|
||||||
|
self.assertEqual(badarg == t1, False)
|
||||||
|
self.assertEqual(badarg != t1, True)
|
||||||
|
|
||||||
|
for badarg in badargs:
|
||||||
self.assertRaises(TypeError, lambda: t1 < badarg)
|
self.assertRaises(TypeError, lambda: t1 < badarg)
|
||||||
self.assertRaises(TypeError, lambda: t1 > badarg)
|
self.assertRaises(TypeError, lambda: t1 > badarg)
|
||||||
self.assertRaises(TypeError, lambda: t1 >= badarg)
|
self.assertRaises(TypeError, lambda: t1 >= badarg)
|
||||||
self.assertRaises(TypeError, lambda: badarg == t1)
|
|
||||||
self.assertRaises(TypeError, lambda: badarg != t1)
|
|
||||||
self.assertRaises(TypeError, lambda: badarg <= t1)
|
self.assertRaises(TypeError, lambda: badarg <= t1)
|
||||||
self.assertRaises(TypeError, lambda: badarg < t1)
|
self.assertRaises(TypeError, lambda: badarg < t1)
|
||||||
self.assertRaises(TypeError, lambda: badarg > t1)
|
self.assertRaises(TypeError, lambda: badarg > t1)
|
||||||
|
@ -1361,7 +1408,7 @@ class TestDateTime(TestDate):
|
||||||
alsobog = AlsoBogus()
|
alsobog = AlsoBogus()
|
||||||
self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
|
self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
|
||||||
|
|
||||||
class TestTime(unittest.TestCase):
|
class TestTime(HarmlessMixedComparison):
|
||||||
|
|
||||||
theclass = time
|
theclass = time
|
||||||
|
|
||||||
|
@ -1431,15 +1478,18 @@ class TestTime(unittest.TestCase):
|
||||||
badargs = (10, 10L, 34.5, "abc", {}, [], ())
|
badargs = (10, 10L, 34.5, "abc", {}, [], ())
|
||||||
if CMP_BUG_FIXED:
|
if CMP_BUG_FIXED:
|
||||||
badargs += (date(1, 1, 1), datetime(1, 1, 1, 1, 1), timedelta(9))
|
badargs += (date(1, 1, 1), datetime(1, 1, 1, 1, 1), timedelta(9))
|
||||||
|
|
||||||
|
for badarg in badargs:
|
||||||
|
self.assertEqual(t1 == badarg, False)
|
||||||
|
self.assertEqual(t1 != badarg, True)
|
||||||
|
self.assertEqual(badarg == t1, False)
|
||||||
|
self.assertEqual(badarg != t1, True)
|
||||||
|
|
||||||
for badarg in badargs:
|
for badarg in badargs:
|
||||||
self.assertRaises(TypeError, lambda: t1 == badarg)
|
|
||||||
self.assertRaises(TypeError, lambda: t1 != badarg)
|
|
||||||
self.assertRaises(TypeError, lambda: t1 <= badarg)
|
self.assertRaises(TypeError, lambda: t1 <= badarg)
|
||||||
self.assertRaises(TypeError, lambda: t1 < badarg)
|
self.assertRaises(TypeError, lambda: t1 < badarg)
|
||||||
self.assertRaises(TypeError, lambda: t1 > badarg)
|
self.assertRaises(TypeError, lambda: t1 > badarg)
|
||||||
self.assertRaises(TypeError, lambda: t1 >= badarg)
|
self.assertRaises(TypeError, lambda: t1 >= badarg)
|
||||||
self.assertRaises(TypeError, lambda: badarg == t1)
|
|
||||||
self.assertRaises(TypeError, lambda: badarg != t1)
|
|
||||||
self.assertRaises(TypeError, lambda: badarg <= t1)
|
self.assertRaises(TypeError, lambda: badarg <= t1)
|
||||||
self.assertRaises(TypeError, lambda: badarg < t1)
|
self.assertRaises(TypeError, lambda: badarg < t1)
|
||||||
self.assertRaises(TypeError, lambda: badarg > t1)
|
self.assertRaises(TypeError, lambda: badarg > t1)
|
||||||
|
|
17
Misc/NEWS
17
Misc/NEWS
|
@ -140,6 +140,21 @@ Extension modules
|
||||||
datetime objects (e.g., mxDateTime) a chance to intercept the
|
datetime objects (e.g., mxDateTime) a chance to intercept the
|
||||||
comparison.
|
comparison.
|
||||||
|
|
||||||
|
date, time, datetime and timedelta comparison: When the exception
|
||||||
|
for mixed-type comparisons in the last paragraph doesn't apply, if
|
||||||
|
the comparison is == then False is returned, and if the comparison is
|
||||||
|
!= then True is returned. Because dict lookup and the "in" operator
|
||||||
|
only invoke __eq__, this allows, for example,
|
||||||
|
|
||||||
|
if some_datetime in some_sequence:
|
||||||
|
and
|
||||||
|
some_dict[some_timedelta] = whatever
|
||||||
|
|
||||||
|
to work as expected, without raising TypeError just because the
|
||||||
|
sequence is heterogeneous, or the dict has mixed-type keys. [This
|
||||||
|
seems like a good idea to implement for all mixed-type comparisons
|
||||||
|
that don't want to allow falling back to address comparison.]
|
||||||
|
|
||||||
The constructors building a datetime from a timestamp could raise
|
The constructors building a datetime from a timestamp could raise
|
||||||
ValueError if the platform C localtime()/gmtime() inserted "leap
|
ValueError if the platform C localtime()/gmtime() inserted "leap
|
||||||
seconds". Leap seconds are ignored now. On such platforms, it's
|
seconds". Leap seconds are ignored now. On such platforms, it's
|
||||||
|
@ -271,7 +286,7 @@ Mac
|
||||||
|
|
||||||
- There are new dialogs EasyDialogs.AskFileForOpen, AskFileForSave
|
- There are new dialogs EasyDialogs.AskFileForOpen, AskFileForSave
|
||||||
and AskFolder. The old macfs.StandardGetFile and friends are deprecated.
|
and AskFolder. The old macfs.StandardGetFile and friends are deprecated.
|
||||||
|
|
||||||
- Most of the standard library now uses pathnames or FSRefs in preference
|
- Most of the standard library now uses pathnames or FSRefs in preference
|
||||||
of FSSpecs, and use the underlying Carbon.File and Carbon.Folder modules
|
of FSSpecs, and use the underlying Carbon.File and Carbon.Folder modules
|
||||||
in stead of macfs. macfs will probably be deprecated in the future.
|
in stead of macfs. macfs will probably be deprecated in the future.
|
||||||
|
|
|
@ -1224,6 +1224,16 @@ diff_to_bool(int diff, int op)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Raises a "can't compare" TypeError and returns NULL. */
|
||||||
|
static PyObject *
|
||||||
|
cmperror(PyObject *a, PyObject *b)
|
||||||
|
{
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"can't compare %s to %s",
|
||||||
|
a->ob_type->tp_name, b->ob_type->tp_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------------
|
/* ---------------------------------------------------------------------------
|
||||||
* Basic object allocation. These allocate Python objects of the right
|
* Basic object allocation. These allocate Python objects of the right
|
||||||
* size and type, and do the Python object-initialization bit. If there's
|
* size and type, and do the Python object-initialization bit. If there's
|
||||||
|
@ -1654,21 +1664,23 @@ delta_subtract(PyObject *left, PyObject *right)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
delta_richcompare(PyDateTime_Delta *self, PyObject *other, int op)
|
delta_richcompare(PyDateTime_Delta *self, PyObject *other, int op)
|
||||||
{
|
{
|
||||||
int diff;
|
int diff = 42; /* nonsense */
|
||||||
|
|
||||||
if (! PyDelta_CheckExact(other)) {
|
if (PyDelta_CheckExact(other)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
diff = GET_TD_DAYS(self) - GET_TD_DAYS(other);
|
||||||
"can't compare %s to %s instance",
|
if (diff == 0) {
|
||||||
self->ob_type->tp_name, other->ob_type->tp_name);
|
diff = GET_TD_SECONDS(self) - GET_TD_SECONDS(other);
|
||||||
return NULL;
|
if (diff == 0)
|
||||||
}
|
diff = GET_TD_MICROSECONDS(self) -
|
||||||
diff = GET_TD_DAYS(self) - GET_TD_DAYS(other);
|
GET_TD_MICROSECONDS(other);
|
||||||
if (diff == 0) {
|
}
|
||||||
diff = GET_TD_SECONDS(self) - GET_TD_SECONDS(other);
|
|
||||||
if (diff == 0)
|
|
||||||
diff = GET_TD_MICROSECONDS(self) -
|
|
||||||
GET_TD_MICROSECONDS(other);
|
|
||||||
}
|
}
|
||||||
|
else if (op == Py_EQ || op == Py_NE)
|
||||||
|
diff = 1; /* any non-zero value will do */
|
||||||
|
|
||||||
|
else /* stop this from falling back to address comparison */
|
||||||
|
return cmperror((PyObject *)self, other);
|
||||||
|
|
||||||
return diff_to_bool(diff, op);
|
return diff_to_bool(diff, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2443,23 +2455,23 @@ date_isocalendar(PyDateTime_Date *self)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
date_richcompare(PyDateTime_Date *self, PyObject *other, int op)
|
date_richcompare(PyDateTime_Date *self, PyObject *other, int op)
|
||||||
{
|
{
|
||||||
int diff;
|
int diff = 42; /* nonsense */
|
||||||
|
|
||||||
if (! PyDate_Check(other)) {
|
if (PyDate_Check(other))
|
||||||
if (PyObject_HasAttrString(other, "timetuple")) {
|
diff = memcmp(self->data, ((PyDateTime_Date *)other)->data,
|
||||||
/* A hook for other kinds of date objects. */
|
_PyDateTime_DATE_DATASIZE);
|
||||||
Py_INCREF(Py_NotImplemented);
|
|
||||||
return Py_NotImplemented;
|
else if (PyObject_HasAttrString(other, "timetuple")) {
|
||||||
}
|
/* A hook for other kinds of date objects. */
|
||||||
/* Stop this from falling back to address comparison. */
|
Py_INCREF(Py_NotImplemented);
|
||||||
PyErr_Format(PyExc_TypeError,
|
return Py_NotImplemented;
|
||||||
"can't compare '%s' to '%s'",
|
|
||||||
self->ob_type->tp_name,
|
|
||||||
other->ob_type->tp_name);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
diff = memcmp(self->data, ((PyDateTime_Date *)other)->data,
|
else if (op == Py_EQ || op == Py_NE)
|
||||||
_PyDateTime_DATE_DATASIZE);
|
diff = 1; /* any non-zero value will do */
|
||||||
|
|
||||||
|
else /* stop this from falling back to address comparison */
|
||||||
|
return cmperror((PyObject *)self, other);
|
||||||
|
|
||||||
return diff_to_bool(diff, op);
|
return diff_to_bool(diff, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3173,12 +3185,13 @@ time_richcompare(PyDateTime_Time *self, PyObject *other, int op)
|
||||||
int offset1, offset2;
|
int offset1, offset2;
|
||||||
|
|
||||||
if (! PyTime_Check(other)) {
|
if (! PyTime_Check(other)) {
|
||||||
|
if (op == Py_EQ || op == Py_NE) {
|
||||||
|
PyObject *result = op == Py_EQ ? Py_False : Py_True;
|
||||||
|
Py_INCREF(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
/* Stop this from falling back to address comparison. */
|
/* Stop this from falling back to address comparison. */
|
||||||
PyErr_Format(PyExc_TypeError,
|
return cmperror((PyObject *)self, other);
|
||||||
"can't compare '%s' to '%s'",
|
|
||||||
self->ob_type->tp_name,
|
|
||||||
other->ob_type->tp_name);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
if (classify_two_utcoffsets((PyObject *)self, &offset1, &n1, Py_None,
|
if (classify_two_utcoffsets((PyObject *)self, &offset1, &n1, Py_None,
|
||||||
other, &offset2, &n2, Py_None) < 0)
|
other, &offset2, &n2, Py_None) < 0)
|
||||||
|
@ -4011,12 +4024,13 @@ datetime_richcompare(PyDateTime_DateTime *self, PyObject *other, int op)
|
||||||
Py_INCREF(Py_NotImplemented);
|
Py_INCREF(Py_NotImplemented);
|
||||||
return Py_NotImplemented;
|
return Py_NotImplemented;
|
||||||
}
|
}
|
||||||
|
if (op == Py_EQ || op == Py_NE) {
|
||||||
|
PyObject *result = op == Py_EQ ? Py_False : Py_True;
|
||||||
|
Py_INCREF(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
/* Stop this from falling back to address comparison. */
|
/* Stop this from falling back to address comparison. */
|
||||||
PyErr_Format(PyExc_TypeError,
|
return cmperror((PyObject *)self, other);
|
||||||
"can't compare '%s' to '%s'",
|
|
||||||
self->ob_type->tp_name,
|
|
||||||
other->ob_type->tp_name);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classify_two_utcoffsets((PyObject *)self, &offset1, &n1,
|
if (classify_two_utcoffsets((PyObject *)self, &offset1, &n1,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue