diff src/if_py_both.h @ 5608:ec02e1474bc2 v7.4.151

updated for version 7.4.151 Problem: Python: slices with steps are not supported. Solution: Support slices in Python vim.List. (ZyX)
author Bram Moolenaar <bram@vim.org>
date Tue, 14 Jan 2014 16:36:51 +0100
parents 2ca470c6096e
children 2ace11abcfb5
line wrap: on
line diff
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -36,8 +36,9 @@ static const char *vim_special_path = "_
 #define PyErr_SET_STRING(exc, str) PyErr_SetString(exc, _(str))
 #define PyErr_SetVim(str) PyErr_SetString(VimError, str)
 #define PyErr_SET_VIM(str) PyErr_SET_STRING(VimError, str)
-#define PyErr_FORMAT(exc, str, tail) PyErr_Format(exc, _(str), tail)
-#define PyErr_VIM_FORMAT(str, tail) PyErr_FORMAT(VimError, str, tail)
+#define PyErr_FORMAT(exc, str, arg) PyErr_Format(exc, _(str), arg)
+#define PyErr_FORMAT2(exc, str, arg1, arg2) PyErr_Format(exc, _(str), arg1,arg2)
+#define PyErr_VIM_FORMAT(str, arg) PyErr_FORMAT(VimError, str, arg)
 
 #define Py_TYPE_NAME(obj) (obj->ob_type->tp_name == NULL \
 	? "(NULL)" \
@@ -2108,8 +2109,6 @@ static struct PyMethodDef DictionaryMeth
 };
 
 static PyTypeObject ListType;
-static PySequenceMethods ListAsSeq;
-static PyMappingMethods ListAsMapping;
 
 typedef struct
 {
@@ -2253,7 +2252,7 @@ ListLength(ListObject *self)
 }
 
     static PyObject *
