mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
gh-130104: Call __rpow__ in ternary pow() if necessary (GH-130251)
Previously it was only called in binary pow() and the binary power operator.
This commit is contained in:
parent
72da4a4458
commit
62ff86fa55
10 changed files with 74 additions and 18 deletions
|
@ -3319,7 +3319,7 @@ left undefined.
|
||||||
:meth:`__divmod__` method should be the equivalent to using
|
:meth:`__divmod__` method should be the equivalent to using
|
||||||
:meth:`__floordiv__` and :meth:`__mod__`; it should not be related to
|
:meth:`__floordiv__` and :meth:`__mod__`; it should not be related to
|
||||||
:meth:`__truediv__`. Note that :meth:`__pow__` should be defined to accept
|
:meth:`__truediv__`. Note that :meth:`__pow__` should be defined to accept
|
||||||
an optional third argument if the ternary version of the built-in :func:`pow`
|
an optional third argument if the three-argument version of the built-in :func:`pow`
|
||||||
function is to be supported.
|
function is to be supported.
|
||||||
|
|
||||||
If one of those methods does not support the operation with the supplied
|
If one of those methods does not support the operation with the supplied
|
||||||
|
@ -3356,10 +3356,15 @@ left undefined.
|
||||||
is called if ``type(x).__sub__(x, y)`` returns :data:`NotImplemented` or ``type(y)``
|
is called if ``type(x).__sub__(x, y)`` returns :data:`NotImplemented` or ``type(y)``
|
||||||
is a subclass of ``type(x)``. [#]_
|
is a subclass of ``type(x)``. [#]_
|
||||||
|
|
||||||
.. index:: pair: built-in function; pow
|
Note that :meth:`__rpow__` should be defined to accept an optional third
|
||||||
|
argument if the three-argument version of the built-in :func:`pow` function
|
||||||
|
is to be supported.
|
||||||
|
|
||||||
Note that ternary :func:`pow` will not try calling :meth:`__rpow__` (the
|
.. versionchanged:: next
|
||||||
coercion rules would become too complicated).
|
|
||||||
|
Three-argument :func:`pow` now try calling :meth:`~object.__rpow__` if necessary.
|
||||||
|
Previously it was only called in two-argument :func:`!pow` and the binary
|
||||||
|
power operator.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -458,6 +458,11 @@ Other language changes
|
||||||
The testbed can also be used to run the test suite of projects other than
|
The testbed can also be used to run the test suite of projects other than
|
||||||
CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.)
|
CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.)
|
||||||
|
|
||||||
|
* Three-argument :func:`pow` now try calling :meth:`~object.__rpow__` if necessary.
|
||||||
|
Previously it was only called in two-argument :func:`!pow` and the binary
|
||||||
|
power operator.
|
||||||
|
(Contributed by Serhiy Storchaka in :gh:`130104`.)
|
||||||
|
|
||||||
* Add a built-in implementation for HMAC (:rfc:`2104`) using formally verified
|
* Add a built-in implementation for HMAC (:rfc:`2104`) using formally verified
|
||||||
code from the `HACL* <https://github.com/hacl-star/hacl-star/>`__ project.
|
code from the `HACL* <https://github.com/hacl-star/hacl-star/>`__ project.
|
||||||
This implementation is used as a fallback when the OpenSSL implementation
|
This implementation is used as a fallback when the OpenSSL implementation
|
||||||
|
|
|
@ -2440,12 +2440,12 @@ class Decimal(object):
|
||||||
|
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def __rpow__(self, other, context=None):
|
def __rpow__(self, other, modulo=None, context=None):
|
||||||
"""Swaps self/other and returns __pow__."""
|
"""Swaps self/other and returns __pow__."""
|
||||||
other = _convert_other(other)
|
other = _convert_other(other)
|
||||||
if other is NotImplemented:
|
if other is NotImplemented:
|
||||||
return other
|
return other
|
||||||
return other.__pow__(self, context=context)
|
return other.__pow__(self, modulo, context=context)
|
||||||
|
|
||||||
def normalize(self, context=None):
|
def normalize(self, context=None):
|
||||||
"""Normalize- strip trailing 0s, change anything equal to 0 to 0e0"""
|
"""Normalize- strip trailing 0s, change anything equal to 0 to 0e0"""
|
||||||
|
|
|
@ -905,8 +905,10 @@ class Fraction(numbers.Rational):
|
||||||
else:
|
else:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __rpow__(b, a):
|
def __rpow__(b, a, modulo=None):
|
||||||
"""a ** b"""
|
"""a ** b"""
|
||||||
|
if modulo is not None:
|
||||||
|
return NotImplemented
|
||||||
if b._denominator == 1 and b._numerator >= 0:
|
if b._denominator == 1 and b._numerator >= 0:
|
||||||
# If a is an int, keep it that way if possible.
|
# If a is an int, keep it that way if possible.
|
||||||
return a ** b._numerator
|
return a ** b._numerator
|
||||||
|
|
|
@ -237,9 +237,8 @@ class CAPITest(unittest.TestCase):
|
||||||
x = X()
|
x = X()
|
||||||
self.assertEqual(power(4, x), (x, 4))
|
self.assertEqual(power(4, x), (x, 4))
|
||||||
self.assertEqual(inplacepower(4, x), (x, 4))
|
self.assertEqual(inplacepower(4, x), (x, 4))
|
||||||
# XXX: Three-arg power doesn't use __rpow__.
|
self.assertEqual(power(4, x, 5), (x, 4, 5))
|
||||||
self.assertRaises(TypeError, power, 4, x, 5)
|
self.assertEqual(inplacepower(4, x, 5), (x, 4, 5))
|
||||||
self.assertRaises(TypeError, inplacepower, 4, x, 5)
|
|
||||||
|
|
||||||
class X:
|
class X:
|
||||||
def __ipow__(*args):
|
def __ipow__(*args):
|
||||||
|
|
|
@ -4493,12 +4493,10 @@ class Coverage:
|
||||||
self.assertIs(Decimal("NaN").fma(7, 1).is_nan(), True)
|
self.assertIs(Decimal("NaN").fma(7, 1).is_nan(), True)
|
||||||
# three arg power
|
# three arg power
|
||||||
self.assertEqual(pow(Decimal(10), 2, 7), 2)
|
self.assertEqual(pow(Decimal(10), 2, 7), 2)
|
||||||
|
self.assertEqual(pow(10, Decimal(2), 7), 2)
|
||||||
if self.decimal == C:
|
if self.decimal == C:
|
||||||
self.assertEqual(pow(10, Decimal(2), 7), 2)
|
|
||||||
self.assertEqual(pow(10, 2, Decimal(7)), 2)
|
self.assertEqual(pow(10, 2, Decimal(7)), 2)
|
||||||
else:
|
else:
|
||||||
# XXX: Three-arg power doesn't use __rpow__.
|
|
||||||
self.assertRaises(TypeError, pow, 10, Decimal(2), 7)
|
|
||||||
# XXX: There is no special method to dispatch on the
|
# XXX: There is no special method to dispatch on the
|
||||||
# third arg of three-arg power.
|
# third arg of three-arg power.
|
||||||
self.assertRaises(TypeError, pow, 10, 2, Decimal(7))
|
self.assertRaises(TypeError, pow, 10, 2, Decimal(7))
|
||||||
|
|
|
@ -3515,6 +3515,10 @@ class ClassPropertiesAndMethods(unittest.TestCase):
|
||||||
self.assertEqual(repr(2 ** I(3)), "I(8)")
|
self.assertEqual(repr(2 ** I(3)), "I(8)")
|
||||||
self.assertEqual(repr(I(2) ** 3), "I(8)")
|
self.assertEqual(repr(I(2) ** 3), "I(8)")
|
||||||
self.assertEqual(repr(pow(I(2), I(3), I(5))), "I(3)")
|
self.assertEqual(repr(pow(I(2), I(3), I(5))), "I(3)")
|
||||||
|
self.assertEqual(repr(pow(I(2), I(3), 5)), "I(3)")
|
||||||
|
self.assertEqual(repr(pow(I(2), 3, 5)), "I(3)")
|
||||||
|
self.assertEqual(repr(pow(2, I(3), 5)), "I(3)")
|
||||||
|
self.assertEqual(repr(pow(2, 3, I(5))), "3")
|
||||||
class S(str):
|
class S(str):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.lower() == other.lower()
|
return self.lower() == other.lower()
|
||||||
|
|
|
@ -1707,6 +1707,12 @@ class FractionTest(unittest.TestCase):
|
||||||
self.assertRaisesMessage(TypeError,
|
self.assertRaisesMessage(TypeError,
|
||||||
message % ("Fraction", "int", "int"),
|
message % ("Fraction", "int", "int"),
|
||||||
pow, F(3), 4, 5)
|
pow, F(3), 4, 5)
|
||||||
|
self.assertRaisesMessage(TypeError,
|
||||||
|
message % ("int", "Fraction", "int"),
|
||||||
|
pow, 3, F(4), 5)
|
||||||
|
self.assertRaisesMessage(TypeError,
|
||||||
|
message % ("int", "int", "Fraction"),
|
||||||
|
pow, 3, 4, F(5))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Three-argument :func:`pow` now try calling :meth:`~object.__rpow__` if
|
||||||
|
necessary.
|
||||||
|
Previously it was only called in two-argument :func:`!pow` and the binary
|
||||||
|
power operator.
|
|
@ -9992,13 +9992,46 @@ slot_nb_power(PyObject *self, PyObject *other, PyObject *modulus)
|
||||||
{
|
{
|
||||||
if (modulus == Py_None)
|
if (modulus == Py_None)
|
||||||
return slot_nb_power_binary(self, other);
|
return slot_nb_power_binary(self, other);
|
||||||
/* Three-arg power doesn't use __rpow__. But ternary_op
|
|
||||||
can call this when the second argument's type uses
|
/* The following code is a copy of SLOT1BINFULL, but for three arguments. */
|
||||||
slot_nb_power, so check before calling self.__pow__. */
|
PyObject* stack[3];
|
||||||
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
|
int do_other = !Py_IS_TYPE(self, Py_TYPE(other)) &&
|
||||||
|
Py_TYPE(other)->tp_as_number != NULL &&
|
||||||
|
Py_TYPE(other)->tp_as_number->nb_power == slot_nb_power;
|
||||||
if (Py_TYPE(self)->tp_as_number != NULL &&
|
if (Py_TYPE(self)->tp_as_number != NULL &&
|
||||||
Py_TYPE(self)->tp_as_number->nb_power == slot_nb_power) {
|
Py_TYPE(self)->tp_as_number->nb_power == slot_nb_power) {
|
||||||
PyObject* stack[3] = {self, other, modulus};
|
PyObject *r;
|
||||||
return vectorcall_method(&_Py_ID(__pow__), stack, 3);
|
if (do_other && PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self))) {
|
||||||
|
int ok = method_is_overloaded(self, other, &_Py_ID(__rpow__));
|
||||||
|
if (ok < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
stack[0] = other;
|
||||||
|
stack[1] = self;
|
||||||
|
stack[2] = modulus;
|
||||||
|
r = vectorcall_maybe(tstate, &_Py_ID(__rpow__), stack, 3);
|
||||||
|
if (r != Py_NotImplemented)
|
||||||
|
return r;
|
||||||
|
Py_DECREF(r);
|
||||||
|
do_other = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack[0] = self;
|
||||||
|
stack[1] = other;
|
||||||
|
stack[2] = modulus;
|
||||||
|
r = vectorcall_maybe(tstate, &_Py_ID(__pow__), stack, 3);
|
||||||
|
if (r != Py_NotImplemented ||
|
||||||
|
Py_IS_TYPE(other, Py_TYPE(self)))
|
||||||
|
return r;
|
||||||
|
Py_DECREF(r);
|
||||||
|
}
|
||||||
|
if (do_other) {
|
||||||
|
stack[0] = other;
|
||||||
|
stack[1] = self;
|
||||||
|
stack[2] = modulus;
|
||||||
|
return vectorcall_maybe(tstate, &_Py_ID(__rpow__), stack, 3);
|
||||||
}
|
}
|
||||||
Py_RETURN_NOTIMPLEMENTED;
|
Py_RETURN_NOTIMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue