Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PyNumber_InPlacePower ignores o3 if o1 implements __ipow__ #86069

Open
hodgestar mannequin opened this issue Oct 1, 2020 · 9 comments
Open

PyNumber_InPlacePower ignores o3 if o1 implements __ipow__ #86069

hodgestar mannequin opened this issue Oct 1, 2020 · 9 comments
Labels
3.7 (EOL) end of life 3.8 (EOL) end of life 3.9 only security fixes 3.10 only security fixes docs Documentation in the Doc dir topic-C-API

Comments

@hodgestar
Copy link
Mannequin

hodgestar mannequin commented Oct 1, 2020

BPO 41903
Nosy @brettcannon, @ammaraskar

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2020-10-01.15:10:02.476>
labels = ['type-bug', '3.8', '3.9', '3.10', 'expert-C-API', '3.7']
title = 'PyNumber_InPlacePower ignores o3 if o1 implements __ipow__'
updated_at = <Date 2020-10-01.15:48:52.092>
user = 'https://bugs.python.org/hodgestar'

bugs.python.org fields:

activity = <Date 2020-10-01.15:48:52.092>
actor = 'ammar2'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['C API']
creation = <Date 2020-10-01.15:10:02.476>
creator = 'hodgestar'
dependencies = []
files = []
hgrepos = []
issue_num = 41903
keywords = []
message_count = 5.0
messages = ['377758', '377760', '377761', '377766', '377767']
nosy_count = 3.0
nosy_names = ['brett.cannon', 'hodgestar', 'ammar2']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue41903'
versions = ['Python 3.5', 'Python 3.6', 'Python 3.7', 'Python 3.8', 'Python 3.9', 'Python 3.10']

Linked PRs

@hodgestar
Copy link
Mannequin Author

hodgestar mannequin commented 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 hodgestar mannequin added 3.7 (EOL) end of life 3.8 (EOL) end of life 3.9 only security fixes 3.10 only security fixes topic-C-API type-bug An unexpected behavior, bug, or error labels Oct 1, 2020
@hodgestar
Copy link
Mannequin Author

hodgestar mannequin commented 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
Copy link
Member

Huh, this is weird. I wonder why the data model provides the signature

  object.__ipow__(self, other[, modulo])

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

@ammaraskar
Copy link
Member

Also of note, there is actually no way to call PyNumber_InPlacePower from pure python from what I can tell. Even libraries like numpy don't implement the three-argument version of the nb_inplace_power slot.

@ammaraskar
Copy link
Member

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@skirpichev
Copy link
Member

This looks like a documentation issue for me.

CC @picnixz
CC @serhiy-storchaka

@skirpichev skirpichev added docs Documentation in the Doc dir and removed type-bug An unexpected behavior, bug, or error labels Feb 14, 2025
skirpichev added a commit to skirpichev/cpython that referenced this issue Feb 14, 2025
There is no way to call ternary form of this dunder.
@serhiy-storchaka
Copy link
Member

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).

@skirpichev
Copy link
Member

This looks like a design mistake.

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).

usually is not passed

Never. At least in the current implementation.

serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Feb 14, 2025
serhiy-storchaka added a commit that referenced this issue Feb 17, 2025
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Feb 17, 2025
Test it with the third argument.
(cherry picked from commit cfe4103)

Co-authored-by: Serhiy Storchaka <[email protected]>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Feb 17, 2025
Test it with the third argument.
(cherry picked from commit cfe41037eb5293a051846ddc0b4afdb7a5f60540)

Co-authored-by: Serhiy Storchaka <[email protected]>
serhiy-storchaka added a commit that referenced this issue Feb 17, 2025
…H-130211)

Test it with the third argument.
(cherry picked from commit cfe4103)

Co-authored-by: Serhiy Storchaka <[email protected]>
serhiy-storchaka added a commit that referenced this issue Feb 17, 2025
…H-130212)

Test it with the third argument.
(cherry picked from commit cfe4103)

Co-authored-by: Serhiy Storchaka <[email protected]>
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Feb 17, 2025
…ower()

The default implementation of the nb_inplace_power slot no longer
ignores the third argument, but passes it to the __ipow__ method
if its value is not None.
@serhiy-storchaka
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.7 (EOL) end of life 3.8 (EOL) end of life 3.9 only security fixes 3.10 only security fixes docs Documentation in the Doc dir topic-C-API
Projects
Status: Todo
Development

No branches or pull requests

3 participants