]> granicus.if.org Git - python/commitdiff
Massive changes as per PEP 208. Read it for details.
authorNeil Schemenauer <nascheme@enme.ucalgary.ca>
Thu, 4 Jan 2001 01:39:06 +0000 (01:39 +0000)
committerNeil Schemenauer <nascheme@enme.ucalgary.ca>
Thu, 4 Jan 2001 01:39:06 +0000 (01:39 +0000)
Objects/abstract.c

index dfdfc43f71281edeb8d1a17746a354d58a2d05f7..9050a4ce23fe954aea688f11a7dc3dd652c0c1bb 100644 (file)
@@ -1,7 +1,12 @@
 /* Abstract Object Interface (many thanks to Jim Fulton) */
 
+
 #include "Python.h"
 #include <ctype.h>
+#include "structmember.h" /* we need the offsetof() macro from there */
+
+#define NEW_STYLE_NUMBER(o) PyType_HasFeature((o)->ob_type, \
+                               Py_TPFLAGS_NEWSTYLENUMBER)
 
 /* Shorthands to return certain errors */
 
@@ -281,830 +286,415 @@ PyNumber_Check(PyObject *o)
 
 /* Binary operators */
 
-#define BINOP(v, w, opname, ropname, thisfunc) \
-       if (PyInstance_Check(v) || PyInstance_Check(w)) \
-               return PyInstance_DoBinOp(v, w, opname, ropname, thisfunc)
+/* New style number protocol support */
 
-PyObject *
-PyNumber_Or(PyObject *v, PyObject *w)
-{
-       BINOP(v, w, "__or__", "__ror__", PyNumber_Or);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_or) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-       return type_error("bad operand type(s) for |");
-}
+#define NB_SLOT(x) offsetof(PyNumberMethods, x)
+#define NB_BINOP(nb_methods, slot) \
+               ((binaryfunc*)(& ((char*)nb_methods)[slot] ))
+#define NB_TERNOP(nb_methods, slot) \
+               ((ternaryfunc*)(& ((char*)nb_methods)[slot] ))
 
-PyObject *
-PyNumber_Xor(PyObject *v, PyObject *w)
-{
-       BINOP(v, w, "__xor__", "__rxor__", PyNumber_Xor);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_xor) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-       return type_error("bad operand type(s) for ^");
-}
+/*
+  Calling scheme used for binary operations:
 
-PyObject *
-PyNumber_And(PyObject *v, PyObject *w)
-{
-       BINOP(v, w, "__and__", "__rand__", PyNumber_And);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_and) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-       return type_error("bad operand type(s) for &");
-}
+  v    w       Action
+  -------------------------------------------------------------------
+  new  new     v.op(v,w), w.op(v,w)
+  new  old     v.op(v,w), coerce(v,w), v.op(v,w)
+  old  new     w.op(v,w), coerce(v,w), v.op(v,w)
+  old  old     coerce(v,w), v.op(v,w)
 
-PyObject *
-PyNumber_Lshift(PyObject *v, PyObject *w)
+  Legend:
+  -------
+  * new == new style number
+  * old == old style number
+  * Action indicates the order in which operations are tried until either
+    a valid result is produced or an error occurs.
+
+ */
+
+static PyObject *
+binary_op1(PyObject *v, PyObject *w, const int op_slot)
 {
-       BINOP(v, w, "__lshift__", "__rlshift__", PyNumber_Lshift);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
+       PyObject *x;
+       binaryfunc *slot;
+       if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v)) {
+               slot = NB_BINOP(v->ob_type->tp_as_number, op_slot);
+               if (*slot) {
+                       x = (*slot)(v, w);
+                       if (x != Py_NotImplemented) {
+                               return x;
+                       }
+                       Py_DECREF(x); /* can't do it */
+               }
+               if (v->ob_type == w->ob_type) {
+                       goto binop_error;
+               }
+       }
+       if (w->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(w)) {
+               slot = NB_BINOP(w->ob_type->tp_as_number, op_slot);
+               if (*slot) {
+                       x = (*slot)(v, w);
+                       if (x != Py_NotImplemented) {
+                               return x;
+                       }
+                       Py_DECREF(x); /* can't do it */
+               }
+       }
+       if (!NEW_STYLE_NUMBER(v) || !NEW_STYLE_NUMBER(w)) {
+               int err = PyNumber_CoerceEx(&v, &w);
+               if (err < 0) {
                        return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_lshift) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
+               }
+               if (err == 0) {
+                       PyNumberMethods *mv = v->ob_type->tp_as_number;
+                       if (mv) {
+                               slot = NB_BINOP(mv, op_slot);
+                               if (*slot) {
+                                       PyObject *x = (*slot)(v, w);
+                                       Py_DECREF(v);
+                                       Py_DECREF(w);
+                                       return x;
+                               }
+                       }
+                       /* CoerceEx incremented the reference counts */
+                       Py_DECREF(v);
+                       Py_DECREF(w);
+               }
        }
