Skip to content

Commit 423a85a

Browse files
committed
patch 8.2.1538: Python: iteration over vim objects fails to keep reference
Problem: Python: iteration over vim objects fails to keep reference. Solution: Keep a reference for the object. (Paul Ollis, closes #6803, closes #6806)
1 parent b06a6d5 commit 423a85a

3 files changed

Lines changed: 68 additions & 6 deletions

File tree

src/if_py_both.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,11 +1442,12 @@ typedef struct
14421442
destructorfun destruct;
14431443
traversefun traverse;
14441444
clearfun clear;
1445+
PyObject *iter_object;
14451446
} IterObject;
14461447

14471448
static PyObject *
14481449
IterNew(void *start, destructorfun destruct, nextfun next, traversefun traverse,
1449-
clearfun clear)
1450+
clearfun clear, PyObject *iter_object)
14501451
{
14511452
IterObject *self;
14521453

@@ -1456,13 +1457,19 @@ IterNew(void *start, destructorfun destruct, nextfun next, traversefun traverse,
14561457
self->destruct = destruct;
14571458
self->traverse = traverse;
14581459
self->clear = clear;
1460+
self->iter_object = iter_object;
1461+
1462+
if (iter_object)
1463+
Py_INCREF(iter_object);
14591464

14601465
return (PyObject *)(self);
14611466
}
14621467

14631468
static void
14641469
IterDestructor(IterObject *self)
14651470
{
1471+
if (self->iter_object)
1472+
Py_DECREF(self->iter_object);
14661473
PyObject_GC_UnTrack((void *)(self));
14671474
self->destruct(self->cur);
14681475
PyObject_GC_Del((void *)(self));
@@ -1844,7 +1851,7 @@ DictionaryIter(DictionaryObject *self)
18441851

18451852
return IterNew(dii,
18461853
(destructorfun) PyMem_Free, (nextfun) DictionaryIterNext,
1847-
NULL, NULL);
1854+
NULL, NULL, (PyObject *)self);
18481855
}
18491856

18501857
static PyInt
@@ -2842,7 +2849,7 @@ ListIter(ListObject *self)
28422849

28432850
return IterNew(lii,
28442851
(destructorfun) ListIterDestruct, (nextfun) ListIterNext,
2845-
NULL, NULL);
2852+
NULL, NULL, (PyObject *)self);
28462853
}
28472854

28482855
static char *ListAttrs[] = {
@@ -3491,7 +3498,7 @@ OptionsIter(OptionsObject *self)
34913498

34923499
return IterNew(oii,
34933500
(destructorfun) PyMem_Free, (nextfun) OptionsIterNext,
3494-
NULL, NULL);
3501+
NULL, NULL, (PyObject *)self);
34953502
}
34963503

34973504
static int
@@ -5488,14 +5495,15 @@ BufMapIterNext(PyObject **buffer)
54885495
}
54895496

54905497
static PyObject *
5491-
BufMapIter(PyObject *self UNUSED)
5498+
BufMapIter(PyObject *self)
54925499
{
54935500
PyObject *buffer;
54945501

54955502
buffer = BufferNew(firstbuf);
54965503
return IterNew(buffer,
54975504
(destructorfun) BufMapIterDestruct, (nextfun) BufMapIterNext,
5498-
(traversefun) BufMapIterTraverse, (clearfun) BufMapIterClear);
5505+
(traversefun) BufMapIterTraverse, (clearfun) BufMapIterClear,
5506+
(PyObject *)self);
54995507
}
55005508

55015509
static PyMappingMethods BufMapAsMapping = {

src/testdir/test_python3.vim

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ source check.vim
44
CheckFeature python3
55
source shared.vim
66

7+
func Create_vim_list()
8+
return [1]
9+
endfunction
10+
11+
func Create_vim_dict()
12+
return {'a': 1}
13+
endfunction
14+
15+
716
" This function should be called first. This sets up python functions used by
817
" the other tests.
918
func Test_AAA_python3_setup()
@@ -3944,4 +3953,47 @@ func Test_python3_keyboard_interrupt()
39443953
close!
39453954
endfunc
39463955

3956+
" Regression: Iterator for a Vim object should hold a reference.
3957+
func Test_python3_iter_ref()
3958+
let g:list_iter_ref_count_increase = -1
3959+
let g:dict_iter_ref_count_increase = -1
3960+
let g:bufmap_iter_ref_count_increase = -1
3961+
let g:options_iter_ref_count_increase = -1
3962+
3963+
py3 << trim EOF
3964+
import sys
3965+
import vim
3966+
3967+
def test_python3_iter_ref():
3968+
create_list = vim.Function('Create_vim_list')
3969+
v = create_list()
3970+
base_ref_count = sys.getrefcount(v)
3971+
for el in v:
3972+
vim.vars['list_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
3973+
3974+
create_dict = vim.Function('Create_vim_dict')
3975+
v = create_dict()
3976+
base_ref_count = sys.getrefcount(v)
3977+
for el in v:
3978+
vim.vars['dict_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
3979+
3980+
v = vim.buffers
3981+
base_ref_count = sys.getrefcount(v)
3982+
for el in v:
3983+
vim.vars['bufmap_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
3984+
3985+
v = vim.options
3986+
base_ref_count = sys.getrefcount(v)
3987+
for el in v:
3988+
vim.vars['options_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
3989+
3990+
test_python3_iter_ref()
3991+
EOF
3992+
3993+
call assert_equal(1, g:list_iter_ref_count_increase)
3994+
call assert_equal(1, g:dict_iter_ref_count_increase)
3995+
call assert_equal(1, g:bufmap_iter_ref_count_increase)
3996+
call assert_equal(1, g:options_iter_ref_count_increase)
3997+
endfunc
3998+
39473999
" vim: shiftwidth=2 sts=2 expandtab

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,8 @@ static char *(features[]) =
754754

755755
static int included_patches[] =
756756
{ /* Add new patch number below this line */
757+
/**/
758+
1538,
757759
/**/
758760
1537,
759761
/**/

0 commit comments

Comments
 (0)