-ListItem(ListObject *self, Py_ssize_t index)
+ListIndex(ListObject *self, Py_ssize_t index)
 {
     listitem_T	*li;
 
@@ -2273,53 +2272,389 @@ ListItem(ListObject *self, Py_ssize_t in
     return ConvertToPyObject(&li->li_tv);
 }
 
-#define PROC_RANGE \
-    if (last < 0) {\
-	if (last < -size) \
-	    last = 0; \
-	else \
-	    last += size; \
-    } \
-    if (first < 0) \
-	first = 0; \
-    if (first > size) \
-	first = size; \
-    if (last > size) \
-	last = size;
-
     static PyObject *
-ListSlice(ListObject *self, Py_ssize_t first, Py_ssize_t last)
+ListSlice(ListObject *self, Py_ssize_t first, Py_ssize_t step,
+	  Py_ssize_t slicelen)
 {
     PyInt	i;
-    PyInt	size = ListLength(self);
-    PyInt	n;
     PyObject	*list;
-    int		reversed = 0;
-
-    PROC_RANGE
-    if (first >= last)
-	first = last;
-
-    n = last-first;
-    list = PyList_New(n);
+
+    if (step == 0)
+    {
+	PyErr_SET_STRING(PyExc_ValueError, N_("slice step cannot be zero"));
+	return NULL;
+    }
+
+    list = PyList_New(slicelen);
     if (list == NULL)
 	return NULL;
 
-    for (i = 0; i < n; ++i)
-    {
-	PyObject	*item = ListItem(self, first + i);
+    for (i = 0; i < slicelen; ++i)
+    {
+	PyObject	*item;
+
+	item = ListIndex(self, first + i*step);
 	if (item == NULL)
 	{
 	    Py_DECREF(list);
 	    return NULL;
 	}
 
-	PyList_SET_ITEM(list, ((reversed)?(n-i-1):(i)), item);
+	PyList_SET_ITEM(list, i, item);
     }
 
     return list;
 }
 
+    static PyObject *
+ListItem(ListObject *self, PyObject* idx)
+{
+#if PY_MAJOR_VERSION < 3
+    if (PyInt_Check(idx))
+    {
+	long _idx = PyInt_AsLong(idx);
+	return ListIndex(self, _idx);
+    }
+    else
+#endif
+    if (PyLong_Check(idx))
+    {
+	long _idx = PyLong_AsLong(idx);
+	return ListIndex(self, _idx);
+    }
+    else if (PySlice_Check(idx))
+    {
+	Py_ssize_t start, stop, step, slicelen;
+
+	if (PySlice_GetIndicesEx(idx, ListLength(self),
+				 &start, &stop, &step, &slicelen) < 0)
+	    return NULL;
+	return ListSlice(self, start, step, slicelen);
+    }
+    else
+    {
+	RAISE_INVALID_INDEX_TYPE(idx);
+	return NULL;
+    }
+}
+
+    static void
+list_restore(Py_ssize_t numadded, Py_ssize_t numreplaced, Py_ssize_t slicelen,
+	list_T *l, listitem_T **lis, listitem_T *lastaddedli)
+{
+    while (numreplaced--)
+    {
+	list_insert(l, lis[numreplaced], lis[slicelen + numreplaced]);
+	listitem_remove(l, lis[slicelen + numreplaced]);
+    }
+    while (numadded--)
+    {
+	listitem_T	*next;
+
+	next = lastaddedli->li_prev;
+	listitem_remove(l, lastaddedli);
+	lastaddedli = next;
+    }
+}
+
+    static int
+ListAssSlice(ListObject *self, Py_ssize_t first,
+	     Py_ssize_t step, Py_ssize_t slicelen, PyObject *obj)
+{
+    PyObject	*iterator;
+    PyObject	*item;
+    listitem_T	*li;
+    listitem_T	*lastaddedli = NULL;
+    listitem_T	*next;
+    typval_T	v;
+    list_T	*l = self->list;
+    PyInt	i;
+    PyInt	j;
+    PyInt	numreplaced = 0;
+    PyInt	numadded = 0;
+    PyInt	size;
+    listitem_T	**lis;
+
+    size = ListLength(self);
+
+    if (l->lv_lock)
+    {
+	RAISE_LOCKED_LIST;
+	return -1;
+    }
+
+    if (step == 0)
+    {
+	PyErr_SET_STRING(PyExc_ValueError, N_("slice step cannot be zero"));
+	return -1;
+    }
+
+    if (step != 1 && slicelen == 0)
+    {
+	/* Nothing to do. Only error out if obj has some items. */
+	int		ret = 0;
+
+	if (obj == NULL)
+	    return 0;
+
+	if (!(iterator = PyObject_GetIter(obj)))
+	    return -1;
+
+	if ((item = PyIter_Next(iterator)))
+	{
+	    PyErr_FORMAT(PyExc_ValueError,
+		    N_("attempt to assign sequence of size greater then %d "
+			"to extended slice"), 0);
+	    Py_DECREF(item);
+	    ret = -1;
+	}
+	Py_DECREF(iterator);
+	return ret;
+    }
+
+    if (obj != NULL)
+	/* XXX May allocate zero bytes. */
+	if (!(lis = PyMem_New(listitem_T *, slicelen * 2)))
+	{
+	    PyErr_NoMemory();
+	    return -1;
+	}
+
+    if (first == size)
+	li = NULL;
+    else
+    {
+	li = list_find(l, (long) first);
+	if (li == NULL)
+	{
+	    PyErr_VIM_FORMAT(N_("internal error: no vim list item %d"),
+		    (int)first);
+	    if (obj != NULL)
+		PyMem_Free(lis);
+	    return -1;
+	}
+	i = slicelen;
+	while (i-- && li != NULL)
+	{
+	    j = step;
+	    next = li;
+	    if (step > 0)
+		while (next != NULL && ((next = next->li_next) != NULL) && --j);
+	    else
+		while (next != NULL && ((next = next->li_prev) != NULL) && ++j);
+
+	    if (obj == NULL)
+		listitem_remove(l, li);
+	    else
+		lis[slicelen - i - 1] = li;
+
+	    li = next;
+	}
+	if (li == NULL && i != -1)
+	{
+	    PyErr_SET_VIM(N_("internal error: not enough list items"));
+	    if (obj != NULL)
+		PyMem_Free(lis);
+	    return -1;
+	}
+    }
+
+    if (obj == NULL)
+	return 0;
+
+    if (!(iterator = PyObject_GetIter(obj)))
+    {
+	PyMem_Free(lis);
+	return -1;
+    }
+
+    i = 0;
+    while ((item = PyIter_Next(iterator)))
+    {
+	if (ConvertFromPyObject(item, &v) == -1)
+	{
+	    Py_DECREF(iterator);
+	    Py_DECREF(item);
+	    PyMem_Free(lis);
+	    return -1;
+	}
+	Py_DECREF(item);
+	if (list_insert_tv(l, &v, numreplaced < slicelen
+				    ? lis[numreplaced]
+				    : li) == FAIL)
+	{
+	    clear_tv(&v);
+	    PyErr_SET_VIM(N_("internal error: failed to add item to list"));
+	    list_restore(numadded, numreplaced, slicelen, l, lis, lastaddedli);
+	    PyMem_Free(lis);
+	    return -1;
+	}
+	if (numreplaced < slicelen)
+	{
+	    lis[slicelen + numreplaced] = lis[numreplaced]->li_prev;
+	    list_remove(l, lis[numreplaced], lis[numreplaced]);
+	    numreplaced++;
+	}
+	else
+	{
+	    if (li)
+		lastaddedli = li->li_prev;
+	    else
+		lastaddedli = l->lv_last;
+	    numadded++;
+	}
+	clear_tv(&v);
+	if (step != 1 && i >= slicelen)
+	{
+	    Py_DECREF(iterator);
+	    PyErr_FORMAT(PyExc_ValueError,
+		    N_("attempt to assign sequence of size greater then %d "
+			"to extended slice"), slicelen);
+	    list_restore(numadded, numreplaced, slicelen, l, lis, lastaddedli);
+	    PyMem_Free(lis);
+	    return -1;
+	}
+	++i;
+    }
+    Py_DECREF(iterator);
+
+    if (step != 1 && i != slicelen)
+    {
+	PyErr_FORMAT2(PyExc_ValueError,
+		N_("attempt to assign sequence of size %d to extended slice "
+		    "of size %d"), i, slicelen);
+	list_restore(numadded, numreplaced, slicelen, l, lis, lastaddedli);
+	PyMem_Free(lis);
+	return -1;
+    }
+
+    if (PyErr_Occurred())
+    {
+	list_restore(numadded, numreplaced, slicelen, l, lis, lastaddedli);
+	PyMem_Free(lis);
+	return -1;
+    }
+
+    for (i = 0; i < numreplaced; i++)
+	listitem_free(lis[i]);
+    if (step == 1)
+	for (i = numreplaced; i < slicelen; i++)
+	    listitem_remove(l, lis[i]);
+
+    PyMem_Free(lis);
+
+    return 0;
+}
+
+    static int
+ListAssIndex(ListObject *self, Py_ssize_t index, PyObject *obj)
+{
+    typval_T	tv;
+    list_T	*l = self->list;
+    listitem_T	*li;
+    Py_ssize_t	length = ListLength(self);
+
+    if (l->lv_lock)
+    {
+	RAISE_LOCKED_LIST;
+	return -1;
+    }
+    if (index > length || (index == length && obj == NULL))
+    {
+	PyErr_SET_STRING(PyExc_IndexError, N_("list index out of range"));
+	return -1;
+    }
+
+    if (obj == NULL)
+    {
+	li = list_find(l, (long) index);
+	list_remove(l, li, li);
+	clear_tv(&li->li_tv);
+	vim_free(li);
+	return 0;
+    }
+
+    if (ConvertFromPyObject(obj, &tv) == -1)
+	return -1;
+
+    if (index == length)
+    {
+	if (list_append_tv(l, &tv) == FAIL)
+	{
+	    clear_tv(&tv);
+	    PyErr_SET_VIM(N_("failed to add item to list"));
+	    return -1;
+	}
+    }
+    else
+    {
+	li = list_find(l, (long) index);
+	clear_tv(&li->li_tv);
+	copy_tv(&tv, &li->li_tv);
+	clear_tv(&tv);
+    }
+    return 0;
+}
+
+    static Py_ssize_t
+ListAssItem(ListObject *self, PyObject *idx, PyObject *obj)
+{
+#if PY_MAJOR_VERSION < 3
+    if (PyInt_Check(idx))
+    {
+	long _idx = PyInt_AsLong(idx);
+	return ListAssIndex(self, _idx, obj);
+    }
+    else
+#endif
+    if (PyLong_Check(idx))
+    {
+	long _idx = PyLong_AsLong(idx);
+	return ListAssIndex(self, _idx, obj);
+    }
+    else if (PySlice_Check(idx))
+    {
+	Py_ssize_t start, stop, step, slicelen;
+
+	if (PySlice_GetIndicesEx(idx, ListLength(self),
+				 &start, &stop, &step, &slicelen) < 0)
+	    return -1;
+	return ListAssSlice(self, start, step, slicelen,
+		obj);
+    }
+    else
+    {
+	RAISE_INVALID_INDEX_TYPE(idx);
+	return -1;
+    }
+}
+
+    static PyObject *
+ListConcatInPlace(ListObject *self, PyObject *obj)
+{
+    list_T	*l = self->list;
+    PyObject	*lookup_dict;
+
+    if (l->lv_lock)
+    {
+	RAISE_LOCKED_LIST;
+	return NULL;
+    }
+
+    if (!(lookup_dict = PyDict_New()))
+	return NULL;
+
+    if (list_py_concat(l, obj, lookup_dict) == -1)
+    {
+	Py_DECREF(lookup_dict);
+	return NULL;
+    }
+    Py_DECREF(lookup_dict);
+
+    Py_INCREF(self);
+    return (PyObject *)(self);
+}
+
 typedef struct
 {
     listwatch_T	lw;
@@ -2370,156 +2705,6 @@ ListIter(ListObject *self)
 	    NULL, NULL);
 }
 
-    static int
-ListAssItem(ListObject *self, Py_ssize_t index, PyObject *obj)
-{
-    typval_T	tv;
-    list_T	*l = self->list;
-    listitem_T	*li;
-    Py_ssize_t	length = ListLength(self);
-
-    if (l->lv_lock)
-    {
-	RAISE_LOCKED_LIST;
-	return -1;
-    }
-    if (index > length || (index == length && obj == NULL))
-    {
-	PyErr_SET_STRING(PyExc_IndexError, N_("list index out of range"));
-	return -1;
-    }
-
-    if (obj == NULL)
-    {
-	li = list_find(l, (long) index);
-	list_remove(l, li, li);
-	clear_tv(&li->li_tv);
-	vim_free(li);
-	return 0;
-    }
-
-    if (ConvertFromPyObject(obj, &tv) == -1)
-	return -1;
-
-    if (index == length)
-    {
-	if (list_append_tv(l, &tv) == FAIL)
-	{
-	    clear_tv(&tv);
-	    PyErr_SET_VIM(N_("failed to add item to list"));
-	    return -1;
-	}
-    }
-    else
-    {
-	li = list_find(l, (long) index);
-	clear_tv(&li->li_tv);
-	copy_tv(&tv, &li->li_tv);
-	clear_tv(&tv);
-    }
-    return 0;
-}
-
-    static int
-ListAssSlice(ListObject *self, Py_ssize_t first, Py_ssize_t last, PyObject *obj)
-{
-    PyInt	size = ListLength(self);
-    PyObject	*iterator;
-    PyObject	*item;
-    listitem_T	*li;
-    listitem_T	*next;
-    typval_T	v;
-    list_T	*l = self->list;
-    PyInt	i;
-
-    if (l->lv_lock)
-    {
-	RAISE_LOCKED_LIST;
-	return -1;
-    }
-
-    PROC_RANGE
-
-    if (first == size)
-	li = NULL;
-    else
-    {
-	li = list_find(l, (long) first);
-	if (li == NULL)
-	{
-	    PyErr_VIM_FORMAT(N_("internal error: no vim list item %d"),
-		    (int)first);
-	    return -1;
-	}
-	if (last > first)
-	{
-	    i = last - first;
-	    while (i-- && li != NULL)
-	    {
-		next = li->li_next;
-		listitem_remove(l, li);
-		li = next;
-	    }
-	}
-    }
-
-    if (obj == NULL)
-	return 0;
-
-    if (!(iterator = PyObject_GetIter(obj)))
-	return -1;
-
-    while ((item = PyIter_Next(iterator)))
-    {
-	if (ConvertFromPyObject(item, &v) == -1)
-	{
-	    Py_DECREF(iterator);
-	    Py_DECREF(item);
-	    return -1;
-	}
-	Py_DECREF(item);
-	if (list_insert_tv(l, &v, li) == FAIL)
-	{
-	    clear_tv(&v);
-	    PyErr_SET_VIM(N_("internal error: failed to add item to list"));
-	    return -1;
-	}
-	clear_tv(&v);
-    }
-    Py_DECREF(iterator);
-
-    if (PyErr_Occurred())
-	return -1;
-
-    return 0;
-}
-
-    static PyObject *
-ListConcatInPlace(ListObject *self, PyObject *obj)
-{
-    list_T	*l = self->list;
-    PyObject	*lookup_dict;
-
-    if (l->lv_lock)
-    {
-	RAISE_LOCKED_LIST;
-	return NULL;
-    }
-
-    if (!(lookup_dict = PyDict_New()))
-	return NULL;
-
-    if (list_py_concat(l, obj, lookup_dict) == -1)
-    {
-	Py_DECREF(lookup_dict);
-	return NULL;
-    }
-    Py_DECREF(lookup_dict);
-
-    Py_INCREF(self);
-    return (PyObject *)(self);
-}
-
 static char *ListAttrs[] = {
     "locked",
     NULL
@@ -2567,6 +2752,25 @@ ListSetattr(ListObject *self, char *name
     }
 }
 
+static PySequenceMethods ListAsSeq = {
+    (lenfunc)		ListLength,	 /* sq_length,	  len(x)   */
+    (binaryfunc)	0,		 /* RangeConcat, sq_concat,  x+y   */
+    0,					 /* RangeRepeat, sq_repeat,  x*n   */
+    (PyIntArgFunc)	ListIndex,	 /* sq_item,	  x[i]	   */
+    0,					 /* was_sq_slice,     x[i:j]   */
+    (PyIntObjArgProc)	ListAssIndex,	 /* sq_as_item,  x[i]=v   */
+    0,					 /* was_sq_ass_slice, x[i:j]=v */
+    0,					 /* sq_contains */
+    (binaryfunc)	ListConcatInPlace,/* sq_inplace_concat */
+    0,					 /* sq_inplace_repeat */
+};
+
+static PyMappingMethods ListAsMapping = {
+    /* mp_length	*/ (lenfunc) ListLength,
+    /* mp_subscript     */ (binaryfunc) ListItem,
+    /* mp_ass_subscript */ (objobjargproc) ListAssItem,
+};
+
 static struct PyMethodDef ListMethods[] = {
     {"extend",	(PyCFunction)ListConcatInPlace,	METH_O,		""},
     {"__dir__",	(PyCFunction)ListDir,		METH_NOARGS,	""},