diff --git a/Objects/abstract.c b/Objects/abstract.c index 48604f3fdfa..03049152aac 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -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) { diff --git a/Objects/classobject.c b/Objects/classobject.c index f0f4438e871..f1fd31f289b 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -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 = { diff --git a/Objects/listobject.c b/Objects/listobject.c index 721a4f2ded0..5a704fedf3f 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -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,15 +649,45 @@ 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 = {