Skip to content
20 changes: 20 additions & 0 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,26 @@ def test_repr(self):
[f'{name}({capture!r}, {args_repr}, {kwargs_repr})'
for kwargs_repr in kwargs_reprs])

def test_equality(self):
p = functools.partial(capture, 1, 2, a=10, b=20)
q = functools.partial(capture, 1, 2, a=10, b=20)
self.assertTrue(p == q)
self.assertFalse(p != q)
self.assertTrue(p.__eq__(q))
self.assertFalse(p.__ne__(q))

q = self.partial(capture, 1, 2, a=10)
self.assertFalse(p == q)
self.assertTrue(p != q)

self.assertNotEqual(p, capture)
self.assertNotEqual(q, capture)

a = self.partial(capture)
b = self.partial(signature)
self.assertFalse(a == b)
self.assertTrue(a != b)

def test_recursive_repr(self):
if self.partial in (c_functools.partial, py_functools.partial):
name = 'functools.partial'
Expand Down
38 changes: 38 additions & 0 deletions Modules/_functoolsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,43 @@ static PyMethodDef partial_methods[] = {
{NULL, NULL} /* sentinel */
};

static PyObject *
partial_richcompare(PyObject *self, PyObject *other, int op)
{
partialobject *a, *b;
PyObject *res;
int eq;

if (op != Py_EQ && op != Py_NE) {
Py_RETURN_NOTIMPLEMENTED;
}

a = (partialobject *) self;
b = (partialobject *) other;

eq = PyObject_RichCompareBool(a->fn, b->fn, Py_EQ);
if (eq == 1) {
eq = PyObject_RichCompareBool(a->args, b->args, Py_EQ);
if (eq == 1) {
eq = PyObject_RichCompareBool(a->kw, b->kw, Py_EQ);
}
}

if (eq < 0) {
return NULL;
}

if (op == Py_EQ) {
res = eq ? Py_True : Py_False;
}
else {
res = eq ? Py_False : Py_True;
}

Py_INCREF(res);
return res;
}

static PyType_Slot partial_type_slots[] = {
{Py_tp_dealloc, partial_dealloc},
{Py_tp_repr, partial_repr},
Expand All @@ -486,6 +523,7 @@ static PyType_Slot partial_type_slots[] = {
{Py_tp_doc, (void *)partial_doc},
{Py_tp_traverse, partial_traverse},
{Py_tp_clear, partial_clear},
{Py_tp_richcompare, partial_richcompare},
{Py_tp_methods, partial_methods},
{Py_tp_members, partial_memberlist},
{Py_tp_getset, partial_getsetlist},
Expand Down