To: vim_dev@googlegroups.com Subject: Patch 7.3.947 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.3.947 Problem: Python: No iterator for vim.list and vim.bufferlist. Solution: Add the iterators. Also fix name of FunctionType. Add tests for vim.buffers. (ZyX) Files: runtime/doc/if_pyth.txt, src/eval.c, src/if_py_both.h, src/if_python3.c, src/if_python.c, src/proto/eval.pro, src/testdir/test86.in, src/testdir/test86.ok, src/testdir/test87.in, src/testdir/test87.ok *** ../vim-7.3.946/runtime/doc/if_pyth.txt 2013-05-15 13:38:41.000000000 +0200 --- runtime/doc/if_pyth.txt 2013-05-15 14:24:11.000000000 +0200 *************** *** 214,219 **** --- 214,220 ---- :py b = vim.buffers[i] # Indexing (read-only) :py b in vim.buffers # Membership test :py n = len(vim.buffers) # Number of elements + :py for b in vim.buffers: # Iterating over buffer list < vim.windows *python-windows* A sequence object providing access to the list of vim windows. The *** ../vim-7.3.946/src/eval.c 2013-05-06 04:50:26.000000000 +0200 --- src/eval.c 2013-05-15 14:24:11.000000000 +0200 *************** *** 390,397 **** static void clear_lval __ARGS((lval_T *lp)); static void set_var_lval __ARGS((lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op)); static int tv_op __ARGS((typval_T *tv1, typval_T *tv2, char_u *op)); - static void list_add_watch __ARGS((list_T *l, listwatch_T *lw)); - static void list_rem_watch __ARGS((list_T *l, listwatch_T *lwrem)); static void list_fix_watch __ARGS((list_T *l, listitem_T *item)); static void ex_unletlock __ARGS((exarg_T *eap, char_u *argstart, int deep)); static int do_unlet_var __ARGS((lval_T *lp, char_u *name_end, int forceit)); --- 390,395 ---- *************** *** 3106,3112 **** /* * Add a watcher to a list. */ ! static void list_add_watch(l, lw) list_T *l; listwatch_T *lw; --- 3104,3110 ---- /* * Add a watcher to a list. */ ! void list_add_watch(l, lw) list_T *l; listwatch_T *lw; *************** *** 3119,3125 **** * Remove a watcher from a list. * No warning when it isn't found... */ ! static void list_rem_watch(l, lwrem) list_T *l; listwatch_T *lwrem; --- 3117,3123 ---- * Remove a watcher from a list. * No warning when it isn't found... */ ! void list_rem_watch(l, lwrem) list_T *l; listwatch_T *lwrem; *** ../vim-7.3.946/src/if_py_both.h 2013-05-15 13:38:41.000000000 +0200 --- src/if_py_both.h 2013-05-15 14:24:11.000000000 +0200 *************** *** 531,596 **** }; /* ! * Buffer list object - Implementation */ ! static PyTypeObject BufMapType; typedef struct { PyObject_HEAD ! } BufMapObject; ! static PyInt ! BufMapLength(PyObject *self UNUSED) { ! buf_T *b = firstbuf; ! PyInt n = 0; ! while (b) ! { ! ++n; ! b = b->b_next; ! } ! return n; } ! static PyObject * ! BufMapItem(PyObject *self UNUSED, PyObject *keyObject) { ! buf_T *b; ! int bnr; ! #if PY_MAJOR_VERSION < 3 ! if (PyInt_Check(keyObject)) ! bnr = PyInt_AsLong(keyObject); ! else ! #endif ! if (PyLong_Check(keyObject)) ! bnr = PyLong_AsLong(keyObject); ! else ! { ! PyErr_SetString(PyExc_ValueError, _("key must be integer")); ! return NULL; ! } ! b = buflist_findnr(bnr); ! if (b) ! return BufferNew(b); ! else ! { ! PyErr_SetString(PyExc_KeyError, _("no such buffer")); ! return NULL; ! } } ! static PyMappingMethods BufMapAsMapping = { ! (lenfunc) BufMapLength, ! (binaryfunc) BufMapItem, ! (objobjargproc) 0, ! }; typedef struct pylinkedlist_S { struct pylinkedlist_S *pll_next; --- 531,592 ---- }; /* ! * Generic iterator object */ ! static PyTypeObject IterType; ! ! typedef PyObject *(*nextfun)(void **); ! typedef void (*destructorfun)(void *); ! ! /* Main purpose of this object is removing the need for do python initialization ! * (i.e. PyType_Ready and setting type attributes) for a big bunch of objects. ! */ typedef struct { PyObject_HEAD ! void *cur; ! nextfun next; ! destructorfun destruct; ! } IterObject; ! static PyObject * ! IterNew(void *start, destructorfun destruct, nextfun next) { ! IterObject *self; ! self = PyObject_NEW(IterObject, &IterType); ! self->cur = start; ! self->next = next; ! self->destruct = destruct; ! return (PyObject *)(self); } ! static void ! IterDestructor(PyObject *self) { ! IterObject *this = (IterObject *)(self); ! this->destruct(this->cur); ! DESTRUCTOR_FINISH(self); ! } ! static PyObject * ! IterNext(PyObject *self) ! { ! IterObject *this = (IterObject *)(self); ! ! return this->next(&this->cur); } ! static PyObject * ! IterIter(PyObject *self) ! { ! return self; ! } typedef struct pylinkedlist_S { struct pylinkedlist_S *pll_next; *************** *** 990,995 **** --- 986,1040 ---- return list; } + typedef struct + { + listwatch_T lw; + list_T *list; + } listiterinfo_T; + + static void + ListIterDestruct(listiterinfo_T *lii) + { + list_rem_watch(lii->list, &lii->lw); + PyMem_Free(lii); + } + + static PyObject * + ListIterNext(listiterinfo_T **lii) + { + PyObject *r; + + if (!((*lii)->lw.lw_item)) + return NULL; + + if (!(r = ConvertToPyObject(&((*lii)->lw.lw_item->li_tv)))) + return NULL; + + (*lii)->lw.lw_item = (*lii)->lw.lw_item->li_next; + + return r; + } + + static PyObject * + ListIter(PyObject *self) + { + listiterinfo_T *lii; + list_T *l = ((ListObject *) (self))->list; + + if (!(lii = PyMem_New(listiterinfo_T, 1))) + { + PyErr_NoMemory(); + return NULL; + } + + list_add_watch(l, &lii->lw); + lii->lw.lw_item = l->lv_first; + lii->list = l; + + return IterNew(lii, + (destructorfun) ListIterDestruct, (nextfun) ListIterNext); + } + static int ListAssItem(PyObject *self, Py_ssize_t index, PyObject *obj) { *************** *** 2869,2874 **** --- 2914,3029 ---- { NULL, NULL, 0, NULL } }; + /* + * Buffer list object - Implementation + */ + + static PyTypeObject BufMapType; + + typedef struct + { + PyObject_HEAD + } BufMapObject; + + static PyInt + BufMapLength(PyObject *self UNUSED) + { + buf_T *b = firstbuf; + PyInt n = 0; + + while (b) + { + ++n; + b = b->b_next; + } + + return n; + } + + static PyObject * + BufMapItem(PyObject *self UNUSED, PyObject *keyObject) + { + buf_T *b; + int bnr; + + #if PY_MAJOR_VERSION < 3 + if (PyInt_Check(keyObject)) + bnr = PyInt_AsLong(keyObject); + else + #endif + if (PyLong_Check(keyObject)) + bnr = PyLong_AsLong(keyObject); + else + { + PyErr_SetString(PyExc_ValueError, _("key must be integer")); + return NULL; + } + + b = buflist_findnr(bnr); + + if (b) + return BufferNew(b); + else + { + PyErr_SetString(PyExc_KeyError, _("no such buffer")); + return NULL; + } + } + + static void + BufMapIterDestruct(PyObject *buffer) + { + /* Iteration was stopped before all buffers were processed */ + if (buffer) + { + Py_DECREF(buffer); + } + } + + static PyObject * + BufMapIterNext(PyObject **buffer) + { + PyObject *next; + PyObject *r; + + if (!*buffer) + return NULL; + + r = *buffer; + + if (CheckBuffer((BufferObject *)(r))) + { + *buffer = NULL; + return NULL; + } + + if (!((BufferObject *)(r))->buf->b_next) + next = NULL; + else if (!(next = BufferNew(((BufferObject *)(r))->buf->b_next))) + return NULL; + *buffer = next; + /* Do not increment reference: we no longer hold it (decref), but whoever on + * other side will hold (incref). Decref+incref = nothing. + */ + return r; + } + + static PyObject * + BufMapIter(PyObject *self UNUSED) + { + PyObject *buffer; + + buffer = BufferNew(firstbuf); + return IterNew(buffer, + (destructorfun) BufMapIterDestruct, (nextfun) BufMapIterNext); + } + + static PyMappingMethods BufMapAsMapping = { + (lenfunc) BufMapLength, + (binaryfunc) BufMapItem, + (objobjargproc) 0, + }; + /* Current items object */ *************** *** 3383,3388 **** --- 3538,3551 ---- OutputType.tp_setattr = OutputSetattr; #endif + vim_memset(&IterType, 0, sizeof(IterType)); + IterType.tp_name = "vim.iter"; + IterType.tp_basicsize = sizeof(IterObject); + IterType.tp_flags = Py_TPFLAGS_DEFAULT; + IterType.tp_doc = "generic iterator object"; + IterType.tp_iter = IterIter; + IterType.tp_iternext = IterNext; + vim_memset(&BufferType, 0, sizeof(BufferType)); BufferType.tp_name = "vim.buffer"; BufferType.tp_basicsize = sizeof(BufferType); *************** *** 3426,3431 **** --- 3589,3595 ---- BufMapType.tp_basicsize = sizeof(BufMapObject); BufMapType.tp_as_mapping = &BufMapAsMapping; BufMapType.tp_flags = Py_TPFLAGS_DEFAULT; + BufMapType.tp_iter = BufMapIter; BufferType.tp_doc = "vim buffer list"; vim_memset(&WinListType, 0, sizeof(WinListType)); *************** *** 3492,3497 **** --- 3656,3662 ---- ListType.tp_flags = Py_TPFLAGS_DEFAULT; ListType.tp_doc = "list pushing modifications to vim structure"; ListType.tp_methods = ListMethods; + ListType.tp_iter = ListIter; #if PY_MAJOR_VERSION >= 3 ListType.tp_getattro = ListGetattro; ListType.tp_setattro = ListSetattro; *************** *** 3501,3507 **** #endif vim_memset(&FunctionType, 0, sizeof(FunctionType)); ! FunctionType.tp_name = "vim.list"; FunctionType.tp_basicsize = sizeof(FunctionObject); FunctionType.tp_dealloc = FunctionDestructor; FunctionType.tp_call = FunctionCall; --- 3666,3672 ---- #endif vim_memset(&FunctionType, 0, sizeof(FunctionType)); ! FunctionType.tp_name = "vim.function"; FunctionType.tp_basicsize = sizeof(FunctionObject); FunctionType.tp_dealloc = FunctionDestructor; FunctionType.tp_call = FunctionCall; *** ../vim-7.3.946/src/if_python3.c 2013-05-15 13:38:41.000000000 +0200 --- src/if_python3.c 2013-05-15 14:24:11.000000000 +0200 *************** *** 1519,1524 **** --- 1519,1525 ---- /* The special value is removed from sys.path in Python3_Init(). */ static wchar_t *(argv[2]) = {L"/must>not&exist/foo", NULL}; + PyType_Ready(&IterType); PyType_Ready(&BufferType); PyType_Ready(&RangeType); PyType_Ready(&WindowType); *** ../vim-7.3.946/src/if_python.c 2013-05-15 13:38:41.000000000 +0200 --- src/if_python.c 2013-05-15 14:24:11.000000000 +0200 *************** *** 1219,1224 **** --- 1219,1225 ---- static char *(argv[2]) = {"/must>not&exist/foo", NULL}; /* Fixups... */ + PyType_Ready(&IterType); PyType_Ready(&BufferType); PyType_Ready(&RangeType); PyType_Ready(&WindowType); *** ../vim-7.3.946/src/proto/eval.pro 2013-05-06 03:52:44.000000000 +0200 --- src/proto/eval.pro 2013-05-15 14:24:11.000000000 +0200 *************** *** 127,130 **** --- 127,132 ---- char_u *do_string_sub __ARGS((char_u *str, char_u *pat, char_u *sub, char_u *flags)); int switch_win __ARGS((win_T **, tabpage_T **, win_T *, tabpage_T *)); void restore_win __ARGS((win_T *, tabpage_T *)); + void list_add_watch __ARGS((list_T *l, listwatch_T *lw)); + void list_rem_watch __ARGS((list_T *l, listwatch_T *lwrem)); /* vim: set ft=c : */ *** ../vim-7.3.946/src/testdir/test86.in 2013-05-12 21:16:17.000000000 +0200 --- src/testdir/test86.in 2013-05-15 14:27:21.000000000 +0200 *************** *** 477,482 **** --- 477,485 ---- : call RecVars(oname) :endfor :only + :for buf in g:bufs[1:] + : execute 'bwipeout!' buf + :endfor :" :" Test buffer object :vnew *************** *** 519,524 **** --- 522,583 ---- # Should not happen in any case cb.append('No exception for ' + expr) EOF + :" + :" Test vim.buffers object + :set hidden + :edit a + :buffer # + :edit b + :buffer # + :edit c + :buffer # + py << EOF + # Check GCing iterator that was not fully exhausted + i = iter(vim.buffers) + cb.append('i:' + str(next(i))) + # and also check creating more then one iterator at a time + i2 = iter(vim.buffers) + cb.append('i2:' + str(next(i2))) + cb.append('i:' + str(next(i))) + # The following should trigger GC and not cause any problems + del i + del i2 + i3 = iter(vim.buffers) + cb.append('i3:' + str(next(i3))) + del i3 + + prevnum = 0 + for b in vim.buffers: + # Check buffer order + if prevnum >= b.number: + cb.append('!!! Buffer numbers not in strictly ascending order') + # Check indexing: vim.buffers[number].number == number + cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b)) + prevnum = b.number + + cb.append(str(len(vim.buffers))) + + bnums = list(map(lambda b: b.number, vim.buffers))[1:] + + # Test wiping out buffer with existing iterator + i4 = iter(vim.buffers) + cb.append('i4:' + str(next(i4))) + vim.command('bwipeout! ' + str(bnums.pop(0))) + try: + next(i4) + except vim.error: + pass + else: + cb.append('!!!! No vim.error') + i4 = iter(vim.buffers) + vim.command('bwipeout! ' + str(bnums.pop(-1))) + vim.command('bwipeout! ' + str(bnums.pop(-1))) + cb.append('i4:' + str(next(i4))) + try: + next(i4) + except StopIteration: + cb.append('StopIteration') + EOF :endfun :" :call Test() *** ../vim-7.3.946/src/testdir/test86.ok 2013-05-15 13:38:41.000000000 +0200 --- src/testdir/test86.ok 2013-05-15 14:27:21.000000000 +0200 *************** *** 319,321 **** --- 319,333 ---- Second line Third line foo + i: + i2: + i: + i3: + 1:= + 6:= + 7:= + 8:= + 4 + i4: + i4: + StopIteration *** ../vim-7.3.946/src/testdir/test87.in 2013-05-12 21:16:17.000000000 +0200 --- src/testdir/test87.in 2013-05-15 14:27:21.000000000 +0200 *************** *** 446,451 **** --- 446,454 ---- : call RecVars(oname) :endfor :only + :for buf in g:bufs[1:] + : execute 'bwipeout!' buf + :endfor :" :" Test buffer object :vnew *************** *** 488,493 **** --- 491,552 ---- # Should not happen in any case cb.append('No exception for ' + expr) EOF + :" + :" Test vim.buffers object + :set hidden + :edit a + :buffer # + :edit b + :buffer # + :edit c + :buffer # + py3 << EOF + # Check GCing iterator that was not fully exhausted + i = iter(vim.buffers) + cb.append('i:' + str(next(i))) + # and also check creating more then one iterator at a time + i2 = iter(vim.buffers) + cb.append('i2:' + str(next(i2))) + cb.append('i:' + str(next(i))) + # The following should trigger GC and not cause any problems + del i + del i2 + i3 = iter(vim.buffers) + cb.append('i3:' + str(next(i3))) + del i3 + + prevnum = 0 + for b in vim.buffers: + # Check buffer order + if prevnum >= b.number: + cb.append('!!! Buffer numbers not in strictly ascending order') + # Check indexing: vim.buffers[number].number == number + cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b)) + prevnum = b.number + + cb.append(str(len(vim.buffers))) + + bnums = list(map(lambda b: b.number, vim.buffers))[1:] + + # Test wiping out buffer with existing iterator + i4 = iter(vim.buffers) + cb.append('i4:' + str(next(i4))) + vim.command('bwipeout! ' + str(bnums.pop(0))) + try: + next(i4) + except vim.error: + pass + else: + cb.append('!!!! No vim.error') + i4 = iter(vim.buffers) + vim.command('bwipeout! ' + str(bnums.pop(-1))) + vim.command('bwipeout! ' + str(bnums.pop(-1))) + cb.append('i4:' + str(next(i4))) + try: + next(i4) + except StopIteration: + cb.append('StopIteration') + EOF :endfun :" :call Test() *************** *** 496,501 **** --- 555,561 ---- :call garbagecollect(1) :" :/^start:/,$wq! test.out + :call getchar() ENDTEST start: *** ../vim-7.3.946/src/testdir/test87.ok 2013-05-15 13:38:41.000000000 +0200 --- src/testdir/test87.ok 2013-05-15 14:27:21.000000000 +0200 *************** *** 308,310 **** --- 308,322 ---- Second line Third line foo + i: + i2: + i: + i3: + 1:= + 6:= + 7:= + 8:= + 4 + i4: + i4: + StopIteration *** ../vim-7.3.946/src/version.c 2013-05-15 14:22:36.000000000 +0200 --- src/version.c 2013-05-15 14:25:26.000000000 +0200 *************** *** 730,731 **** --- 730,733 ---- { /* Add new patch number below this line */ + /**/ + 947, /**/ -- "It's so simple to be wise. Just think of something stupid to say and then don't say it." -- Sam Levenson /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///