-       return type_error("bad operand type(s) for <<");
+binop_error:
+       Py_INCREF(Py_NotImplemented);
+       return Py_NotImplemented;
 }
-
-PyObject *
-PyNumber_Rshift(PyObject *v, PyObject *w)
+           
+static PyObject *
+binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name)
 {
-       BINOP(v, w, "__rshift__", "__rrshift__", PyNumber_Rshift);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_rshift) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
+       PyObject *result = binary_op1(v, w, op_slot);
+       if (result == Py_NotImplemented) {
+               Py_DECREF(Py_NotImplemented);
+               PyErr_Format(PyExc_TypeError, 
+                               "unsupported operand type(s) for %s", op_name);
+               return NULL;
        }
-       return type_error("bad operand type(s) for >>");
+       return result;
 }
 
-PyObject *
-PyNumber_Add(PyObject *v, PyObject *w)
-{
-       PySequenceMethods *m;
 
-       BINOP(v, w, "__add__", "__radd__", PyNumber_Add);
-       m = v->ob_type->tp_as_sequence;
-       if (m && m->sq_concat)
-               return (*m->sq_concat)(v, w);
-       else if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_add) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-       return type_error("bad operand type(s) for +");
-}
+/*
+  Calling scheme used for ternary operations:
 
-PyObject *
-PyNumber_Subtract(PyObject *v, PyObject *w)
-{
-       BINOP(v, w, "__sub__", "__rsub__", PyNumber_Subtract);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_subtract) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-       return type_error("bad operand type(s) for -");
-}
+  v    w       z       Action
+  -------------------------------------------------------------------
+  new  new     new     v.op(v,w,z), w.op(v,w,z), z.op(v,w,z)
+  new  old     new     v.op(v,w,z), z.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
+  old  new     new     w.op(v,w,z), z.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
+  old  old     new     z.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
+  new  new     old     v.op(v,w,z), w.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
+  new  old     old     v.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
+  old  new     old     w.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
+  old  old     old     coerce(v,w,z), v.op(v,w,z)
 
-PyObject *
-PyNumber_Multiply(PyObject *v, PyObject *w)
-{
-       PyTypeObject *tp = v->ob_type;
-       PySequenceMethods *m;
+  Legend:
+  -------
+  * new == new style number
+  * old == old style number
+  * Action indicates the order in which operations are tried until either
+    a valid result is produced or an error occurs.
+  * coerce(v,w,z) actually does: coerce(v,w), coerce(v,z), coerce(w,z) and
+    only if z != Py_None; if z == Py_None, then it is treated as absent
+    variable and only coerce(v,w) is tried.
 
-       BINOP(v, w, "__mul__", "__rmul__", PyNumber_Multiply);
-       if (tp->tp_as_number != NULL &&
-           w->ob_type->tp_as_sequence != NULL) {
-               /* number*sequence -- swap v and w */
-               PyObject *tmp = v;
-               v = w;
-               w = tmp;
-               tp = v->ob_type;
-       }
-       if (tp->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_multiply) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-       m = tp->tp_as_sequence;
-       if (m && m->sq_repeat) {
-               long mul_value;
+ */
 
-               if (PyInt_Check(w)) {
-                       mul_value = PyInt_AsLong(w);
+static PyObject *
+ternary_op(PyObject *v,
+          PyObject *w,
+          PyObject *z,
+          const int op_slot,
+          const char *op_name)
+{
+       PyNumberMethods *mv, *mw, *mz;
+       register PyObject *x = NULL;
+       register ternaryfunc *slot;
+       
+       mv = v->ob_type->tp_as_number;
+       if (mv != NULL && NEW_STYLE_NUMBER(v)) {
+               /* try v.op(v,w,z) */
+               slot = NB_TERNOP(mv, op_slot);
+               if (*slot) {
+                       x = (*slot)(v, w, z);
+                       if (x != Py_NotImplemented)
+                               return x;
+                       /* Can't do it... fall through */
+                       Py_DECREF(x);
                }
-               else if (PyLong_Check(w)) {
-                       mul_value = PyLong_AsLong(w);
-                       if (mul_value == -1 && PyErr_Occurred())
-                                return NULL; 
+               if (v->ob_type == w->ob_type &&
+                               (z == Py_None || z->ob_type == v->ob_type)) {
+                       goto ternary_error;
                }
-               else {
-                       return type_error(
-                               "can't multiply sequence with non-int");
+       }
+       mw = w->ob_type->tp_as_number;
+       if (mw != NULL && NEW_STYLE_NUMBER(w)) {
+               /* try w.op(v,w,z) */
+               slot = NB_TERNOP(mw,op_slot);
+               if (*slot) {
+                       x = (*slot)(v, w, z);
+                       if (x != Py_NotImplemented)
+                               return x;
+                       /* Can't do it... fall through */
+                       Py_DECREF(x);
+               }
+               if (NEW_STYLE_NUMBER(v) &&
+                               (z == Py_None || z->ob_type == v->ob_type)) {
+                       goto ternary_error;
                }
-               return (*m->sq_repeat)(v, (int)mul_value);
        }
-       return type_error("bad operand type(s) for *");
-}
-
-PyObject *
-PyNumber_Divide(PyObject *v, PyObject *w)
-{
-       BINOP(v, w, "__div__", "__rdiv__", PyNumber_Divide);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_divide) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
+       mz = z->ob_type->tp_as_number;
+       if (mz != NULL && NEW_STYLE_NUMBER(z)) {
+               /* try: z.op(v,w,z) */
+               slot = NB_TERNOP(mz, op_slot);
+               if (*slot) {
+                       x = (*slot)(v, w, z);
+                       if (x != Py_NotImplemented)
+                               return x;
+                       /* Can't do it... fall through */
+                       Py_DECREF(x);
+               }
        }
-       return type_error("bad operand type(s) for /");
-}
 
-PyObject *
-PyNumber_Remainder(PyObject *v, PyObject *w)
-{
-       if (PyString_Check(v))
-               return PyString_Format(v, w);
-       else if (PyUnicode_Check(v))
-               return PyUnicode_Format(v, w);
-       BINOP(v, w, "__mod__", "__rmod__", PyNumber_Remainder);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_remainder) != NULL)
-                       x = (*f)(v, w);
+       if (!NEW_STYLE_NUMBER(v) || !NEW_STYLE_NUMBER(w) ||
+                       (z != Py_None && !NEW_STYLE_NUMBER(z))) {
+               /* we have an old style operand, coerce */
+               PyObject *v1, *z1, *w2, *z2;
+               int c;
+               
+               c = PyNumber_Coerce(&v, &w);
+               if (c != 0)
+                       goto error3;
+
+               /* Special case: if the third argument is None, it is
+                  treated as absent argument and not coerced. */
+               if (z == Py_None) {
+                       if (v->ob_type->tp_as_number) {
+                               slot = NB_TERNOP(v->ob_type->tp_as_number,
+                                                op_slot);
+                               if (*slot)
+                                       x = (*slot)(v, w, z);
+                               else
+                                       c = -1;
+                       }
+                       else
+                               c = -1;
+                       goto error2;
+               }
+               v1 = v;
+               z1 = z;
+               c = PyNumber_Coerce(&v1, &z1);
+               if (c != 0)
+                       goto error2;
+               w2 = w;
+               z2 = z1;
+               c = PyNumber_Coerce(&w2, &z2);
+               if (c != 0)
+                       goto error1;
+
+               if (v1->ob_type->tp_as_number != NULL) {
+                       slot = NB_TERNOP(v1->ob_type->tp_as_number,
+                                        op_slot);
+                       if (*slot)
+                               x = (*slot)(v1, w2, z2);
+                       else
+                               c = -1;
+               }
+               else
+                       c = -1;
+
+               Py_DECREF(w2);
+               Py_DECREF(z2);
+       error1:
+               Py_DECREF(v1);
+               Py_DECREF(z1);
+       error2:
                Py_DECREF(v);
                Py_DECREF(w);
-               if (f != NULL)
+       error3:
+               if (c >= 0)
                        return x;
        }
-       return type_error("bad operand type(s) for %");
+       
+ternary_error:
+       PyErr_Format(PyExc_TypeError, "unsupported operand type(s) for %s",
+                       op_name);
+       return NULL;
 }
 
+#define BINARY_FUNC(func, op, op_name) \
+    PyObject * \
+    func(PyObject *v, PyObject *w) { \
+           return binary_op(v, w, NB_SLOT(op), op_name); \
+    }
+
+BINARY_FUNC(PyNumber_Or, nb_or, "|")
+BINARY_FUNC(PyNumber_Xor, nb_xor, "^")
+BINARY_FUNC(PyNumber_And, nb_and, "&")
+BINARY_FUNC(PyNumber_Lshift, nb_lshift, "<<")
+BINARY_FUNC(PyNumber_Rshift, nb_rshift, ">>")
+BINARY_FUNC(PyNumber_Subtract, nb_subtract, "-")
+BINARY_FUNC(PyNumber_Multiply, nb_multiply, "*")
+BINARY_FUNC(PyNumber_Divide, nb_divide, "/")
+BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()")
+
 PyObject *
-PyNumber_Divmod(PyObject *v, PyObject *w)
+PyNumber_Add(PyObject *v, PyObject *w)
 {
-       BINOP(v, w, "__divmod__", "__rdivmod__", PyNumber_Divmod);
-       if (v->ob_type->tp_as_number != NULL) {
-               PyObject *x = NULL;
-               PyObject * (*f)(PyObject *, PyObject *) = NULL;
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_divmod) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
+       PyObject *result = binary_op1(v, w, NB_SLOT(nb_add));
+       if (result == Py_NotImplemented) {
+               PySequenceMethods *m = v->ob_type->tp_as_sequence;
+               Py_DECREF(Py_NotImplemented);
+               if (m && m->sq_concat) {
+                       result = (*m->sq_concat)(v, w);
+               }
+                else {
+                    PyErr_SetString(PyExc_TypeError,
+                                    "unsupported operand types for +");
+                    result = NULL;
+                }
        }
-       return type_error("bad operand type(s) for divmod()");
+       return result;
 }
 
-/* Power (binary or ternary) */
-
-static PyObject *
-do_pow(PyObject *v, PyObject *w)
+PyObject *
+PyNumber_Remainder(PyObject *v, PyObject *w)
 {
-       PyObject *res;
-       PyObject * (*f)(PyObject *, PyObject *, PyObject *);
-       BINOP(v, w, "__pow__", "__rpow__", do_pow);
-       if (v->ob_type->tp_as_number == NULL ||
-           w->ob_type->tp_as_number == NULL) {
-               PyErr_SetString(PyExc_TypeError,
-                               "pow(x, y) requires numeric arguments");
-               return NULL;
-       }
-       if (PyNumber_Coerce(&v, &w) != 0)
-               return NULL;
-       if (v->ob_type->tp_as_number != NULL &&
-           (f = v->ob_type->tp_as_number->nb_power) != NULL)
-               res = (*f)(v, w, Py_None);
-       else
-               res = type_error("pow(x, y) not defined for these operands");
-       Py_DECREF(v);
-       Py_DECREF(w);
-       return res;
+       if (PyString_Check(v))
+               return PyString_Format(v, w);
+       else if (PyUnicode_Check(v))
+               return PyUnicode_Format(v, w);
+       return binary_op(v, w, NB_SLOT(nb_remainder), "%");
 }
 
 PyObject *
 PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
 {
-       PyObject *res;
-       PyObject *v1, *z1, *w2, *z2;
-       PyObject * (*f)(PyObject *, PyObject *, PyObject *);
-
-       if (z == Py_None)
-               return do_pow(v, w);
-       /* XXX The ternary version doesn't do class instance coercions */
-       if (PyInstance_Check(v))
-               return v->ob_type->tp_as_number->nb_power(v, w, z);
-       if (v->ob_type->tp_as_number == NULL ||
-           z->ob_type->tp_as_number == NULL ||
-           w->ob_type->tp_as_number == NULL) {
-               return type_error("pow(x, y, z) requires numeric arguments");
-       }
-       if (PyNumber_Coerce(&v, &w) != 0)
-               return NULL;
-       res = NULL;
-       v1 = v;
-       z1 = z;
-       if (PyNumber_Coerce(&v1, &z1) != 0)
-               goto error2;
-       w2 = w;
-       z2 = z1;
-       if (PyNumber_Coerce(&w2, &z2) != 0)
-               goto error1;
-       if (v->ob_type->tp_as_number != NULL &&
-           (f = v1->ob_type->tp_as_number->nb_power) != NULL)
-               res = (*f)(v1, w2, z2);
-       else
-               res = type_error(
-                       "pow(x, y, z) not defined for these operands");
-       Py_DECREF(w2);
-       Py_DECREF(z2);
-  error1:
-       Py_DECREF(v1);
-       Py_DECREF(z1);
-  error2:
-       Py_DECREF(v);
-       Py_DECREF(w);
-       return res;
+       return ternary_op(v, w, z, NB_SLOT(nb_power), "** or pow()");
 }
 
 /* Binary in-place operators */
 
 /* The in-place operators are defined to fall back to the 'normal',
-   non in-place operations, if the in-place methods are not in place, and to
-   take class instances into account. This is how it is supposed to work:
-
-   - If the left-hand-side object (the first argument) is an
-     instance object, try to let PyInstance_HalfBinOp() handle it.  Pass the
-     non in-place variant of the function as callback, because it will only
-     be used if the left-hand object is changed by coercion.
-
-   - Otherwise, if the left hand object is not an instance object, it has
-     the appropriate struct members, and they are filled, call the
-     appropriate function and return the result. No coercion is done on the
-     arguments; the left-hand object is the one the operation is performed
-     on, and it's up to the function to deal with the right-hand object.
-     
-   - Otherwise, in-place modification is not supported. Handle it exactly as
-     a non in-place operation of the same kind:
+   non in-place operations, if the in-place methods are not in place.
 
-     - If either object is an instance, let PyInstance_DoBinOp() handle it.
+   - If the left hand object has the appropriate struct members, and
+     they are filled, call the appropriate function and return the
+     result.  No coercion is done on the arguments; the left-hand object
+     is the one the operation is performed on, and it's up to the
+     function to deal with the right-hand object.
      
-     - Otherwise, both arguments are C types. If the left-hand object has
-       the appropriate struct members filled, coerce, call the
-       appropriate function, and return the result.
-  
-     - Otherwise, we are out of options: raise a type error specific to
-       augmented assignment.
+   - Otherwise, in-place modification is not supported. Handle it exactly as
+     a non in-place operation of the same kind.
 
    */
 
 #define HASINPLACE(t) PyType_HasFeature((t)->ob_type, Py_TPFLAGS_HAVE_INPLACEOPS)
 
-PyObject *
-PyNumber_InPlaceOr(PyObject *v, PyObject *w)
-{
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__ior__", &x,
-                                        PyNumber_Or, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_or) != NULL)
-               return (*f)(v, w);
-
-       BINOP(v, w, "__or__", "__ror__", PyNumber_Or);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_or) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-
-       return type_error("bad operand type(s) for |=");
-}
-
-PyObject *
-PyNumber_InPlaceXor(PyObject *v, PyObject *w)
-{
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__ixor__", &x,
-                                        PyNumber_Xor, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_xor) != NULL)
-               return (*f)(v, w);
-
-       BINOP(v, w, "__xor__", "__rxor__", PyNumber_Xor);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_xor) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-
-       return type_error("bad operand type(s) for ^=");
-}
-
-PyObject *
-PyNumber_InPlaceAnd(PyObject *v, PyObject *w)
-{
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__iand__", &x,
-                                        PyNumber_And, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_and) != NULL)
-               return (*f)(v, w);
-
-       BINOP(v, w, "__and__", "__rand__", PyNumber_And);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_and) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-
-       return type_error("bad operand type(s) for &=");
-}
-
-PyObject *
-PyNumber_InPlaceLshift(PyObject *v, PyObject *w)
-{
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__ilshift__", &x,
-                                       PyNumber_Lshift, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_lshift) != NULL)
-               return (*f)(v, w);
-
-       BINOP(v, w, "__lshift__", "__rlshift__", PyNumber_Lshift);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_lshift) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
+static PyObject *
+binary_iop(PyObject *v, PyObject *w, const int iop_slot, const int op_slot,
+               const char *op_name)
+{
+       PyNumberMethods *mv = v->ob_type->tp_as_number;
+       if (mv != NULL && HASINPLACE(v)) {
+               binaryfunc *slot = NB_BINOP(mv, iop_slot);
+               if (*slot) {
+                       PyObject *x = (*slot)(v, w);
+                       if (x != Py_NotImplemented) {
+                               return x;
+                       }
+                       Py_DECREF(x);
+               }
        }
-
-       return type_error("bad operand type(s) for <<=");
+       return binary_op(v, w, op_slot, op_name);
 }
 
