diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index f7e09fd771eaf2..24e4fd6abe4636 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -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__}" diff --git a/Misc/NEWS.d/next/Library/2023-04-07-22-22-42.gh-issue-65329.dN0uDk.rst b/Misc/NEWS.d/next/Library/2023-04-07-22-22-42.gh-issue-65329.dN0uDk.rst new file mode 100644 index 00000000000000..9b2fbc37e32b07 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-07-22-22-42.gh-issue-65329.dN0uDk.rst @@ -0,0 +1 @@ +Make comparable the equivalent instances of :func:`functools.partial` objects. Patched by Shakur Shams Mullick and Furkan Onder. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index f077a0ed329516..cb63ec87abe07e 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -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}, @@ -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},