gh-49766: Make date-datetime comparison more symmetric and flexible (GH-114760)

Now the special comparison methods like `__eq__` and `__lt__` return
NotImplemented if one of comparands is date and other is datetime
instead of ignoring the time part and the time zone or forcefully
return "not equal" or raise TypeError.

It makes comparison of date and datetime subclasses more symmetric
and allows to change the default behavior by overriding
the special comparison methods in subclasses.

It is now the same as if date and datetime was independent classes.
This commit is contained in:
Serhiy Storchaka 2024-02-11 13:06:43 +02:00 committed by GitHub
parent d9d6909697
commit b104360788
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 91 additions and 84 deletions

View file

@ -1816,16 +1816,6 @@ diff_to_bool(int diff, int op)
Py_RETURN_RICHCOMPARE(diff, 0, op);
}
/* 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",
Py_TYPE(a)->tp_name, Py_TYPE(b)->tp_name);
return NULL;
}
/* ---------------------------------------------------------------------------
* Class implementations.
*/
@ -3448,7 +3438,15 @@ date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
static PyObject *
date_richcompare(PyObject *self, PyObject *other, int op)
{
if (PyDate_Check(other)) {
/* Since DateTime is a subclass of Date, if the other object is
* a DateTime, it would compute an equality testing or an ordering
* based on the date part alone, and we don't want that.
* So return NotImplemented here in that case.
* If a subclass wants to change this, it's up to the subclass to do so.
* The behavior is the same as if Date and DateTime were independent
* classes.
*/
if (PyDate_Check(other) && !PyDateTime_Check(other)) {
int diff = memcmp(((PyDateTime_Date *)self)->data,
((PyDateTime_Date *)other)->data,
_PyDateTime_DATE_DATASIZE);
@ -5880,21 +5878,7 @@ datetime_richcompare(PyObject *self, PyObject *other, int op)
PyObject *offset1, *offset2;
int diff;
if (! PyDateTime_Check(other)) {
if (PyDate_Check(other)) {
/* Prevent invocation of date_richcompare. We want to
return NotImplemented here to give the other object
a chance. But since DateTime is a subclass of
Date, if the other object is a Date, it would
compute an ordering based on the date part alone,
and we don't want that. So force unequal or
uncomparable here in that case. */
if (op == Py_EQ)
Py_RETURN_FALSE;
if (op == Py_NE)
Py_RETURN_TRUE;
return cmperror(self, other);
}
if (!PyDateTime_Check(other)) {
Py_RETURN_NOTIMPLEMENTED;
}