-PyObject *
-PyNumber_InPlaceRshift(PyObject *v, PyObject *w)
-{
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__irshift__", &x,
-                                       PyNumber_Rshift, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_rshift) != NULL)
-               return (*f)(v, w);
-
-       BINOP(v, w, "__rshift__", "__rrshift__", PyNumber_Rshift);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_rshift) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
+#define INPLACE_BINOP(func, iop, op, op_name) \
+       PyObject * \
+       func(PyObject *v, PyObject *w) { \
+               return binary_iop(v, w, NB_SLOT(iop), NB_SLOT(op), op_name); \
        }
 
-       return type_error("bad operand type(s) for >>=");
-}
+INPLACE_BINOP(PyNumber_InPlaceOr, nb_inplace_or, nb_or, "|=")
+INPLACE_BINOP(PyNumber_InPlaceXor, nb_inplace_xor, nb_xor, "^=")
+INPLACE_BINOP(PyNumber_InPlaceAnd, nb_inplace_and, nb_and, "&=")
+INPLACE_BINOP(PyNumber_InPlaceLshift, nb_inplace_lshift, nb_lshift, "<<=")
+INPLACE_BINOP(PyNumber_InPlaceRshift, nb_inplace_rshift, nb_rshift, ">>=")
+INPLACE_BINOP(PyNumber_InPlaceSubtract, nb_inplace_subtract, nb_subtract, "-=")
+INPLACE_BINOP(PyNumber_InPlaceDivide, nb_inplace_divide, nb_divide, "/=")
 
 PyObject *
 PyNumber_InPlaceAdd(PyObject *v, PyObject *w)
 {
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__iadd__", &x,
-                                        PyNumber_Add, 0) <= 0)
-                       return x;
-       }
-       else if (HASINPLACE(v)) {
-               if (v->ob_type->tp_as_sequence != NULL)
-                       f = v->ob_type->tp_as_sequence->sq_inplace_concat;
-               if (f == NULL && v->ob_type->tp_as_number != NULL)
-                       f = v->ob_type->tp_as_number->nb_inplace_add;
-               if (f != NULL)
-                       return (*f)(v, w);
-       }
-
-       BINOP(v, w, "__add__", "__radd__", PyNumber_Add);
+       binaryfunc f = NULL;
 
        if (v->ob_type->tp_as_sequence != NULL) {
-               f = v->ob_type->tp_as_sequence->sq_concat;
+               if (HASINPLACE(v))
+                       f = v->ob_type->tp_as_sequence->sq_inplace_concat;
+               if (f == NULL)
+                       f = v->ob_type->tp_as_sequence->sq_concat;
                if (f != NULL)
                        return (*f)(v, w);
        }
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL) {
-                       f = v->ob_type->tp_as_number->nb_add;
-                       if (f != NULL)
-                               x = (*f)(v, w);
-               }
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-
-       return type_error("bad operand type(s) for +=");
-}
-
-PyObject *
-PyNumber_InPlaceSubtract(PyObject *v, PyObject *w)
-{
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__isub__", &x,
-                                       PyNumber_Subtract, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_subtract) != NULL)
-               return (*f)(v, w);
-
-       BINOP(v, w, "__sub__", "__rsub__", PyNumber_Subtract);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_subtract) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-
-       return type_error("bad operand type(s) for -=");
+       return binary_iop(v, w, NB_SLOT(nb_inplace_add), NB_SLOT(nb_add), "+=");
 }
 
 PyObject *
 PyNumber_InPlaceMultiply(PyObject *v, PyObject *w)
 {
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
        PyObject * (*g)(PyObject *, int) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__imul__", &x,
-                                       PyNumber_Multiply, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_multiply) != NULL)
-               return (*f)(v, w);
-       else if (v->ob_type->tp_as_sequence != NULL && HASINPLACE(v) &&
-                (g = v->ob_type->tp_as_sequence->sq_inplace_repeat) != NULL) {
-               long mul_value;
-
+       if (HASINPLACE(v) && v->ob_type->tp_as_sequence &&
+               (g = v->ob_type->tp_as_sequence->sq_inplace_repeat)) {
+               long n;
                if (PyInt_Check(w)) {
-                       mul_value = PyInt_AsLong(w);
+                        = PyInt_AsLong(w);
                }
                else if (PyLong_Check(w)) {
-                       mul_value = PyLong_AsLong(w);
-                       if (mul_value == -1 && PyErr_Occurred())
-                                return NULL; 
-               }
-               else {
-                       return type_error(
-                               "can't multiply sequence with non-int");
-               }
-               return (*g)(v, (int)mul_value);
-       }
-
-       BINOP(v, w, "__mul__", "__rmul__", PyNumber_Multiply);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_multiply) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_sequence != NULL &&
-                (g = v->ob_type->tp_as_sequence->sq_repeat) != NULL) {
-               long mul_value;
-
-               if (PyInt_Check(w)) {
-                       mul_value = PyInt_AsLong(w);
-               }
-               else if (PyLong_Check(w)) {
-                       mul_value = PyLong_AsLong(w);
-                       if (mul_value == -1 && PyErr_Occurred())
-                                return NULL; 
+                       n = PyLong_AsLong(w);
+                       if (n == -1 && PyErr_Occurred())
+                               return NULL;
                }
                else {
-                       return type_error(
-                               "can't multiply sequence with non-int");
+                       return type_error("can't multiply sequence to non-int");
                }
-               return (*g)(v, (int)mul_value);
+               return (*g)(v, (int)n);
        }
