You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PyNumber_InPlacePower ignores o3 if o1 implements __ipow__ #86069
The documentation for PyNumber_InPlacePower [1] reads:
This is the equivalent of the Python statement o1 **= o2 when o3 is Py_None, or an in-place variant of pow(o1, o2, o3) otherwise. If o3 is to be ignored, pass Py_None in its place (passing NULL for o3 would cause an illegal memory access).
However, if a class A implements __ipow__ then PyNumber_InPlacePower(o1, o2, o3) ALWAYS ignores o3 if o1 is an instance of A.
This happens because if A implements __ipow__ then PyNumber_InPlacePower always calls the nb_inplace_power slot [2] and the slot always drops the third argument [3].
This appears to have always been the case in Python, so likely a small documentation patch is all that is required. If people agree, I will open a documentation pull request.
The documentation for __ipow__ [4] suggests that the intention was to support the modulus argument, so perhaps that argues for fixing the behaviour of PyNumber_InPlacePower.
The default implementation of the nb_inplace_power slot, slot_nb_inplace_power() in Objects/typeobject.c, ignores the third argument and calls the __ipow__() method with only two arguments (including self). But an extension class can implement the nb_inplace_power slot that does not ignore the third argument. The __ipow__() wrapper accepts 2 or 3 arguments (including self), all of them are passed to the nb_inplace_power slot, which can not ignore the third argument.
This looks like a design mistake. PyNumber_InPlacePower() should not have the third argument, and __ipow__() should not accept the third argument. But currently they do, and we are stuck with this.
The fact that the third argument of PyNumber_InPlacePower()usually is not passed to the __ipow__() method cab be considered a bug. We can fix this or we can document this wart, although it is not easy to document it without referring to the implementation details (and not easy to describe the change in the behavior if we fix it).
Lets correct this! There is no way to call the 3-arg dunder with some Python API (like pow()), only directly. Or even with the current C-API (PyNumber_InPlacePower).
The current behavior is inconsistent. If __ipow__ is not defined, then the third argument is not ignored, but passed to __pow__. If __ipow__ is defined, then the third argument is ignored. For extension classes it is not ignored, it is only ignored for Python classes.
One way to solve this is to stop ignoring the third argument for __ipow__. #130225 implements this way.
Other way -- deprecate passing non-None third argument in PyNumber_InPlacePower() and in the __ipow__ wrapper.
Activity
hodgestar commentedon Oct 1, 2020
The documentation for PyNumber_InPlacePower [1] reads:
This is the equivalent of the Python statement o1 **= o2 when o3 is Py_None, or an in-place variant of pow(o1, o2, o3) otherwise. If o3 is to be ignored, pass Py_None in its place (passing NULL for o3 would cause an illegal memory access).
However, if a class A implements __ipow__ then PyNumber_InPlacePower(o1, o2, o3) ALWAYS ignores o3 if o1 is an instance of A.
This happens because if A implements __ipow__ then PyNumber_InPlacePower always calls the nb_inplace_power slot [2] and the slot always drops the third argument [3].
This appears to have always been the case in Python, so likely a small documentation patch is all that is required. If people agree, I will open a documentation pull request.
[1] https://docs.python.org/3/c-api/number.html?highlight=pynumber_inplacepower#c.PyNumber_InPlacePower
[2] https://github.com/python/cpython/blob/master/Objects/abstract.c#L1164
[3] https://github.com/python/cpython/blob/master/Objects/typeobject.c#L6631-L6636
hodgestar commentedon Oct 1, 2020
The documentation for __ipow__ [4] suggests that the intention was to support the modulus argument, so perhaps that argues for fixing the behaviour of PyNumber_InPlacePower.
[4] https://docs.python.org/3/reference/datamodel.html#object.\_\_ipow__
ammaraskar commentedon Oct 1, 2020
Huh, this is weird. I wonder why the data model provides the signature
if the slot always seems to drop the third argument.
Adding Brett to the nosy who found another issue around the special casing of pow/** recently. See also bpo-38302
4 remaining items
pythongh-86069: correct object.__ipow__() signature
serhiy-storchaka commentedon Feb 14, 2025
The default implementation of the
nb_inplace_powerslot,slot_nb_inplace_power()inObjects/typeobject.c, ignores the third argument and calls the__ipow__()method with only two arguments (includingself). But an extension class can implement thenb_inplace_powerslot that does not ignore the third argument. The__ipow__()wrapper accepts 2 or 3 arguments (includingself), all of them are passed to thenb_inplace_powerslot, which can not ignore the third argument.This looks like a design mistake.
PyNumber_InPlacePower()should not have the third argument, and__ipow__()should not accept the third argument. But currently they do, and we are stuck with this.The fact that the third argument of
PyNumber_InPlacePower()usually is not passed to the__ipow__()method cab be considered a bug. We can fix this or we can document this wart, although it is not easy to document it without referring to the implementation details (and not easy to describe the change in the behavior if we fix it).skirpichev commentedon Feb 14, 2025
Lets correct this! There is no way to call the 3-arg dunder with some Python API (like pow()), only directly. Or even with the current C-API (PyNumber_InPlacePower).
Never. At least in the current implementation.
pythongh-86069: Add more PyNumber_InPlacePower() tests
gh-86069: Add more PyNumber_InPlacePower() tests (GH-130111)
pythongh-86069: Add more PyNumber_InPlacePower() tests (pythonGH-130111)
pythongh-86069: Add more PyNumber_InPlacePower() tests (pythonGH-130111)
[3.13] gh-86069: Add more PyNumber_InPlacePower() tests (GH-130111) (G…
[3.12] gh-86069: Add more PyNumber_InPlacePower() tests (GH-130111) (G…
pythongh-86069: Do not ignore the third argument in slot_nb_inplace_p…
serhiy-storchaka commentedon Feb 18, 2025
The current behavior is inconsistent. If
__ipow__is not defined, then the third argument is not ignored, but passed to__pow__. If__ipow__is defined, then the third argument is ignored. For extension classes it is not ignored, it is only ignored for Python classes.One way to solve this is to stop ignoring the third argument for
__ipow__. #130225 implements this way.Other way -- deprecate passing non-None third argument in
PyNumber_InPlacePower()and in the__ipow__wrapper.