]> granicus.if.org Git - python/commitdiff
Support for the in-place operations introduced by augmented assignment. Only
authorThomas Wouters <thomas@python.org>
Thu, 24 Aug 2000 20:08:19 +0000 (20:08 +0000)
committerThomas Wouters <thomas@python.org>
Thu, 24 Aug 2000 20:08:19 +0000 (20:08 +0000)
the list object supports this currently, but other candidates are
gladly accepted (like arraymodule and such.)

Objects/abstract.c
Objects/classobject.c
Objects/listobject.c

index 48604f3fdfa4dfb4cf766a7292b09ba2281ce15e..03049152aac72e51ebdf92a8db8973f4278b01dc 100644 (file)
@@ -614,6 +614,486 @@ PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
        return res;
 }
 
+/* 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:
+
+     - If either object is an instance, let PyInstance_DoBinOp() handle it.
+     
+     - 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.
+
+   */
+
+#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;
+
+       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;
+
+       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;
+
+       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;
+
+       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;
+       }
+
+       return type_error("bad operand type(s) for <<=");
+}
+
+PyObject *
+PyNumber_InPlaceRshift(PyObject *v, PyObject *w)
+{
+       PyObject * (*f)(PyObject *, PyObject *) = NULL;
+       PyObject *x;
+
+       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;
+       }
+
+       return type_error("bad operand type(s) for >>=");
+}
+
+PyObject *
+PyNumber_InPlaceAdd(PyObject *v, PyObject *w)
+{
+       PyObject * (*f)(PyObject *, PyObject *) = NULL;
+       PyObject *x;
+
+       if (PyInstance_Check(v)) {
+               if (PyInstance_HalfBinOp(v, w, "__iadd__", &x,
+                                       PyNumber_Add, 0) <= 0)
+                       return x;
+       } else if (HASINPLACE(v) && (v->ob_type->tp_as_sequence != NULL &&
+                 (f = v->ob_type->tp_as_sequence->sq_inplace_concat) != NULL) ||
+                 (v->ob_type->tp_as_number != NULL &&
+                 (f = v->ob_type->tp_as_number->nb_inplace_add) != NULL))
+               return (*f)(v, w);
+
+       BINOP(v, w, "__add__", "__radd__", PyNumber_Add);
+
+       if (v->ob_type->tp_as_sequence != NULL &&
+           (f = v->ob_type->tp_as_sequence->sq_concat) != NULL)
+               return (*f)(v, w);
+       else 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) != 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;
+
+       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 -=");
+}
+
+PyObject *
+PyNumber_InPlaceMultiply(PyObject *v, PyObject *w)
+{
+       PyObject * (*f)(PyObject *, PyObject *) = NULL;
+       PyObject * (*f2)(PyObject *, int) = NULL;
+       PyObject *x;
+
+       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) &&
+                  (f2 = v->ob_type->tp_as_sequence->sq_inplace_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; 
+               }
+               else {
+                       return type_error(
+                               "can't multiply sequence with non-int");
+               }
+               return (*f2)(v, (int)mul_value);
+       }
+       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 (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 &&
+                  (f2 = 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; 
+               }
+               else {
+                       return type_error(
+                               "can't multiply sequence with non-int");
+               }
+               return (*f2)(v, (int)mul_value);
+       }
+       return type_error("bad operand type(s) for *=");
+}
+
+PyObject *
+PyNumber_InPlaceDivide(PyObject *v, PyObject *w)
+{
+       PyObject * (*f)(PyObject *, PyObject *) = NULL;
+       PyObject *x;
+
+       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;
+
+       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;
+
+       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;
+}
+
+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;
+}
+
+
 /* Unary operators and functions */
 
 PyObject *
@@ -856,6 +1336,40 @@ PySequence_Repeat(PyObject *o, int count)
        return type_error("object can't be repeated");
 }
 
+PyObject *
+PySequence_InPlaceConcat(PyObject *s, PyObject *o)
+{
+       PySequenceMethods *m;
+
+       if (s == NULL || o == NULL)
+               return null_error();
+
+       m = s->ob_type->tp_as_sequence;
+       if (m && HASINPLACE(s) && m->sq_inplace_concat)
+               return m->sq_inplace_concat(s, o);
+       if (m && m->sq_concat)
+               return m->sq_concat(s, o);
+
+       return type_error("object can't be concatenated");
+}
+
+PyObject *
+PySequence_InPlaceRepeat(PyObject *o, int count)
+{
+       PySequenceMethods *m;
+
+       if (o == NULL)
+               return null_error();
+
+       m = o->ob_type->tp_as_sequence;
+       if (m && HASINPLACE(o) && m->sq_inplace_repeat)
+               return m->sq_inplace_repeat(o, count);
+       if (m && m->sq_repeat)
+               return m->sq_repeat(o, count);
+
+       return type_error("object can't be repeated");
+}
+
 PyObject *
 PySequence_GetItem(PyObject *s, int i)
 {
index f0f4438e87103b35d14175b3970f38820f59fb3c..f1fd31f289b9aed53eef84e65c395c9d4ad03167 100644 (file)
@@ -1193,12 +1193,6 @@ generic_unary_op(PyInstanceObject *self, PyObject *methodname)
 }
 
 