-       return type_error("bad operand type(s) for *=");
+       return binary_iop(v, w, NB_SLOT(nb_inplace_multiply),
+                               NB_SLOT(nb_multiply), "*=");
 }
 
-PyObject *
-PyNumber_InPlaceDivide(PyObject *v, PyObject *w)
-{
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__idiv__", &x,
-                                       PyNumber_Divide, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_divide) != NULL)
-               return (*f)(v, w);
 
-       BINOP(v, w, "__div__", "__rdiv__", PyNumber_Divide);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if (v->ob_type->tp_as_number != NULL &&
-                   (f = v->ob_type->tp_as_number->nb_divide) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-
-       return type_error("bad operand type(s) for /=");
-}
 
 PyObject *
 PyNumber_InPlaceRemainder(PyObject *v, PyObject *w)
 {
-       PyObject * (*f)(PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__imod__", &x,
-                                       PyNumber_Remainder, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_remainder) != NULL)
-               return (*f)(v, w);
-
        if (PyString_Check(v))
                return PyString_Format(v, w);
        else if (PyUnicode_Check(v))
                return PyUnicode_Format(v, w);
-
-       BINOP(v, w, "__mod__", "__rmod__", PyNumber_Remainder);
-
-       if (v->ob_type->tp_as_number != NULL) {
-               if (PyNumber_Coerce(&v, &w) != 0)
-                       return NULL;
-               if ((f = v->ob_type->tp_as_number->nb_remainder) != NULL)
-                       x = (*f)(v, w);
-               Py_DECREF(v);
-               Py_DECREF(w);
-               if (f != NULL)
-                       return x;
-       }
-
-       return type_error("bad operand type(s) for %=");
-}
-
-
-/* In-place Power (binary or ternary, for API consistency) */
-
-static PyObject *
-do_inplace_pow(PyObject *v, PyObject *w)
-{
-       PyObject * (*f)(PyObject *, PyObject *, PyObject *) = NULL;
-       PyObject *x = NULL;
-
-       if (PyInstance_Check(v)) {
-               if (PyInstance_HalfBinOp(v, w, "__ipow__", &x, do_pow, 0) <= 0)
-                       return x;
-       }
-       else if (v->ob_type->tp_as_number != NULL && HASINPLACE(v) &&
-                (f = v->ob_type->tp_as_number->nb_inplace_power) != NULL)
-               return (*f)(v, w, Py_None);
-
-       BINOP(v, w, "__pow__", "__rpow__", do_pow);
-
-       if (v->ob_type->tp_as_number == NULL ||
-           w->ob_type->tp_as_number == NULL) {
-               return type_error("bad operand type(s) for **=");
-       }
-       if (PyNumber_Coerce(&v, &w) != 0)
-               return NULL;
-       if ((f = v->ob_type->tp_as_number->nb_power) != NULL)
-               x = (*f)(v, w, Py_None);
        else
-               x = type_error("bad operand type(s) for **=");
-       Py_DECREF(v);
-       Py_DECREF(w);
-       return x;
+               return binary_iop(v, w, NB_SLOT(nb_inplace_remainder),
+                                       NB_SLOT(nb_remainder), "%=");
 }
 
