summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-08-29 12:57:16 +0200
committerBram Moolenaar <Bram@vim.org>2020-08-29 12:57:16 +0200
commit423a85a11a9d3d658906aea715fed7fe6aa83cd8 (patch)
treee37cd64de2c0662552bba8ded601ebc3b00db093
parentb06a6d59d12dbd67d55b3c46f6e5547e9103c931 (diff)
patch 8.2.1538: Python: iteration over vim objects fails to keep referencev8.2.1538
Problem: Python: iteration over vim objects fails to keep reference. Solution: Keep a reference for the object. (Paul Ollis, closes #6803, closes #6806)
-rw-r--r--src/if_py_both.h20
-rw-r--r--src/testdir/test_python3.vim52
-rw-r--r--src/version.c2
3 files changed, 68 insertions, 6 deletions
diff --git a/src/if_py_both.h b/src/if_py_both.h
index 8eb77470a3..86942b686c 100644
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -1442,11 +1442,12 @@ typedef struct
destructorfun destruct;
traversefun traverse;
clearfun clear;
+ PyObject *iter_object;
} IterObject;
static PyObject *
IterNew(void *start, destructorfun destruct, nextfun next, traversefun traverse,
- clearfun clear)
+ clearfun clear, PyObject *iter_object)
{
IterObject *self;
@@ -1456,6 +1457,10 @@ IterNew(void *start, destructorfun destruct, nextfun next, traversefun traverse,
self->destruct = destruct;
self->traverse = traverse;
self->clear = clear;
+ self->iter_object = iter_object;
+
+ if (iter_object)
+ Py_INCREF(iter_object);
return (PyObject *)(self);
}
@@ -1463,6 +1468,8 @@ IterNew(void *start, destructorfun destruct, nextfun next, traversefun traverse,
static void
IterDestructor(IterObject *self)
{
+ if (self->iter_object)
+ Py_DECREF(self->iter_object);
PyObject_GC_UnTrack((void *)(self));
self->destruct(self->cur);
PyObject_GC_Del((void *)(self));
@@ -1844,7 +1851,7 @@ DictionaryIter(DictionaryObject *self)
return IterNew(dii,
(destructorfun) PyMem_Free, (nextfun) DictionaryIterNext,
- NULL, NULL);
+ NULL, NULL, (PyObject *)self);
}
static PyInt
@@ -2842,7 +2849,7 @@ ListIter(ListObject *self)
return IterNew(lii,
(destructorfun) ListIterDestruct, (nextfun) ListIterNext,
- NULL, NULL);
+ NULL, NULL, (PyObject *)self);
}
static char *ListAttrs[] = {
@@ -3491,7 +3498,7 @@ OptionsIter(OptionsObject *self)
return IterNew(oii,
(destructorfun) PyMem_Free, (nextfun) OptionsIterNext,
- NULL, NULL);
+ NULL, NULL, (PyObject *)self);
}
static int
@@ -5488,14 +5495,15 @@ BufMapIterNext(PyObject **buffer)
}
static PyObject *
-BufMapIter(PyObject *self UNUSED)
+BufMapIter(PyObject *self)
{
PyObject *buffer;
buffer = BufferNew(firstbuf);
return IterNew(buffer,
(destructorfun) BufMapIterDestruct, (nextfun) BufMapIterNext,
- (traversefun) BufMapIterTraverse, (clearfun) BufMapIterClear);
+ (traversefun) BufMapIterTraverse, (clearfun) BufMapIterClear,
+ (PyObject *)self);
}
static PyMappingMethods BufMapAsMapping = {
diff --git a/src/testdir/test_python3.vim b/src/testdir/test_python3.vim
index 830fbba50c..0885c96988 100644
--- a/src/testdir/test_python3.vim
+++ b/src/testdir/test_python3.vim
@@ -4,6 +4,15 @@ source check.vim
CheckFeature python3
source shared.vim
+func Create_vim_list()
+ return [1]
+endfunction
+
+func Create_vim_dict()
+ return {'a': 1}
+endfunction
+
+
" This function should be called first. This sets up python functions used by
" the other tests.
func Test_AAA_python3_setup()
@@ -3944,4 +3953,47 @@ func Test_python3_keyboard_interrupt()
close!
endfunc
+" Regression: Iterator for a Vim object should hold a reference.
+func Test_python3_iter_ref()
+ let g:list_iter_ref_count_increase = -1
+ let g:dict_iter_ref_count_increase = -1
+ let g:bufmap_iter_ref_count_increase = -1
+ let g:options_iter_ref_count_increase = -1
+
+ py3 << trim EOF
+ import sys
+ import vim
+
+ def test_python3_iter_ref():
+ create_list = vim.Function('Create_vim_list')
+ v = create_list()
+ base_ref_count = sys.getrefcount(v)
+ for el in v:
+ vim.vars['list_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
+
+ create_dict = vim.Function('Create_vim_dict')
+ v = create_dict()
+ base_ref_count = sys.getrefcount(v)
+ for el in v:
+ vim.vars['dict_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
+
+ v = vim.buffers
+ base_ref_count = sys.getrefcount(v)
+ for el in v:
+ vim.vars['bufmap_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
+
+ v = vim.options
+ base_ref_count = sys.getrefcount(v)
+ for el in v:
+ vim.vars['options_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
+
+ test_python3_iter_ref()
+ EOF
+
+ call assert_equal(1, g:list_iter_ref_count_increase)
+ call assert_equal(1, g:dict_iter_ref_count_increase)
+ call assert_equal(1, g:bufmap_iter_ref_count_increase)
+ call assert_equal(1, g:options_iter_ref_count_increase)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index d4d525bbdd..9e3430a106 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1538,
+/**/
1537,
/**/
1536,