-/* Forward */
-static int
-halfbinop(PyObject *, PyObject *, char *, PyObject **,
-          PyObject * (*)(PyObject *, PyObject *), int);
-
-
 /* Implement a binary operator involving at least one class instance. */
 
 PyObject *
@@ -1208,9 +1202,9 @@ PyInstance_DoBinOp(PyObject *v, PyObject *w, char *opname, char *ropname,
        char buf[256];
        PyObject *result = NULL;
 
-       if (halfbinop(v, w, opname, &result, thisfunc, 0) <= 0)
+       if (PyInstance_HalfBinOp(v, w, opname, &result, thisfunc, 0) <= 0)
                return result;
-       if (halfbinop(w, v, ropname, &result, thisfunc, 1) <= 0)
+       if (PyInstance_HalfBinOp(w, v, ropname, &result, thisfunc, 1) <= 0)
                return result;
        /* Sigh -- special case for comparisons */
        if (strcmp(opname, "__cmp__") == 0) {
@@ -1234,9 +1228,9 @@ PyInstance_DoBinOp(PyObject *v, PyObject *w, char *opname, char *ropname,
 
 static PyObject *coerce_obj;
 
-static int
-halfbinop(PyObject *v, PyObject *w, char *opname, PyObject **r_result,
-          PyObject * (*thisfunc)(PyObject *, PyObject *), int swapped)
+int
+PyInstance_HalfBinOp(PyObject *v, PyObject *w, char *opname, PyObject **r_result,
+                    PyObject * (*thisfunc)(PyObject *, PyObject *), int swapped)
 {
        PyObject *func;
        PyObject *args;
@@ -1451,6 +1445,35 @@ instance_pow(PyObject *v, PyObject *w, PyObject *z)
        return result;
 }
 
+static PyObject *
+instance_inplace_pow(PyObject *v, PyObject *w, PyObject *z)
+{
+       /* XXX Doesn't do coercions... */
+       PyObject *func;
+       PyObject *args;
+       PyObject *result;
+       static PyObject *ipowstr;
+
+       if (ipowstr == NULL)
+               ipowstr = PyString_InternFromString("__ipow__");
+       func = PyObject_GetAttr(v, ipowstr);
+       if (func == NULL) {
+               if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+                       return NULL;
+               return instance_pow(v, w, z);
+       }
+       args = Py_BuildValue("(OO)", w, z);
+       if (args == NULL) {
+               Py_DECREF(func);
+               return NULL;
+       }
+       result = PyEval_CallObject(func, args);
+       Py_DECREF(func);
+       Py_DECREF(args);
+       return result;
+}
+
+
 static PyNumberMethods instance_as_number = {
        0, /*nb_add*/
        0, /*nb_subtract*/
@@ -1475,6 +1498,17 @@ static PyNumberMethods instance_as_number = {
        (unaryfunc)instance_float, /*nb_float*/
        (unaryfunc)instance_oct, /*nb_oct*/
        (unaryfunc)instance_hex, /*nb_hex*/
+       0, /*nb_inplace_add*/
+       0, /*nb_inplace_subtract*/
+       0, /*nb_inplace_multiply*/
+       0, /*nb_inplace_divide*/
+       0, /*nb_inplace_remainder*/
+       (ternaryfunc)instance_inplace_pow, /*nb_inplace_power*/
+       0, /*nb_inplace_lshift*/
+       0, /*nb_inplace_rshift*/
+       0, /*nb_inplace_and*/
+       0, /*nb_inplace_xor*/
+       0, /*nb_inplace_or*/
 };
 
 PyTypeObject PyInstance_Type = {
index 721a4f2ded0fa03a77e5161191536d88b7307489..5a704fedf3f49e4374ef742ea1de47630229a255 100644 (file)
@@ -496,6 +496,50 @@ PyList_SetSlice(PyObject *a, int ilow, int ihigh, PyObject *v)
        return list_ass_slice((PyListObject *)a, ilow, ihigh, v);
 }
 
+static PyObject *
+list_inplace_repeat(PyListObject *self, int n)
+{
+       PyObject **items;
+       int size, i, j;
+
+
+       size = PyList_GET_SIZE(self);
+       if (size == 0) {
+               Py_INCREF(self);
+               return (PyObject *)self;
+       }
+
+       items = self->ob_item;
+
+       if (n < 1) {
+               self->ob_item = NULL;
+               self->ob_size = 0;
+               for (i = 0; i < size; i++)
+                       Py_XDECREF(items[i]);
+               PyMem_DEL(items);
+               Py_INCREF(self);
+               return (PyObject *)self;
+       }
+
+       NRESIZE(items, PyObject*, size*n);
+       if (items == NULL) {
+               PyErr_NoMemory();
+               goto finally;
+       }
+       self->ob_item = items;
+       for (i = 1; i < n; i++) { /* Start counting at 1, not 0 */
+               for (j = 0; j < size; j++) {
+                       PyObject *o = PyList_GET_ITEM(self, j);
+                       Py_INCREF(o);
+                       PyList_SET_ITEM(self, self->ob_size++, o);
+               }
+       }
+       Py_INCREF(self);
+       return (PyObject *)self;
+  finally:
+       return NULL;
+}
+
 static int
 list_ass_item(PyListObject *a, int i, PyObject *v)
 {
@@ -556,25 +600,17 @@ listappend(PyListObject *self, PyObject *args)
        return ins(self, (int) self->ob_size, v);
 }
 
-static PyObject *
-listextend(PyListObject *self, PyObject *args)
+static int
+listextend_internal(PyListObject *self, PyObject *b)
 {
-       PyObject *b = NULL, *res = NULL;
        PyObject **items;
        int selflen = PyList_GET_SIZE(self);
        int blen;
        register int i;
 
-       if (!PyArg_ParseTuple(args, "O:extend", &b))
-               return NULL;
-
-       b = PySequence_Fast(b, "list.extend() argument must be a sequence");
-       if (!b)
-               return NULL;
-
        if (PyObject_Size(b) == 0)
                /* short circuit when b is empty */
-               goto ok;
+               return 0;
 
        if (self == (PyListObject*)b) {
                /* as in list_ass_slice() we must special case the
@@ -586,7 +622,7 @@ listextend(PyListObject *self, PyObject *args)
                Py_DECREF(b);
                b = PyList_New(selflen);
                if (!b)
-                       return NULL;
+                       return -1;
                for (i = 0; i < selflen; i++) {
                        PyObject *o = PyList_GET_ITEM(self, i);
                        Py_INCREF(o);
@@ -601,8 +637,10 @@ listextend(PyListObject *self, PyObject *args)
        NRESIZE(items, PyObject*, selflen + blen);
        if (items == NULL) {
                PyErr_NoMemory();
-               goto failed;
+               Py_DECREF(b);
+               return -1;
        }
+
        self->ob_item = items;
 
        /* populate the end of self with b's items */
@@ -611,14 +649,44 @@ listextend(PyListObject *self, PyObject *args)
                Py_INCREF(o);
                PyList_SET_ITEM(self, self->ob_size++, o);
        }
-  ok:
-       res = Py_None;
-       Py_INCREF(res);
-  failed:
        Py_DECREF(b);
-       return res;
+       return 0;
+}
+
+
+static PyObject *
+list_inplace_concat(PyListObject *self, PyObject *other)
+{
+       other = PySequence_Fast(other, "argument to += must be a sequence");
+       if (!other)
+               return NULL;
+
+       if (listextend_internal(self, other) < 0)
+               return NULL;
+
+       Py_INCREF(self);
+       return (PyObject *)self;
 }
 
+static PyObject *
+listextend(PyListObject *self, PyObject *args)
+{
+
+       PyObject *b;
+       
+       if (!PyArg_ParseTuple(args, "O:extend", &b))
+               return NULL;
+
+       b = PySequence_Fast(b, "list.extend() argument must be a sequence");
+       if (!b)
+               return NULL;
+
+       if (listextend_internal(self, b) < 0)
+               return NULL;
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
 
 static PyObject *
 listpop(PyListObject *self, PyObject *args)
@@ -1407,6 +1475,8 @@ static PySequenceMethods list_as_sequence = {
        (intobjargproc)list_ass_item, /*sq_ass_item*/
        (intintobjargproc)list_ass_slice, /*sq_ass_slice*/
        (objobjproc)list_contains, /*sq_contains*/
+       (binaryfunc)list_inplace_concat, /*sq_inplace_concat*/
+       (intargfunc)list_inplace_repeat, /*sq_inplace_repeat*/
 };
 
 PyTypeObject PyList_Type = {