diff --git a/7.3.947 b/7.3.947 new file mode 100644 index 0000000..2093c9d --- /dev/null +++ b/7.3.947 @@ -0,0 +1,703 @@ +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 ///