Skip to content
32 changes: 32 additions & 0 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,38 @@ 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))
self.assertTrue(p.func == q.func)
self.assertTrue(p.func == capture)
self.assertTrue(p.args == q.args)
self.assertTrue(p.keywords == q.keywords)

k = self.partial(capture, 1, 2, a=10)
self.assertFalse(p == k)
self.assertTrue(p != k)
self.assertTrue(p.func == k.func)
self.assertTrue(p.args == k.args)
self.assertFalse(p.keywords == k.keywords)

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

# Check against other types
self.assertNotEqual(p.func, repr)
self.assertNotEqual(p, capture)
self.assertNotEqual(q, capture)
self.assertNotEqual(q, functools.partial)


def test_recursive_repr(self):
name = f"{self.partial.__module__}.{self.partial.__qualname__}"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make comparable the equivalent instances of :func:`functools.partial` objects. Patched by Shakur Shams Mullick and Furkan Onder.
39 changes: 39 additions & 0 deletions Modules/_functoolsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,44 @@ 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_IS_TYPE(self, Py_TYPE(other)))
{
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 @@ -853,6 +891,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
Loading