+
 PyObject *
 PyNumber_InPlacePower(PyObject *v, PyObject *w, PyObject *z)
 {
-       PyObject *res;
-       PyObject *v1, *z1, *w2, *z2, *oldv;
-       PyObject * (*f)(PyObject *, PyObject *, PyObject *);
-
-       if (z == Py_None)
-               return do_inplace_pow(v, w);
-       /* XXX The ternary version doesn't do class instance coercions */
-       if (PyInstance_Check(v))
-               return v->ob_type->tp_as_number->nb_inplace_power(v, w, z);
-       if (v->ob_type->tp_as_number == NULL ||
-           z->ob_type->tp_as_number == NULL ||
-           w->ob_type->tp_as_number == NULL) {
-               return type_error(
-                       "(inplace) pow(x, y, z) requires numeric arguments");
-       }
-       oldv = v;
-       Py_INCREF(oldv);
-       res = NULL;
-       if (PyNumber_Coerce(&v, &w) != 0)
-               goto error3;
-       v1 = v;
-       z1 = z;
-       if (PyNumber_Coerce(&v1, &z1) != 0)
-               goto error2;
-       w2 = w;
-       z2 = z1;
-       if (PyNumber_Coerce(&w2, &z2) != 0)
-               goto error1;
-       if (oldv == v1 && HASINPLACE(v1) &&
-           v->ob_type->tp_as_number != NULL &&
-           (f = v1->ob_type->tp_as_number->nb_inplace_power) != NULL)
-               res = (*f)(v1, w2, z2);
-       else if (v1->ob_type->tp_as_number != NULL &&
-                (f = v1->ob_type->tp_as_number->nb_power) != NULL)
-               res = (*f)(v1, w2, z2);
-       else
-               res = type_error(
-                    "(inplace) pow(x, y, z) not defined for these operands");
-       Py_DECREF(w2);
-       Py_DECREF(z2);
-  error1:
-       Py_DECREF(v1);
-       Py_DECREF(z1);
-  error2:
-       Py_DECREF(v);
-       Py_DECREF(w);
-  error3:
-       Py_DECREF(oldv);
-       return res;
+       if (HASINPLACE(v) && v->ob_type->tp_as_number &&
+           v->ob_type->tp_as_number->nb_inplace_power != NULL) {
+               return ternary_op(v, w, z, NB_SLOT(nb_inplace_power), "**=");
+       }
+       else {
+               return ternary_op(v, w, z, NB_SLOT(nb_power), "**=");
+       }
 }