diff --git a/Doc/c-api/number.rst b/Doc/c-api/number.rst index ad8b5935258fa7..0438869d9bf7fb 100644 --- a/Doc/c-api/number.rst +++ b/Doc/c-api/number.rst @@ -196,8 +196,9 @@ Number Protocol See the built-in function :func:`pow`. Returns ``NULL`` on failure. The operation is done *in-place* when *o1* supports it. This is the equivalent of the Python - statement ``o1 **= o2`` when o3 is :c:data:`Py_None`, or an in-place variant of - ``pow(o1, o2, o3)`` otherwise. If *o3* is to be ignored, pass :c:data:`Py_None` + statement ``o1 **= o2`` when o3 is :c:data:`Py_None`, or hypothetical in-place variant of + ``pow(o1, o2, o3)`` (not supported in Python) otherwise. + If *o3* is to be ignored, pass :c:data:`Py_None` in its place (passing ``NULL`` for *o3* would cause an illegal memory access). diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 005a768f684e2c..40d6c610dda63a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -3389,6 +3389,13 @@ left undefined. :ref:`faq-augmented-assignment-tuple-error`), but this behavior is in fact part of the data model. + Note that :meth:`__ipow__` should be defined to accept an optional third + argument if the third non-``None`` argument of + :c:func:`PyNumber_InPlacePower` is to be supported. + + .. versionchanged:: next + :meth:`!__ipow__` can now be called with three arguments in + :c:func:`!PyNumber_InPlacePower`. .. method:: object.__neg__(self) object.__pos__(self) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 7131eeb697eb69..a0a3a73e2e7a41 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -75,6 +75,11 @@ New features Other language changes ====================== +* The :meth:`~object.__ipow__` method should be defined to accept an optional + third argument (as it was documented) to support non-``None`` third argument + in :c:func:`PyNumber_InPlacePower` which no longer ignored if + :meth:`!__ipow__` is defined. + (Contributed by Serhiy Storchaka in :gh:`86069`.) New modules diff --git a/Lib/test/test_capi/test_number.py b/Lib/test/test_capi/test_number.py index bdd8868529f632..ddbc2f4ac38fed 100644 --- a/Lib/test/test_capi/test_number.py +++ b/Lib/test/test_capi/test_number.py @@ -246,8 +246,7 @@ def __ipow__(*args): x = X() self.assertEqual(inplacepower(x, 11), (x, 11)) - # XXX: In-place power doesn't pass the third arg to __ipow__. - self.assertEqual(inplacepower(x, 11, 5), (x, 11)) + self.assertEqual(inplacepower(x, 11, 5), (x, 11, 5)) def test_long(self): # Test PyNumber_Long() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-17-15-04-19.gh-issue-86069.qeWCJF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-17-15-04-19.gh-issue-86069.qeWCJF.rst new file mode 100644 index 00000000000000..e67e557f9b5dc8 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-17-15-04-19.gh-issue-86069.qeWCJF.rst @@ -0,0 +1,4 @@ +The default implementation of the +:c:member:`~PyNumberMethods.nb_inplace_power` slot no longer ignores the +third argument, but passes it to the :meth:`~object.__ipow__` method if its +value is not ``None``. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index a7ab69fef4c721..734e175bcba746 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10081,8 +10081,14 @@ SLOT1(slot_nb_inplace_remainder, __imod__, PyObject *) static PyObject * slot_nb_inplace_power(PyObject *self, PyObject * arg1, PyObject *arg2) { - PyObject *stack[2] = {self, arg1}; - return vectorcall_method(&_Py_ID(__ipow__), stack, 2); + if (arg2 == Py_None) { + PyObject *stack[2] = {self, arg1}; + return vectorcall_method(&_Py_ID(__ipow__), stack, 2); + } + else { + PyObject *stack[3] = {self, arg1, arg2}; + return vectorcall_method(&_Py_ID(__ipow__), stack, 3); + } } SLOT1(slot_nb_inplace_lshift, __ilshift__, PyObject *) SLOT1(slot_nb_inplace_rshift, __irshift__, PyObject *)