mirror of https://github.com/python/cpython
bpo-38302: __pow__/__rpow__ now called when __ipow__ returns NotImplemented (#16459)
This commit is contained in:
parent
5e48e83688
commit
cc02b4f2e8
|
@ -276,6 +276,9 @@ Other Language Changes
|
||||||
the :meth:`~object.__int__` method but do not have the
|
the :meth:`~object.__int__` method but do not have the
|
||||||
:meth:`~object.__index__` method).
|
:meth:`~object.__index__` method).
|
||||||
(Contributed by Serhiy Storchaka in :issue:`37999`.)
|
(Contributed by Serhiy Storchaka in :issue:`37999`.)
|
||||||
|
* If :func:`object.__ipow__` returns :const:`NotImplemented`, the operator will
|
||||||
|
correctly fall back to :func:`object.__pow__` and :func:`object.__rpow__` as expected.
|
||||||
|
(Contributed by Alex Shkop in :issue:`38302`.)
|
||||||
|
|
||||||
* Assignment expressions can now be used unparenthesized within set literals
|
* Assignment expressions can now be used unparenthesized within set literals
|
||||||
and set comprehensions, as well as in sequence indexes (but not slices).
|
and set comprehensions, as well as in sequence indexes (but not slices).
|
||||||
|
|
|
@ -3903,6 +3903,48 @@ order (MRO) for bases """
|
||||||
a = C()
|
a = C()
|
||||||
a **= 2
|
a **= 2
|
||||||
|
|
||||||
|
def test_ipow_returns_not_implemented(self):
|
||||||
|
class A:
|
||||||
|
def __ipow__(self, other):
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
class B(A):
|
||||||
|
def __rpow__(self, other):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
class C(A):
|
||||||
|
def __pow__(self, other):
|
||||||
|
return 2
|
||||||
|
a = A()
|
||||||
|
b = B()
|
||||||
|
c = C()
|
||||||
|
|
||||||
|
a **= b
|
||||||
|
self.assertEqual(a, 1)
|
||||||
|
|
||||||
|
c **= b
|
||||||
|
self.assertEqual(c, 2)
|
||||||
|
|
||||||
|
def test_no_ipow(self):
|
||||||
|
class B:
|
||||||
|
def __rpow__(self, other):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
a = object()
|
||||||
|
b = B()
|
||||||
|
a **= b
|
||||||
|
self.assertEqual(a, 1)
|
||||||
|
|
||||||
|
def test_ipow_exception_text(self):
|
||||||
|
x = None
|
||||||
|
with self.assertRaises(TypeError) as cm:
|
||||||
|
x **= 2
|
||||||
|
self.assertIn('unsupported operand type(s) for **=', str(cm.exception))
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError) as cm:
|
||||||
|
y = x ** 2
|
||||||
|
self.assertIn('unsupported operand type(s) for **', str(cm.exception))
|
||||||
|
|
||||||
def test_mutable_bases(self):
|
def test_mutable_bases(self):
|
||||||
# Testing mutable bases...
|
# Testing mutable bases...
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
If :func:`object.__ipow__` returns :const:`NotImplemented`, the operator will correctly fall back to :func:`object.__pow__` and :func:`object.__rpow__` as expected.
|
|
@ -882,10 +882,8 @@ static PyObject *
|
||||||
ternary_op(PyObject *v,
|
ternary_op(PyObject *v,
|
||||||
PyObject *w,
|
PyObject *w,
|
||||||
PyObject *z,
|
PyObject *z,
|
||||||
const int op_slot
|
const int op_slot,
|
||||||
#ifndef NDEBUG
|
const char *op_name
|
||||||
, const char *op_name
|
|
||||||
#endif
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
PyNumberMethods *mv = Py_TYPE(v)->tp_as_number;
|
PyNumberMethods *mv = Py_TYPE(v)->tp_as_number;
|
||||||
|
@ -955,16 +953,18 @@ ternary_op(PyObject *v,
|
||||||
if (z == Py_None) {
|
if (z == Py_None) {
|
||||||
PyErr_Format(
|
PyErr_Format(
|
||||||
PyExc_TypeError,
|
PyExc_TypeError,
|
||||||
"unsupported operand type(s) for ** or pow(): "
|
"unsupported operand type(s) for %.100s: "
|
||||||
"'%.100s' and '%.100s'",
|
"'%.100s' and '%.100s'",
|
||||||
|
op_name,
|
||||||
Py_TYPE(v)->tp_name,
|
Py_TYPE(v)->tp_name,
|
||||||
Py_TYPE(w)->tp_name);
|
Py_TYPE(w)->tp_name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyErr_Format(
|
PyErr_Format(
|
||||||
PyExc_TypeError,
|
PyExc_TypeError,
|
||||||
"unsupported operand type(s) for pow(): "
|
"unsupported operand type(s) for %.100s: "
|
||||||
"'%.100s', '%.100s', '%.100s'",
|
"'%.100s', '%.100s', '%.100s'",
|
||||||
|
op_name,
|
||||||
Py_TYPE(v)->tp_name,
|
Py_TYPE(v)->tp_name,
|
||||||
Py_TYPE(w)->tp_name,
|
Py_TYPE(w)->tp_name,
|
||||||
Py_TYPE(z)->tp_name);
|
Py_TYPE(z)->tp_name);
|
||||||
|
@ -972,13 +972,6 @@ ternary_op(PyObject *v,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NDEBUG
|
|
||||||
# define TERNARY_OP(v, w, z, op_slot, op_name) ternary_op(v, w, z, op_slot)
|
|
||||||
#else
|
|
||||||
# define TERNARY_OP(v, w, z, op_slot, op_name) ternary_op(v, w, z, op_slot, op_name)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#define BINARY_FUNC(func, op, op_name) \
|
#define BINARY_FUNC(func, op, op_name) \
|
||||||
PyObject * \
|
PyObject * \
|
||||||
func(PyObject *v, PyObject *w) { \
|
func(PyObject *v, PyObject *w) { \
|
||||||
|
@ -1077,7 +1070,7 @@ PyNumber_Remainder(PyObject *v, PyObject *w)
|
||||||
PyObject *
|
PyObject *
|
||||||
PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
|
PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
|
||||||
{
|
{
|
||||||
return TERNARY_OP(v, w, z, NB_SLOT(nb_power), "** or pow()");
|
return ternary_op(v, w, z, NB_SLOT(nb_power), "** or pow()");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Binary in-place operators */
|
/* Binary in-place operators */
|
||||||
|
@ -1140,6 +1133,24 @@ binary_iop(PyObject *v, PyObject *w, const int iop_slot, const int op_slot,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
ternary_iop(PyObject *v, PyObject *w, PyObject *z, const int iop_slot, const int op_slot,
|
||||||
|
const char *op_name)
|
||||||
|
{
|
||||||
|
PyNumberMethods *mv = Py_TYPE(v)->tp_as_number;
|
||||||
|
if (mv != NULL) {
|
||||||
|
ternaryfunc slot = NB_TERNOP(mv, iop_slot);
|
||||||
|
if (slot) {
|
||||||
|
PyObject *x = (slot)(v, w, z);
|
||||||
|
if (x != Py_NotImplemented) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
Py_DECREF(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ternary_op(v, w, z, op_slot, op_name);
|
||||||
|
}
|
||||||
|
|
||||||
#define INPLACE_BINOP(func, iop, op, op_name) \
|
#define INPLACE_BINOP(func, iop, op, op_name) \
|
||||||
PyObject * \
|
PyObject * \
|
||||||
func(PyObject *v, PyObject *w) { \
|
func(PyObject *v, PyObject *w) { \
|
||||||
|
@ -1237,13 +1248,8 @@ PyNumber_InPlaceRemainder(PyObject *v, PyObject *w)
|
||||||
PyObject *
|
PyObject *
|
||||||
PyNumber_InPlacePower(PyObject *v, PyObject *w, PyObject *z)
|
PyNumber_InPlacePower(PyObject *v, PyObject *w, PyObject *z)
|
||||||
{
|
{
|
||||||
if (Py_TYPE(v)->tp_as_number &&
|
return ternary_iop(v, w, z, NB_SLOT(nb_inplace_power),
|
||||||
Py_TYPE(v)->tp_as_number->nb_inplace_power != NULL) {
|
NB_SLOT(nb_power), "**=");
|
||||||
return TERNARY_OP(v, w, z, NB_SLOT(nb_inplace_power), "**=");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return TERNARY_OP(v, w, z, NB_SLOT(nb_power), "**=");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue