bpo-38302. __rpow__ now called when __ipow__ returns NotImplemented#16459
bpo-38302. __rpow__ now called when __ipow__ returns NotImplemented#16459brettcannon merged 9 commits intopython:masterfrom
Conversation
brandtbucher
left a comment
There was a problem hiding this comment.
Thanks, this looks good! Just noticed a couple of small things:
Objects/abstract.c
Outdated
There was a problem hiding this comment.
PyNumber_InPlacePower can now just mirror the other PyNumber_InPlace* functions, right?
So this is the only line that's needed here.
Lib/test/test_descr.py
Outdated
There was a problem hiding this comment.
It looks like we're also lacking a test for when A doesn't define __ipow__. Do you mind adding one?
There was a problem hiding this comment.
Sure, added the test
There was a problem hiding this comment.
Could you add a test for the two fallbacks and ensure the same class is returning NotImplemented and implementing the regular power methods i.e something like:
class A:
def __ipow__(self, other):
return NotImplemented
class B(A):
def __pow__(self, other):
return 1
class C(A):
def __rpow__(self, other):
return 2The linked bug is only exhibited when a class has an ipow slot but when called it returns NotImplemented.
There was a problem hiding this comment.
Not sure I understood correctly what you meant, but I updated the test to also test the __pow__ fallback.
There was a problem hiding this comment.
Thank you, what you have now looks good. What I meant was that to catch the original bug/properly be a regression test, the class has to look like:
class A:
def __ipow__(self, _):
return NotImplemented
def __pow__(self, other):
...if the class looks like
class A:
def __pow__(self, other):
...then the old logic returns a false here and correctly goes to the non-inplace version of power anyway:
Lines 1162 to 1163 in 8e19c8b
There was a problem hiding this comment.
Ah yes, you are right. Thanks!
This comment has been minimized.
This comment has been minimized.
brandtbucher
left a comment
There was a problem hiding this comment.
@ashkop, one more small thing in the new test. Then it looks good!
Lib/test/test_descr.py
Outdated
There was a problem hiding this comment.
This can just be:
| a = A() | |
| a = object() |
Then you don't need the class definition above!
Lib/test/test_descr.py
Outdated
There was a problem hiding this comment.
Could you add a test for the two fallbacks and ensure the same class is returning NotImplemented and implementing the regular power methods i.e something like:
class A:
def __ipow__(self, other):
return NotImplemented
class B(A):
def __pow__(self, other):
return 1
class C(A):
def __rpow__(self, other):
return 2The linked bug is only exhibited when a class has an ipow slot but when called it returns NotImplemented.
There was a problem hiding this comment.
Just fyi, @brettcannon posted on the original bug pointing out that this would not call __pow__ either. I'd recommend changing this to
If :func:`object.__ipow__` returns :const:`NotImplemented`, the operator will correctly
fall back to :func:`object.__pow__` and :func:`object.__rpow__` as expected.It might also be worth mentioning this in whatsnew, even though it's obscure it is potentially a change in behavior that people should be informed about.
There was a problem hiding this comment.
Updated the news entry, and also added a line to What's New
Objects/abstract.c
Outdated
There was a problem hiding this comment.
Thanks for fixing this! Really weird that op_name went unused so long. Would you mind adding a test for this like:
x = None
with self.assertRaises(TypeError) as cm:
x **= 2
self.assertIn('unsupported operand type(s) for **=', str(cm.exception))There was a problem hiding this comment.
Thanks, added a new test
a2476e7 to
25f95b5
Compare
Lib/test/test_descr.py
Outdated
There was a problem hiding this comment.
Thank you, what you have now looks good. What I meant was that to catch the original bug/properly be a regression test, the class has to look like:
class A:
def __ipow__(self, _):
return NotImplemented
def __pow__(self, other):
...if the class looks like
class A:
def __pow__(self, other):
...then the old logic returns a false here and correctly goes to the non-inplace version of power anyway:
Lines 1162 to 1163 in 8e19c8b
|
Merged upstream, added |
|
I also tested this PR against my own test suite that uncovered this issue and it passed! |
|
@brettcannon: Please replace |
|
@ashkop thanks for the PR! |
Basically copied the behavior of
binary_iopfor the ternary operator. Also made sure thatop_nameis used in error message.https://bugs.python.org/issue38302