1
0
forked from aniani/vim

patch 9.1.1239: if_python: no tuple data type support

Problem:  if_python: no tuple data type support (after v9.1.1232)
Solution: Add support for using Vim tuple in the python interface
          (Yegappan Lakshmanan)

closes: #16964

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yegappan Lakshmanan 2025-03-26 18:46:21 +01:00 committed by Christian Brabandt
parent 9d5487f6fd
commit 038be2701d
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
17 changed files with 1065 additions and 32 deletions

View File

@ -1,4 +1,4 @@
*builtin.txt* For Vim version 9.1. Last change: 2025 Mar 24
*builtin.txt* For Vim version 9.1. Last change: 2025 Mar 26
VIM REFERENCE MANUAL by Bram Moolenaar
@ -8336,13 +8336,14 @@ py3eval({expr} [, {locals}]) *py3eval()*
converted to Vim data structures.
If a {locals} |Dictionary| is given, it defines set of local
variables available in the expression. The keys are variable
names and the values are the variable values. |Dictionary| and
|List| values are referenced, and may be updated by the
expression (as if |python-bindeval| was used).
names and the values are the variable values. |Dictionary|,
|List| and |Tuple| values are referenced, and may be updated
by the expression (as if |python-bindeval| was used).
Numbers and strings are returned as they are (strings are
copied though, Unicode strings are additionally converted to
'encoding').
Lists are represented as Vim |List| type.
Tuples are represented as Vim |Tuple| type.
Dictionaries are represented as Vim |Dictionary| type with
keys converted to strings.
Note that in a `:def` function local variables are not visible
@ -8364,6 +8365,7 @@ pyeval({expr} [, {locals}]) *pyeval()*
Numbers and strings are returned as they are (strings are
copied though).
Lists are represented as Vim |List| type.
Tuples are represented as Vim |Tuple| type.
Dictionaries are represented as Vim |Dictionary| type,
non-string keys result in error.
Note that in a `:def` function local variables are not visible

View File

@ -1,4 +1,4 @@
*if_pyth.txt* For Vim version 9.1. Last change: 2024 Nov 09
*if_pyth.txt* For Vim version 9.1. Last change: 2025 Mar 26
VIM REFERENCE MANUAL by Paul Moore
@ -184,8 +184,9 @@ vim.eval(str) *python-eval*
evaluator (see |expression|). Returns the expression result as:
- a string if the Vim expression evaluates to a string or number
- a list if the Vim expression evaluates to a Vim list
- a tuple if the Vim expression evaluates to a Vim tuple
- a dictionary if the Vim expression evaluates to a Vim dictionary
Dictionaries and lists are recursively expanded.
Dictionaries, lists and tuples are recursively expanded.
Examples: >
:" value of the 'textwidth' option
:py text_width = vim.eval("&tw")
@ -196,6 +197,8 @@ vim.eval(str) *python-eval*
:" Result is a string! Use string.atoi() to convert to a number.
:py str = vim.eval("12+12")
:
:py tuple = vim.eval('(1, 2, 3)')
:
:py tagList = vim.eval('taglist("eval_expr")')
< The latter will return a python list of python dicts, for instance:
[{'cmd': '/^eval_expr(arg, nextcmd)$/', 'static': 0, 'name': ~
@ -207,8 +210,8 @@ vim.eval(str) *python-eval*
vim.bindeval(str) *python-bindeval*
Like |python-eval|, but returns special objects described in
|python-bindeval-objects|. These python objects let you modify (|List|
or |Dictionary|) or call (|Funcref|) vim objects.
|python-bindeval-objects|. These python objects let you modify
(|List|, |Tuple| or |Dictionary|) or call (|Funcref|) vim objects.
vim.strwidth(str) *python-strwidth*
Like |strwidth()|: returns number of display cells str occupies, tab
@ -688,6 +691,22 @@ vim.List object *python-List*
print isinstance(l, vim.List) # True
class List(vim.List): # Subclassing
vim.Tuple object *python-Tuple*
Sequence-like object providing access to vim |Tuple| type.
Supports `.locked` attribute, see |python-.locked|. Also supports the
following methods:
Method Description ~
__new__(), __new__(iterable)
You can use `vim.Tuple()` to create new vim tuples.
Without arguments constructs empty list.
Examples: >
t = vim.Tuple("abc") # Constructor, result: ('a', 'b', 'c')
print t[1:] # slicing
print t[0] # getting item
for i in t: # iteration
print isinstance(t, vim.Tuple) # True
class Tuple(vim.Tuple): # Subclassing
vim.Function object *python-Function*
Function-like object, acting like vim |Funcref| object. Accepts special
keyword argument `self`, see |Dictionary-function|. You can also use

View File

@ -9683,6 +9683,7 @@ python-2-and-3 if_pyth.txt /*python-2-and-3*
python-Dictionary if_pyth.txt /*python-Dictionary*
python-Function if_pyth.txt /*python-Function*
python-List if_pyth.txt /*python-List*
python-Tuple if_pyth.txt /*python-Tuple*
python-VIM_SPECIAL_PATH if_pyth.txt /*python-VIM_SPECIAL_PATH*
python-_get_paths if_pyth.txt /*python-_get_paths*
python-bindeval if_pyth.txt /*python-bindeval*

View File

@ -2,7 +2,7 @@
" Language: Vim script
" Maintainer: Hirohito Higashi <h.east.727 ATMARK gmail.com>
" Doug Kearns <dougkearns@gmail.com>
" Last Change: 2025 Mar 18
" Last Change: 2025 Mar 26
" Former Maintainer: Charles E. Campbell
" DO NOT CHANGE DIRECTLY.
@ -132,16 +132,16 @@ syn case match
syn keyword vimFuncName contained abs acos add and append appendbufline argc argidx arglistid argv asin assert_beeps assert_equal assert_equalfile assert_exception assert_fails assert_false assert_inrange assert_match assert_nobeep assert_notequal assert_notmatch assert_report assert_true atan atan2 autocmd_add autocmd_delete autocmd_get balloon_gettext balloon_show balloon_split base64_decode base64_encode bindtextdomain blob2list blob2str browse browsedir bufadd bufexists buflisted bufload bufloaded bufname bufnr bufwinid bufwinnr byte2line byteidx byteidxcomp call ceil ch_canread ch_close ch_close_in ch_evalexpr ch_evalraw ch_getbufnr ch_getjob ch_info ch_log ch_logfile ch_open ch_read ch_readblob ch_readraw ch_sendexpr ch_sendraw ch_setoptions ch_status changenr
syn keyword vimFuncName contained char2nr charclass charcol charidx chdir cindent clearmatches col complete complete_add complete_check complete_info confirm copy cos cosh count cscope_connection cursor debugbreak deepcopy delete deletebufline did_filetype diff diff_filler diff_hlID digraph_get digraph_getlist digraph_set digraph_setlist echoraw empty environ err_teapot escape eval eventhandler executable execute exepath exists exists_compiled exp expand expandcmd extend extendnew feedkeys filecopy filereadable filewritable filter finddir findfile flatten flattennew float2nr floor fmod fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext foldtextresult foreach foreground fullcommand funcref function garbagecollect get getbufinfo getbufline getbufoneline
syn keyword vimFuncName contained getbufvar getcellpixels getcellwidths getchangelist getchar getcharmod getcharpos getcharsearch getcharstr getcmdcomplpat getcmdcompltype getcmdline getcmdpos getcmdprompt getcmdscreenpos getcmdtype getcmdwintype getcompletion getcurpos getcursorcharpos getcwd getenv getfontname getfperm getfsize getftime getftype getimstatus getjumplist getline getloclist getmarklist getmatches getmousepos getmouseshape getpid getpos getqflist getreg getreginfo getregion getregionpos getregtype getscriptinfo getstacktrace gettabinfo gettabvar gettabwinvar gettagstack gettext getwininfo getwinpos getwinposx getwinposy getwinvar glob glob2regpat globpath has has_key haslocaldir hasmapto histadd histdel histget histnr hlID hlexists hlget hlset hostname
syn keyword vimFuncName contained iconv id indent index indexof input inputdialog inputlist inputrestore inputsave inputsecret insert instanceof interrupt invert isabsolutepath isdirectory isinf islocked isnan items job_getchannel job_info job_setoptions job_start job_status job_stop join js_decode js_encode json_decode json_encode keys keytrans len libcall libcallnr line line2byte lispindent list2blob list2str listener_add listener_flush listener_remove localtime log log10 luaeval map maparg mapcheck maplist mapnew mapset match matchadd matchaddpos matcharg matchbufline matchdelete matchend matchfuzzy matchfuzzypos matchlist matchstr matchstrlist matchstrpos max menu_info min mkdir mode mzeval nextnonblank ngettext nr2char or pathshorten perleval popup_atcursor
syn keyword vimFuncName contained iconv id indent index indexof input inputdialog inputlist inputrestore inputsave inputsecret insert instanceof interrupt invert isabsolutepath isdirectory isinf islocked isnan items job_getchannel job_info job_setoptions job_start job_status job_stop join js_decode js_encode json_decode json_encode keys keytrans len libcall libcallnr line line2byte lispindent list2blob list2str list2tuple listener_add listener_flush listener_remove localtime log log10 luaeval map maparg mapcheck maplist mapnew mapset match matchadd matchaddpos matcharg matchbufline matchdelete matchend matchfuzzy matchfuzzypos matchlist matchstr matchstrlist matchstrpos max menu_info min mkdir mode mzeval nextnonblank ngettext nr2char or pathshorten perleval popup_atcursor
syn keyword vimFuncName contained popup_beval popup_clear popup_close popup_create popup_dialog popup_filter_menu popup_filter_yesno popup_findecho popup_findinfo popup_findpreview popup_getoptions popup_getpos popup_hide popup_list popup_locate popup_menu popup_move popup_notification popup_setbuf popup_setoptions popup_settext popup_show pow prevnonblank printf prompt_getprompt prompt_setcallback prompt_setinterrupt prompt_setprompt prop_add prop_add_list prop_clear prop_find prop_list prop_remove prop_type_add prop_type_change prop_type_delete prop_type_get prop_type_list pum_getpos pumvisible py3eval pyeval pyxeval rand range readblob readdir readdirex readfile reduce reg_executing reg_recording reltime reltimefloat reltimestr remote_expr remote_foreground
syn keyword vimFuncName contained remote_peek remote_read remote_send remote_startserver remove rename repeat resolve reverse round rubyeval screenattr screenchar screenchars screencol screenpos screenrow screenstring search searchcount searchdecl searchpair searchpairpos searchpos server2client serverlist setbufline setbufvar setcellwidths setcharpos setcharsearch setcmdline setcmdpos setcursorcharpos setenv setfperm setline setloclist setmatches setpos setqflist setreg settabvar settabwinvar settagstack setwinvar sha256 shellescape shiftwidth sign_define sign_getdefined sign_getplaced sign_jump sign_place sign_placelist sign_undefine sign_unplace sign_unplacelist simplify sin sinh slice sort sound_clear sound_playevent sound_playfile sound_stop soundfold spellbadword
syn keyword vimFuncName contained spellsuggest split sqrt srand state str2blob str2float str2list str2nr strcharlen strcharpart strchars strdisplaywidth strftime strgetchar stridx string strlen strpart strptime strridx strtrans strutf16len strwidth submatch substitute swapfilelist swapinfo swapname synID synIDattr synIDtrans synconcealed synstack system systemlist tabpagebuflist tabpagenr tabpagewinnr tagfiles taglist tan tanh tempname term_dumpdiff term_dumpload term_dumpwrite term_getaltscreen term_getansicolors term_getattr term_getcursor term_getjob term_getline term_getscrolled term_getsize term_getstatus term_gettitle term_gettty term_list term_scrape term_sendkeys term_setansicolors term_setapi term_setkill term_setrestore term_setsize term_start term_wait
syn keyword vimFuncName contained terminalprops test_alloc_fail test_autochdir test_feedinput test_garbagecollect_now test_garbagecollect_soon test_getvalue test_gui_event test_ignore_error test_mswin_event test_null_blob test_null_channel test_null_dict test_null_function test_null_job test_null_list test_null_partial test_null_string test_option_not_set test_override test_refcount test_setmouse test_settime test_srand_seed test_unknown test_void timer_info timer_pause timer_start timer_stop timer_stopall tolower toupper tr trim trunc type typename undofile undotree uniq utf16idx values virtcol virtcol2col visualmode wildmenumode win_execute win_findbuf win_getid win_gettype win_gotoid win_id2tabwin win_id2win win_move_separator win_move_statusline win_screenpos
syn keyword vimFuncName contained win_splitmove winbufnr wincol windowsversion winheight winlayout winline winnr winrestcmd winrestview winsaveview winwidth wordcount writefile xor
syn keyword vimFuncName contained terminalprops test_alloc_fail test_autochdir test_feedinput test_garbagecollect_now test_garbagecollect_soon test_getvalue test_gui_event test_ignore_error test_mswin_event test_null_blob test_null_channel test_null_dict test_null_function test_null_job test_null_list test_null_partial test_null_string test_null_tuple test_option_not_set test_override test_refcount test_setmouse test_settime test_srand_seed test_unknown test_void timer_info timer_pause timer_start timer_stop timer_stopall tolower toupper tr trim trunc tuple2list type typename undofile undotree uniq utf16idx values virtcol virtcol2col visualmode wildmenumode win_execute win_findbuf win_getid win_gettype win_gotoid win_id2tabwin win_id2win win_move_separator win_move_statusline
syn keyword vimFuncName contained win_screenpos win_splitmove winbufnr wincol windowsversion winheight winlayout winline winnr winrestcmd winrestview winsaveview winwidth wordcount writefile xor
" Predefined variable names {{{2
" GEN_SYN_VIM: vimVarName, START_STR='syn keyword vimVimVarName contained', END_STR=''
syn keyword vimVimVarName contained count count1 prevcount errmsg warningmsg statusmsg shell_error this_session version lnum termresponse fname lang lc_time ctype charconvert_from charconvert_to fname_in fname_out fname_new fname_diff cmdarg foldstart foldend folddashes foldlevel progname servername dying exception throwpoint register cmdbang insertmode val key profiling fcs_reason fcs_choice beval_bufnr beval_winnr beval_winid beval_lnum beval_col beval_text scrollstart swapname swapchoice swapcommand char mouse_win mouse_winid mouse_lnum mouse_col operator searchforward hlsearch oldfiles windowid progpath completed_item option_new option_old option_oldlocal option_oldglobal option_command option_type errors false true none null numbermax numbermin numbersize
syn keyword vimVimVarName contained vim_did_enter testing t_number t_string t_func t_list t_dict t_float t_bool t_none t_job t_channel t_blob t_class t_object termrfgresp termrbgresp termu7resp termstyleresp termblinkresp event versionlong echospace argv collate exiting colornames sizeofint sizeoflong sizeofpointer maxcol python3_version t_typealias t_enum t_enumvalue stacktrace
syn keyword vimVimVarName contained vim_did_enter testing t_number t_string t_func t_list t_dict t_float t_bool t_none t_job t_channel t_blob t_class t_object termrfgresp termrbgresp termu7resp termstyleresp termblinkresp event versionlong echospace argv collate exiting colornames sizeofint sizeoflong sizeofpointer maxcol python3_version t_typealias t_enum t_enumvalue stacktrace t_tuple
"--- syntax here and above generated by runtime/syntax/generator/gen_syntax_vim.vim ---

View File

@ -2050,7 +2050,7 @@ get_lval_subscript(
var2.v_type = VAR_UNKNOWN;
}
if (lp->ll_tuple != NULL)
if (lp->ll_tuple != NULL && (flags & GLV_READ_ONLY) == 0)
{
if (!quiet)
emsg(_(e_tuple_is_immutable));

View File

@ -8632,6 +8632,9 @@ f_islocked(typval_T *argvars, typval_T *rettv)
else if (lv.ll_list != NULL)
// List item.
rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv);
else if (lv.ll_tuple != NULL)
// Tuple item.
rettv->vval.v_number = tv_islocked(lv.ll_tv);
else
// Dictionary item.
rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv);

View File

@ -289,7 +289,6 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name);
# define ex_endif ex_ni
# define ex_endtry ex_ni
# define ex_endwhile ex_ni
# define ex_enum ex_ni
# define ex_eval ex_ni
# define ex_execute ex_ni
# define ex_finally ex_ni

View File

@ -438,6 +438,21 @@ set_ref_in_list_items(
return abort;
}
/*
* Mark a tuple and its items with "copyID".
* Returns TRUE if setting references failed somehow.
*/
int
set_ref_in_tuple(tuple_T *tuple, int copyID)
{
if (tuple != NULL && tuple->tv_copyID != copyID)
{
tuple->tv_copyID = copyID;
return set_ref_in_tuple_items(tuple, copyID, NULL, NULL);
}
return FALSE;
}
/*
* Mark all lists and dicts referenced through tuple "t" with "copyID".
* "ht_stack" is used to add hashtabs to be marked. Can be NULL.

View File

@ -152,14 +152,15 @@ static PyObject* Vim_PyObject_New(PyTypeObject *type, size_t objsize)
# define PyList_GET_ITEM(list, i) PyList_GetItem(list, i)
# define PyList_GET_SIZE(o) PyList_Size(o)
# define PyTuple_GET_ITEM(o, pos) PyTuple_GetItem(o, pos)
# define PyTuple_GET_SIZE(o) PyTuple_Size(o)
// PyList_SET_ITEM and PyList_SetItem have slightly different behaviors. The
// former will leave the old item dangling, and the latter will decref on it.
// Since we only use this on new lists, this difference doesn't matter.
# define PyList_SET_ITEM(list, i, item) PyList_SetItem(list, i, item)
# define PyTuple_GET_ITEM(o, pos) PyTuple_GetItem(o, pos)
# define PyTuple_GET_SIZE(o) PyTuple_Size(o)
# if Py_LIMITED_API < 0x03080000
// PyIter_check only became part of stable ABI in 3.8, and there is no easy way
// to check for it in the API. We simply return false as a compromise. This
@ -1014,11 +1015,14 @@ VimToPython(typval_T *our_tv, int depth, PyObject *lookup_dict)
// Check if we run into a recursive loop. The item must be in lookup_dict
// then and we can use it again.
if ((our_tv->v_type == VAR_LIST && our_tv->vval.v_list != NULL)
|| (our_tv->v_type == VAR_TUPLE && our_tv->vval.v_tuple != NULL)
|| (our_tv->v_type == VAR_DICT && our_tv->vval.v_dict != NULL))
{
sprintf(ptrBuf, "%p",
our_tv->v_type == VAR_LIST ? (void *)our_tv->vval.v_list
: (void *)our_tv->vval.v_dict);
: our_tv->v_type == VAR_TUPLE ?
(void *)our_tv->vval.v_tuple
: (void *)our_tv->vval.v_dict);
if ((ret = PyDict_GetItemString(lookup_dict, ptrBuf)))
{
@ -1079,6 +1083,39 @@ VimToPython(typval_T *our_tv, int depth, PyObject *lookup_dict)
Py_DECREF(newObj);
}
}
else if (our_tv->v_type == VAR_TUPLE)
{
tuple_T *tuple = our_tv->vval.v_tuple;
int len;
if (tuple == NULL)
return NULL;
len = TUPLE_LEN(tuple);
ret = PyTuple_New(len);
if (ret == NULL)
return NULL;
if (PyDict_SetItemString(lookup_dict, ptrBuf, ret))
{
Py_DECREF(ret);
return NULL;
}
for (int idx = 0; idx < len; idx++)
{
typval_T *item_tv = TUPLE_ITEM(tuple, idx);
newObj = VimToPython(item_tv, depth + 1, lookup_dict);
if (!newObj)
{
Py_DECREF(ret);
return NULL;
}
PyTuple_SET_ITEM(ret, idx, newObj);
}
}
else if (our_tv->v_type == VAR_DICT)
{
@ -1800,6 +1837,7 @@ typedef struct pylinkedlist_S {
static pylinkedlist_T *lastdict = NULL;
static pylinkedlist_T *lastlist = NULL;
static pylinkedlist_T *lasttuple = NULL;
static pylinkedlist_T *lastfunc = NULL;
static void
@ -3216,6 +3254,355 @@ static struct PyMethodDef ListMethods[] = {
{ NULL, NULL, 0, NULL}
};
DEFINE_PY_TYPE_OBJECT(TupleType);
typedef struct
{
PyObject_HEAD
tuple_T *tuple;
pylinkedlist_T ref;
} TupleObject;
#define NEW_TUPLE(tuple) TupleNew(TupleTypePtr, tuple)
static PyObject *
TupleNew(PyTypeObject *subtype, tuple_T *tuple)
{
TupleObject *self;
if (tuple == NULL)
return NULL;
self = (TupleObject *) Py_TYPE_GET_TP_ALLOC(subtype)(subtype, 0);
if (self == NULL)
return NULL;
self->tuple = tuple;
++tuple->tv_refcount;
pyll_add((PyObject *)(self), &self->ref, &lasttuple);
return (PyObject *)(self);
}
static tuple_T *
py_tuple_alloc(void)
{
tuple_T *ret;
if (!(ret = tuple_alloc()))
{
PyErr_NoMemory();
return NULL;
}
++ret->tv_refcount;
return ret;
}
static int
tuple_py_concat(tuple_T *t, PyObject *obj, PyObject *lookup_dict)
{
PyObject *iterator;
PyObject *item;
if (!(iterator = PyObject_GetIter(obj)))
return -1;
while ((item = PyIter_Next(iterator)))
{
typval_T new_tv;
if (_ConvertFromPyObject(item, &new_tv, lookup_dict) == -1)
{
Py_DECREF(item);
Py_DECREF(iterator);
return -1;
}
Py_DECREF(item);
if (tuple_append_tv(t, &new_tv) == FAIL)
{
Py_DECREF(iterator);
return -1;
}
}
Py_DECREF(iterator);
// Iterator may have finished due to an exception
if (PyErr_Occurred())
return -1;
return 0;
}
static PyObject *
TupleConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
{
tuple_T *tuple;
PyObject *obj = NULL;
if (kwargs)
{
PyErr_SET_STRING(PyExc_TypeError,
N_("tuple constructor does not accept keyword arguments"));
return NULL;
}
if (!PyArg_ParseTuple(args, "|O", &obj))
return NULL;
if (!(tuple = py_tuple_alloc()))
return NULL;
if (obj)
{
PyObject *lookup_dict;
if (!(lookup_dict = PyDict_New()))
{
tuple_unref(tuple);
return NULL;
}
if (tuple_py_concat(tuple, obj, lookup_dict) == -1)
{
Py_DECREF(lookup_dict);
tuple_unref(tuple);
return NULL;
}
Py_DECREF(lookup_dict);
}
return TupleNew(subtype, tuple);
}
static void
TupleDestructor(PyObject *self_obj)
{
TupleObject *self = (TupleObject *)self_obj;
pyll_remove(&self->ref, &lasttuple);
tuple_unref(self->tuple);
DESTRUCTOR_FINISH(self);
}
static PyInt
TupleLength(TupleObject *self)
{
return ((PyInt)(tuple_len(self->tuple)));
}
static PyObject *
TupleIndex(TupleObject *self, Py_ssize_t index)
{
PyInt len = TupleLength(self);
if (index < 0)
index = len + index;
if (index < 0 || index >= len)
{
PyErr_SET_STRING(PyExc_IndexError, N_("tuple index out of range"));
return NULL;
}
return ConvertToPyObject(TUPLE_ITEM(self->tuple, index));
}
/*
* Return a new tuple object for the tuple slice starting from the index
* "first" and of length "slicelen" skipping "step" items.
*/
static PyObject *
TupleSlice(
TupleObject *self,
Py_ssize_t first,
Py_ssize_t step,
Py_ssize_t slicelen)
{
PyInt i;
PyObject *tuple;
tuple = PyTuple_New(slicelen);
if (tuple == NULL)
return NULL;
for (i = 0; i < slicelen; ++i)
{
PyObject *item;
item = TupleIndex(self, first + i * step);
if (item == NULL)
{
Py_DECREF(tuple);
return NULL;
}
PyTuple_SET_ITEM(tuple, i, item);
}
return tuple;
}
static PyObject *
TupleItem(TupleObject *self, PyObject* idx)
{
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(idx))
{
long _idx = PyInt_AsLong(idx);
return TupleIndex(self, _idx);
}
else
#endif
if (PyLong_Check(idx))
{
long _idx = PyLong_AsLong(idx);
return TupleIndex(self, _idx);
}
else if (PySlice_Check(idx))
{
Py_ssize_t start, stop, step, slicelen;
if (PySlice_GetIndicesEx((PySliceObject_T *)idx, TupleLength(self),
&start, &stop, &step, &slicelen) < 0)
return NULL;
return TupleSlice(self, start, step, slicelen);
}
else
{
RAISE_INVALID_INDEX_TYPE(idx);
return NULL;
}
}
typedef struct
{
tuple_T *tuple;
int index;
} tupleiterinfo_T;
static void
TupleIterDestruct(void *arg)
{
tupleiterinfo_T *tii = (tupleiterinfo_T*)arg;
tuple_unref(tii->tuple);
PyMem_Free(tii);
}
static PyObject *
TupleIterNext(void **arg)
{
PyObject *ret;
tupleiterinfo_T **tii = (tupleiterinfo_T**)arg;
if ((*tii)->index >= TUPLE_LEN((*tii)->tuple))
return NULL;
if (!(ret = ConvertToPyObject(TUPLE_ITEM((*tii)->tuple, (*tii)->index))))
return NULL;
(*tii)->index++;
return ret;
}
static PyObject *
TupleIter(PyObject *self_obj)
{
TupleObject *self = (TupleObject*)self_obj;
tupleiterinfo_T *tii;
tuple_T *t = self->tuple;
if (!(tii = PyMem_New(tupleiterinfo_T, 1)))
{
PyErr_NoMemory();
return NULL;
}
tii->tuple = t;
tii->index = 0;
++t->tv_refcount;
return IterNew(tii,
TupleIterDestruct, TupleIterNext,
NULL, NULL, (PyObject *)self);
}
static char *TupleAttrs[] = {
"locked",
NULL
};
static PyObject *
TupleDir(PyObject *self, PyObject *args UNUSED)
{
return ObjectDir(self, TupleAttrs);
}
static int
TupleSetattr(PyObject *self_obj, char *name, PyObject *valObject)
{
TupleObject *self = (TupleObject*)self_obj;
if (valObject == NULL)
{
PyErr_SET_STRING(PyExc_AttributeError,
N_("cannot delete vim.Tuple attributes"));
return -1;
}
if (strcmp(name, "locked") == 0)
{
if (self->tuple->tv_lock == VAR_FIXED)
{
PyErr_SET_STRING(PyExc_TypeError, N_("cannot modify fixed tuple"));
return -1;
}
else
{
int istrue = PyObject_IsTrue(valObject);
if (istrue == -1)
return -1;
else if (istrue)
self->tuple->tv_lock = VAR_LOCKED;
else
self->tuple->tv_lock = 0;
}
return 0;
}
else
{
PyErr_FORMAT(PyExc_AttributeError, N_("cannot set attribute %s"), name);
return -1;
}
}
static PySequenceMethods TupleAsSeq = {
(lenfunc) TupleLength, // sq_length, len(x)
(binaryfunc) 0, // RangeConcat, sq_concat, x+y
0, // RangeRepeat, sq_repeat, x*n
(PyIntArgFunc) TupleIndex, // sq_item, x[i]
0, // was_sq_slice, x[i:j]
(PyIntObjArgProc) 0, // sq_as_item, x[i]=v
0, // was_sq_ass_slice, x[i:j]=v
0, // sq_contains
(binaryfunc) 0, // sq_inplace_concat
0, // sq_inplace_repeat
};
static PyMappingMethods TupleAsMapping = {
/* mp_length */ (lenfunc) TupleLength,
/* mp_subscript */ (binaryfunc) TupleItem,
/* mp_ass_subscript */ (objobjargproc) 0,
};
static struct PyMethodDef TupleMethods[] = {
{"__dir__", (PyCFunction)TupleDir, METH_NOARGS, ""},
{ NULL, NULL, 0, NULL}
};
typedef struct
{
PyObject_HEAD
@ -6219,6 +6606,7 @@ set_ref_in_py(const int copyID)
{
pylinkedlist_T *cur;
list_T *ll;
tuple_T *tt;
int i;
int abort = FALSE;
FunctionObject *func;
@ -6239,6 +6627,15 @@ set_ref_in_py(const int copyID)
}
}
if (lasttuple != NULL)
{
for (cur = lasttuple ; !abort && cur != NULL ; cur = cur->pll_prev)
{
tt = ((TupleObject *) (cur->pll_obj))->tuple;
abort = set_ref_in_tuple(tt, copyID);
}
}
if (lastfunc != NULL)
{
for (cur = lastfunc ; !abort && cur != NULL ; cur = cur->pll_prev)
@ -6461,6 +6858,27 @@ pyseq_to_tv(PyObject *obj, typval_T *tv, PyObject *lookup_dict)
return 0;
}
static int
pytuple_to_tv(PyObject *obj, typval_T *tv, PyObject *lookup_dict)
{
tuple_T *t;
if (!(t = py_tuple_alloc()))
return -1;
tv->v_type = VAR_TUPLE;
tv->vval.v_tuple = t;
if (tuple_py_concat(t, obj, lookup_dict) == -1)
{
tuple_unref(t);
return -1;
}
--t->tv_refcount;
return 0;
}
typedef int (*pytotvfunc)(PyObject *, typval_T *, PyObject *);
static int
@ -6504,6 +6922,8 @@ convert_dl(PyObject *obj, typval_T *tv,
++tv->vval.v_dict->dv_refcount;
else if (tv->v_type == VAR_LIST)
++tv->vval.v_list->lv_refcount;
else if (tv->v_type == VAR_TUPLE)
++tv->vval.v_tuple->tv_refcount;
}
else
{
@ -6607,6 +7027,12 @@ _ConvertFromPyObject(PyObject *obj, typval_T *tv, PyObject *lookup_dict)
tv->vval.v_list = (((ListObject *)(obj))->list);
++tv->vval.v_list->lv_refcount;
}
else if (PyType_IsSubtype(obj->ob_type, TupleTypePtr))
{
tv->v_type = VAR_TUPLE;
tv->vval.v_tuple = (((TupleObject *)(obj))->tuple);
++tv->vval.v_tuple->tv_refcount;
}
else if (PyType_IsSubtype(obj->ob_type, FunctionTypePtr))
{
FunctionObject *func = (FunctionObject *) obj;
@ -6680,6 +7106,8 @@ _ConvertFromPyObject(PyObject *obj, typval_T *tv, PyObject *lookup_dict)
if (PyErr_Occurred())
return -1;
}
else if (PyTuple_Check(obj))
return convert_dl(obj, tv, pytuple_to_tv, lookup_dict);
else if (PyDict_Check(obj))
return convert_dl(obj, tv, pydict_to_tv, lookup_dict);
else if (PyFloat_Check(obj))
@ -6742,6 +7170,8 @@ ConvertToPyObject(typval_T *tv)
return PyFloat_FromDouble((double) tv->vval.v_float);
case VAR_LIST:
return NEW_LIST(tv->vval.v_list);
case VAR_TUPLE:
return NEW_TUPLE(tv->vval.v_tuple);
case VAR_DICT:
return NEW_DICTIONARY(tv->vval.v_dict);
case VAR_FUNC:
@ -6777,7 +7207,6 @@ ConvertToPyObject(typval_T *tv)
case VAR_CLASS:
case VAR_OBJECT:
case VAR_TYPEALIAS:
case VAR_TUPLE: // FIXME: Need to add support for tuple
Py_INCREF(Py_None);
return Py_None;
case VAR_BOOL:
@ -7002,6 +7431,27 @@ init_structs(void)
ListType.tp_setattr = ListSetattr;
#endif
// Tuple type
CLEAR_FIELD(TupleType);
TupleType.tp_name = "vim.tuple";
TupleType.tp_dealloc = TupleDestructor;
TupleType.tp_basicsize = sizeof(TupleObject);
TupleType.tp_as_sequence = &TupleAsSeq;
TupleType.tp_as_mapping = &TupleAsMapping;
TupleType.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE;
TupleType.tp_doc = "tuple pushing modifications to Vim structure";
TupleType.tp_methods = TupleMethods;
TupleType.tp_iter = TupleIter;
TupleType.tp_new = TupleConstructor;
TupleType.tp_alloc = PyType_GenericAlloc;
#if PY_MAJOR_VERSION >= 3
TupleType.tp_getattro = TupleGetattro;
TupleType.tp_setattro = TupleSetattro;
#else
TupleType.tp_getattr = TupleGetattr;
TupleType.tp_setattr = TupleSetattr;
#endif
CLEAR_FIELD(FunctionType);
FunctionType.tp_name = "vim.function";
FunctionType.tp_basicsize = sizeof(FunctionObject);
@ -7064,6 +7514,7 @@ init_types(void)
PYTYPE_READY(CurrentType);
PYTYPE_READY(DictionaryType);
PYTYPE_READY(ListType);
PYTYPE_READY(TupleType);
PYTYPE_READY(FunctionType);
PYTYPE_READY(OptionsType);
PYTYPE_READY(OutputType);
@ -7101,6 +7552,7 @@ shutdown_types(void)
PYTYPE_CLEANUP(CurrentType);
PYTYPE_CLEANUP(DictionaryType);
PYTYPE_CLEANUP(ListType);
PYTYPE_CLEANUP(TupleType);
PYTYPE_CLEANUP(FunctionType);
PYTYPE_CLEANUP(OptionsType);
PYTYPE_CLEANUP(OutputType);
@ -7239,6 +7691,7 @@ populate_module(PyObject *m)
{"TabPage", (PyObject *)TabPageTypePtr},
{"Dictionary", (PyObject *)DictionaryTypePtr},
{"List", (PyObject *)ListTypePtr},
{"Tuple", (PyObject *)TupleTypePtr},
{"Function", (PyObject *)FunctionTypePtr},
{"Options", (PyObject *)OptionsTypePtr},
#if PY_VERSION_HEX < 0x030700f0

View File

@ -201,6 +201,10 @@ struct PyMethodDef { Py_ssize_t a; };
# define PyList_SetItem dll_PyList_SetItem
# define PyList_Size dll_PyList_Size
# define PyList_Type (*dll_PyList_Type)
# define PyTuple_GetItem dll_PyTuple_GetItem
# define PyTuple_New dll_PyTuple_New
# define PyTuple_Size dll_PyTuple_Size
# define PyTuple_Type (*dll_PyTuple_Type)
# define PySequence_Check dll_PySequence_Check
# define PySequence_Size dll_PySequence_Size
# define PySequence_GetItem dll_PySequence_GetItem
@ -352,13 +356,16 @@ static PyObject*(*dll_PyList_New)(PyInt size);
static int(*dll_PyList_SetItem)(PyObject *, PyInt, PyObject *);
static PyInt(*dll_PyList_Size)(PyObject *);
static PyTypeObject* dll_PyList_Type;
static PyObject*(*dll_PyTuple_GetItem)(PyObject *, PyInt);
static int(*dll_PyTuple_SetItem)(PyObject *, PyInt, PyObject *);
static int(*dll_PyTuple_SET_ITEM)(PyObject *, PyInt, PyObject *);
static PyObject*(*dll_PyTuple_New)(PyInt size);
static PyInt(*dll_PyTuple_Size)(PyObject *);
static PyTypeObject* dll_PyTuple_Type;
static int (*dll_PySequence_Check)(PyObject *);
static PyInt(*dll_PySequence_Size)(PyObject *);
static PyObject*(*dll_PySequence_GetItem)(PyObject *, PyInt);
static PyObject*(*dll_PySequence_Fast)(PyObject *, const char *);
static PyInt(*dll_PyTuple_Size)(PyObject *);
static PyObject*(*dll_PyTuple_GetItem)(PyObject *, PyInt);
static PyTypeObject* dll_PyTuple_Type;
static int (*dll_PySlice_GetIndicesEx)(PySliceObject *r, PyInt length,
PyInt *start, PyInt *stop, PyInt *step,
PyInt *slicelen);
@ -540,6 +547,12 @@ static struct
{"PyList_SetItem", (PYTHON_PROC*)&dll_PyList_SetItem},
{"PyList_Size", (PYTHON_PROC*)&dll_PyList_Size},
{"PyList_Type", (PYTHON_PROC*)&dll_PyList_Type},
{"PyTuple_GetItem", (PYTHON_PROC*)&dll_PyTuple_GetItem},
{"PyTuple_SetItem", (PYTHON_PROC*)&dll_PyTuple_SetItem},
{"PyTuple_SET_ITEM", (PYTHON_PROC*)&dll_PyTuple_SET_ITEM},
{"PyTuple_New", (PYTHON_PROC*)&dll_PyTuple_New},
{"PyTuple_Size", (PYTHON_PROC*)&dll_PyTuple_Size},
{"PyTuple_Type", (PYTHON_PROC*)&dll_PyTuple_Type},
{"PySequence_Size", (PYTHON_PROC*)&dll_PySequence_Size},
{"PySequence_Check", (PYTHON_PROC*)&dll_PySequence_Check},
{"PySequence_GetItem", (PYTHON_PROC*)&dll_PySequence_GetItem},
@ -786,6 +799,7 @@ static PyObject *TabPageGetattr(PyObject *, char *);
static PyObject *RangeGetattr(PyObject *, char *);
static PyObject *DictionaryGetattr(PyObject *, char*);
static PyObject *ListGetattr(PyObject *, char *);
static PyObject *TupleGetattr(PyObject *, char *);
static PyObject *FunctionGetattr(PyObject *, char *);
#ifndef Py_VISIT
@ -1510,6 +1524,17 @@ ListGetattr(PyObject *self, char *name)
return Py_FindMethod(ListMethods, self, name);
}
static PyObject *
TupleGetattr(PyObject *self, char *name)
{
if (strcmp(name, "locked") == 0)
return PyInt_FromLong(((TupleObject *)(self))->tuple->tv_lock);
else if (strcmp(name, "__members__") == 0)
return ObjectDir(NULL, TupleAttrs);
return Py_FindMethod(TupleMethods, self, name);
}
static PyObject *
FunctionGetattr(PyObject *self, char *name)
{

View File

@ -187,12 +187,16 @@ static HINSTANCE hinstPy3 = 0; // Instance of python.dll
# define PyList_New py3_PyList_New
# define PyList_SetItem py3_PyList_SetItem
# define PyList_Size py3_PyList_Size
# define PyTuple_New py3_PyTuple_New
# define PyTuple_GetItem py3_PyTuple_GetItem
# define PyTuple_SetItem py3_PyTuple_SetItem
# undef PyTuple_SET_ITEM
# define PyTuple_SET_ITEM py3_PyTuple_SET_ITEM
# define PyTuple_Size py3_PyTuple_Size
# define PySequence_Check py3_PySequence_Check
# define PySequence_Size py3_PySequence_Size
# define PySequence_GetItem py3_PySequence_GetItem
# define PySequence_Fast py3_PySequence_Fast
# define PyTuple_Size py3_PyTuple_Size
# define PyTuple_GetItem py3_PyTuple_GetItem
# if PY_VERSION_HEX >= 0x030601f0
# define PySlice_AdjustIndices py3_PySlice_AdjustIndices
# define PySlice_Unpack py3_PySlice_Unpack
@ -371,11 +375,14 @@ static PyObject* (*py3_PySys_GetObject)(char *);
static int (*py3_PyList_Append)(PyObject *, PyObject *);
static int (*py3_PyList_Insert)(PyObject *, int, PyObject *);
static Py_ssize_t (*py3_PyList_Size)(PyObject *);
static PyObject* (*py3_PyTuple_New)(Py_ssize_t size);
static int (*py3_PyTuple_SetItem)(PyObject *, Py_ssize_t, PyObject *);
static int (*py3_PyTuple_SET_ITEM)(PyObject *, Py_ssize_t, PyObject *);
static Py_ssize_t (*py3_PyTuple_Size)(PyObject *);
static int (*py3_PySequence_Check)(PyObject *);
static Py_ssize_t (*py3_PySequence_Size)(PyObject *);
static PyObject* (*py3_PySequence_GetItem)(PyObject *, Py_ssize_t);
static PyObject* (*py3_PySequence_Fast)(PyObject *, const char *);
static Py_ssize_t (*py3_PyTuple_Size)(PyObject *);
static PyObject* (*py3_PyTuple_GetItem)(PyObject *, Py_ssize_t);
static int (*py3_PyMapping_Check)(PyObject *);
static PyObject* (*py3_PyMapping_Keys)(PyObject *);
@ -585,12 +592,15 @@ static struct
{"PyList_Append", (PYTHON_PROC*)&py3_PyList_Append},
{"PyList_Insert", (PYTHON_PROC*)&py3_PyList_Insert},
{"PyList_Size", (PYTHON_PROC*)&py3_PyList_Size},
{"PyTuple_New", (PYTHON_PROC*)&py3_PyTuple_New},
{"PyTuple_GetItem", (PYTHON_PROC*)&py3_PyTuple_GetItem},
{"PyTuple_SetItem", (PYTHON_PROC*)&py3_PyTuple_SetItem},
{"PyTuple_SET_ITEM", (PYTHON_PROC*)&py3_PyTuple_SET_ITEM},
{"PyTuple_Size", (PYTHON_PROC*)&py3_PyTuple_Size},
{"PySequence_Check", (PYTHON_PROC*)&py3_PySequence_Check},
{"PySequence_Size", (PYTHON_PROC*)&py3_PySequence_Size},
{"PySequence_GetItem", (PYTHON_PROC*)&py3_PySequence_GetItem},
{"PySequence_Fast", (PYTHON_PROC*)&py3_PySequence_Fast},
{"PyTuple_Size", (PYTHON_PROC*)&py3_PyTuple_Size},
{"PyTuple_GetItem", (PYTHON_PROC*)&py3_PyTuple_GetItem},
# if PY_VERSION_HEX >= 0x030601f0
{"PySlice_AdjustIndices", (PYTHON_PROC*)&py3_PySlice_AdjustIndices},
{"PySlice_Unpack", (PYTHON_PROC*)&py3_PySlice_Unpack},
@ -1113,6 +1123,8 @@ static PyObject *DictionaryGetattro(PyObject *, PyObject *);
static int DictionarySetattro(PyObject *, PyObject *, PyObject *);
static PyObject *ListGetattro(PyObject *, PyObject *);
static int ListSetattro(PyObject *, PyObject *, PyObject *);
static PyObject *TupleGetattro(PyObject *, PyObject *);
static int TupleSetattro(PyObject *, PyObject *, PyObject *);
static PyObject *FunctionGetattro(PyObject *, PyObject *);
static struct PyModuleDef vimmodule;
@ -2028,6 +2040,26 @@ ListSetattro(PyObject *self, PyObject *nameobj, PyObject *val)
return ListSetattr(self, name, val);
}
// Tuple object - Definitions
static PyObject *
TupleGetattro(PyObject *self, PyObject *nameobj)
{
GET_ATTR_STRING(name, nameobj);
if (strcmp(name, "locked") == 0)
return PyLong_FromLong(((TupleObject *) (self))->tuple->tv_lock);
return PyObject_GenericGetAttr(self, nameobj);
}
static int
TupleSetattro(PyObject *self, PyObject *nameobj, PyObject *val)
{
GET_ATTR_STRING(name, nameobj);
return TupleSetattr(self, name, val);
}
// Function object - Definitions
static PyObject *

View File

@ -5,6 +5,7 @@ int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack, tuple_st
int set_ref_in_dict(dict_T *d, int copyID);
int set_ref_in_list(list_T *ll, int copyID);
int set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack, tuple_stack_T **tuple_stack);
int set_ref_in_tuple(tuple_T *tuple, int copyID);
int set_ref_in_tuple_items(tuple_T *tuple, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
int set_ref_in_callback(callback_T *cb, int copyID);
int set_ref_in_item_class(class_T *cl, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack, tuple_stack_T **tuple_stack);

View File

@ -406,6 +406,107 @@ func Test_python_list()
\ 'Vim(python):TypeError: index must be int or slice, not dict')
endfunc
" Test for the python Tuple object
func Test_python_tuple()
" Try to convert a null tuple
call AssertException(["py l = vim.eval('test_null_tuple()')"],
\ 'Vim(python):SystemError: error return without exception set')
" Try to convert a Tuple with a null Tuple item
call AssertException(["py t = vim.eval('(test_null_tuple(),)')"],
\ 'Vim(python):SystemError: error return without exception set')
" Try to convert a List with a null Tuple item
call AssertException(["py t = vim.eval('[test_null_tuple()]')"],
\ 'Vim(python):SystemError: error return without exception set')
" Try to convert a Tuple with a null List item
call AssertException(["py t = vim.eval('(test_null_list(),)')"],
\ 'Vim(python):SystemError: error return without exception set')
" Try to bind a null Tuple variable (works because an empty tuple is used)
let cmds =<< trim END
let t = test_null_tuple()
py tt = vim.bindeval('t')
END
call AssertException(cmds, '')
" Creating a tuple using different iterators
py t1 = vim.Tuple(['abc', 20, 1.2, (4, 5)])
call assert_equal(('abc', 20, 1.2, (4, 5)), pyeval('t1'))
py t2 = vim.Tuple('abc')
call assert_equal(('a', 'b', 'c'), pyeval('t2'))
py t3 = vim.Tuple({'color': 'red', 'model': 'ford'})
call assert_equal(('color', 'model'), pyeval('t3'))
py t4 = vim.Tuple()
call assert_equal((), pyeval('t4'))
py t5 = vim.Tuple(x**2 for x in range(5))
call assert_equal((0, 1, 4, 9, 16), pyeval('t5'))
py t6 = vim.Tuple(('abc', 20, 1.2, (4, 5)))
call assert_equal(('abc', 20, 1.2, (4, 5)), pyeval('t6'))
" Convert between Vim tuple/list and python tuple/list
py t = vim.Tuple(vim.bindeval("('a', ('b',), ['c'], {'s': 'd'})"))
call assert_equal(('a', ('b',), ['c'], {'s': 'd'}), pyeval('t'))
call assert_equal(['a', ('b',), ['c'], {'s': 'd'}], pyeval('list(t)'))
call assert_equal(('a', ('b',), ['c'], {'s': 'd'}), pyeval('tuple(t)'))
py l = vim.List(vim.bindeval("['e', ('f',), ['g'], {'s': 'h'}]"))
call assert_equal(('e', ('f',), ['g'], {'s': 'h'}), pyeval('tuple(l)'))
" Tuple assignment
py tt = vim.bindeval('("a", "b")')
call AssertException(['py tt[0] = 10'],
\ "Vim(python):TypeError: 'vim.tuple' object does not support item assignment")
py tt = vim.bindeval('("a", "b")')
call AssertException(['py tt[0:1] = (10, 20)'],
\ "Vim(python):TypeError: 'vim.tuple' object does not support item assignment")
" iterating over tuple from Python
py print([x for x in vim.bindeval("('a', 'b')")])
" modifying a list item within a tuple
let t = ('a', ['b', 'c'], 'd')
py vim.bindeval('t')[1][1] = 'x'
call assert_equal(('a', ['b', 'x'], 'd'), t)
" length of a tuple
let t = ()
py p_t = vim.bindeval('t')
call assert_equal(0, pyeval('len(p_t)'))
let t = ('a', )
py p_t = vim.bindeval('t')
call assert_equal(1, pyeval('len(p_t)'))
let t = ('a', 'b', 'c')
py p_t = vim.bindeval('t')
call assert_equal(3, pyeval('len(p_t)'))
" membership test
let t = ('a', 'b', 'c')
py p_t = vim.bindeval('t')
call assert_true(pyeval("b'c' in p_t"))
call assert_true(pyeval("b'd' not in p_t"))
py x = vim.eval('("a", (2), [3], {})')
call assert_equal(('a', '2', ['3'], {}), pyeval('x'))
" Using a keyword argument for a tuple
call AssertException(['py x = vim.Tuple(a=1)'],
\ 'Vim(python):TypeError: tuple constructor does not accept keyword arguments')
" Using dict as an index
call AssertException(['py x = tt[{}]'],
\ 'Vim(python):TypeError: index must be int or slice, not dict')
call AssertException(['py x = tt["abc"]'],
\ 'Vim(python):TypeError: index must be int or slice, not str')
call AssertException(['py del tt.locked'],
\ 'Vim(python):AttributeError: cannot delete vim.Tuple attributes')
call AssertException(['py tt.foobar = 1'],
\ 'Vim(python):AttributeError: cannot set attribute foobar')
endfunc
" Test for the python Dict object
func Test_python_dict()
let d = {}
@ -787,11 +888,24 @@ func Test_python_lock_scope_attr()
\ 'Vim(python):TypeError: cannot modify fixed list')
endfunc
" Test for locking/unlocking a tuple
func Test_tuple_lock()
let t = (1, 2, 3)
py t = vim.bindeval('t')
py t.locked = True
call assert_equal(1, islocked('t'))
py t.locked = False
call assert_equal(0, islocked('t'))
endfunc
" Test for pyeval()
func Test_python_pyeval()
let l = pyeval('range(3)')
call assert_equal([0, 1, 2], l)
let t = pyeval('("a", "b", "c")')
call assert_equal(("a", "b", "c"), t)
let d = pyeval('{"a": "b", "c": 1, "d": ["e"]}')
call assert_equal([['a', 'b'], ['c', 1], ['d', ['e']]], sort(items(d)))
@ -812,18 +926,20 @@ func Test_python_pyeval()
call AssertException(['let v = pyeval("vim")'], 'E859:')
endfunc
" Test for py3eval with locals
" Test for pyeval with locals
func Test_python_pyeval_locals()
let str = 'a string'
let num = 0xbadb33f
let d = {'a': 1, 'b': 2, 'c': str}
let l = [ str, num, d ]
let t = ( str, num, d )
let locals = #{
\ s: str,
\ n: num,
\ d: d,
\ l: l,
\ t: t,
\ }
" check basics
@ -831,6 +947,8 @@ func Test_python_pyeval_locals()
call assert_equal(0xbadb33f, pyeval('n', locals))
call assert_equal(d, pyeval('d', locals))
call assert_equal(l, pyeval('l', locals))
call assert_equal(t, pyeval('t', locals))
call assert_equal('a-b-c', 'b"-".join(t)'->pyeval({'t': ('a', 'b', 'c')}))
py << trim EOF
def __UpdateDict(d, upd):
@ -997,6 +1115,92 @@ func Test_python_list_slice()
\ 'Vim(python):SystemError: error return without exception set')
endfunc
" Slice
func Test_python_tuple_slice()
py tt = vim.bindeval('(0, 1, 2, 3, 4, 5)')
py t = tt[:4]
call assert_equal((0, 1, 2, 3), pyeval('t'))
py t = tt[2:]
call assert_equal((2, 3, 4, 5), pyeval('t'))
py t = tt[:-4]
call assert_equal((0, 1), pyeval('t'))
py t = tt[-2:]
call assert_equal((4, 5), pyeval('t'))
py t = tt[2:4]
call assert_equal((2, 3), pyeval('t'))
py t = tt[4:2]
call assert_equal((), pyeval('t'))
py t = tt[-4:-2]
call assert_equal((2, 3), pyeval('t'))
py t = tt[-2:-4]
call assert_equal((), pyeval('t'))
py t = tt[:]
call assert_equal((0, 1, 2, 3, 4, 5), pyeval('t'))
py t = tt[0:6]
call assert_equal((0, 1, 2, 3, 4, 5), pyeval('t'))
py t = tt[-10:10]
call assert_equal((0, 1, 2, 3, 4, 5), pyeval('t'))
py t = tt[4:2:-1]
call assert_equal((4, 3), pyeval('t'))
py t = tt[::2]
call assert_equal((0, 2, 4), pyeval('t'))
py t = tt[4:2:1]
call assert_equal((), pyeval('t'))
" Error case: Use an invalid index
call AssertException(['py x = tt[-10]'], 'Vim(python):IndexError: tuple index out of range')
" Use a step value of 0
call AssertException(['py x = tt[0:3:0]'],
\ 'Vim(python):ValueError: slice step cannot be zero')
" Error case: Invalid slice type
call AssertException(["py x = tt['abc']"],
\ "Vim(python):TypeError: index must be int or slice, not str")
" Error case: List with a null tuple item
let t = (test_null_tuple(),)
py tt = vim.bindeval('t')
call AssertException(["py x = tt[:]"], 'Vim(python):SystemError: error return without exception set')
endfunc
func Test_python_pytuple_to_vimtuple()
let t = pyeval("('a', 'b')")
call assert_equal(('a', 'b'), t)
let t = pyeval("()")
call assert_equal((), t)
let t = pyeval("('x',)")
call assert_equal(('x',), t)
let t = pyeval("((1, 2), (), (3, 4))")
call assert_equal(((1, 2), (), (3, 4)), t)
let t = pyeval("((1, 2), {'a': 10}, [5, 6])")
call assert_equal(((1, 2), {'a': 10}, [5, 6]), t)
" Invalid python tuple
py << trim END
class FailingIter(object):
def __iter__(self):
raise NotImplementedError('iter')
END
call assert_fails('call pyeval("(1, FailingIter, 2)")',
\ 'E859: Failed to convert returned python object to a Vim value')
py del FailingIter
endfunc
" Test for tuple garbage collection
func Test_python_tuple_garbage_collect()
let t = (1, (2, 3), [4, 5], {'a': 6})
py py_t = vim.bindeval('t')
let save_testing = v:testing
let v:testing = 1
call test_garbagecollect_now()
let v:testing = save_testing
let new_t = pyeval('py_t')
call assert_equal((1, (2, 3), [4, 5], {'a': 6}), new_t)
endfunc
" Vars
func Test_python_vars()
let g:foo = 'bac'
@ -1976,6 +2180,7 @@ func Test_python_dir_method()
('range', vim.current.range),
('dictionary', vim.bindeval('{}')),
('list', vim.bindeval('[]')),
('tuple', vim.bindeval('()')),
('function', vim.bindeval('function("tr")')),
('output', sys.stdout),
):
@ -1991,6 +2196,7 @@ func Test_python_dir_method()
range:__dir__,__members__,append,end,start
dictionary:__dir__,__members__,get,has_key,items,keys,locked,pop,popitem,scope,update,values
list:__dir__,__members__,extend,locked
tuple:__dir__,__members__,locked
function:__dir__,__members__,args,auto_rebind,self,softspace
output:__dir__,__members__,close,closed,flush,isatty,readable,seekable,softspace,writable,write,writelines
END
@ -2003,7 +2209,9 @@ func Test_python_new()
call assert_equal({'a': 1}, pyeval('vim.Dictionary(a=1)'))
call assert_equal({'a': 1}, pyeval('vim.Dictionary(((''a'', 1),))'))
call assert_equal([], pyeval('vim.List()'))
call assert_equal((), pyeval('vim.Tuple()'))
call assert_equal(['a', 'b', 'c', '7'], pyeval('vim.List(iter(''abc7''))'))
call assert_equal(('a', 'b', 'c', '7'), pyeval('vim.Tuple(iter(''abc7''))'))
call assert_equal(function('tr'), pyeval('vim.Function(''tr'')'))
call assert_equal(function('tr', [123, 3, 4]),
\ pyeval('vim.Function(''tr'', args=[123, 3, 4])'))

View File

@ -8,6 +8,10 @@ func Create_vim_list()
return [1]
endfunction
func Create_vim_tuple()
return ('a', 'b')
endfunction
func Create_vim_dict()
return {'a': 1}
endfunction
@ -627,6 +631,107 @@ func Test_python3_list()
\ 'Vim(py3):TypeError: index must be int or slice, not dict')
endfunc
" Test for the python Tuple object
func Test_python3_tuple()
" Try to convert a null tuple
call AssertException(["py3 l = vim.eval('test_null_tuple()')"],
\ s:system_error_pat)
" Try to convert a Tuple with a null Tuple item
call AssertException(["py3 t = vim.eval('(test_null_tuple(),)')"],
\ s:system_error_pat)
" Try to convert a List with a null Tuple item
call AssertException(["py3 t = vim.eval('[test_null_tuple()]')"],
\ s:system_error_pat)
" Try to convert a Tuple with a null List item
call AssertException(["py3 t = vim.eval('(test_null_list(),)')"],
\ s:system_error_pat)
" Try to bind a null Tuple variable (works because an empty tuple is used)
let cmds =<< trim END
let t = test_null_tuple()
py3 tt = vim.bindeval('t')
END
call AssertException(cmds, '')
" Creating a tuple using different iterators
py3 t1 = vim.Tuple(['abc', 20, 1.2, (4, 5)])
call assert_equal(('abc', 20, 1.2, (4, 5)), py3eval('t1'))
py3 t2 = vim.Tuple('abc')
call assert_equal(('a', 'b', 'c'), py3eval('t2'))
py3 t3 = vim.Tuple({'color': 'red', 'model': 'ford'})
call assert_equal(('color', 'model'), py3eval('t3'))
py3 t4 = vim.Tuple()
call assert_equal((), py3eval('t4'))
py3 t5 = vim.Tuple(x**2 for x in range(5))
call assert_equal((0, 1, 4, 9, 16), py3eval('t5'))
py3 t6 = vim.Tuple(('abc', 20, 1.2, (4, 5)))
call assert_equal(('abc', 20, 1.2, (4, 5)), py3eval('t6'))
" Convert between Vim tuple/list and python tuple/list
py3 t = vim.Tuple(vim.bindeval("('a', ('b',), ['c'], {'s': 'd'})"))
call assert_equal(('a', ('b',), ['c'], {'s': 'd'}), py3eval('t'))
call assert_equal(['a', ('b',), ['c'], {'s': 'd'}], py3eval('list(t)'))
call assert_equal(('a', ('b',), ['c'], {'s': 'd'}), py3eval('tuple(t)'))
py3 l = vim.List(vim.bindeval("['e', ('f',), ['g'], {'s': 'h'}]"))
call assert_equal(('e', ('f',), ['g'], {'s': 'h'}), py3eval('tuple(l)'))
" Tuple assignment
py3 tt = vim.bindeval('("a", "b")')
call AssertException(['py3 tt[0] = 10'],
\ "Vim(py3):TypeError: 'vim.tuple' object does not support item assignment")
py3 tt = vim.bindeval('("a", "b")')
call AssertException(['py3 tt[0:1] = (10, 20)'],
\ "Vim(py3):TypeError: 'vim.tuple' object does not support item assignment")
" iterating over tuple from Python
py3 print([x for x in vim.bindeval("('a', 'b')")])
" modifying a list item within a tuple
let t = ('a', ['b', 'c'], 'd')
py3 vim.bindeval('t')[1][1] = 'x'
call assert_equal(('a', ['b', 'x'], 'd'), t)
" length of a tuple
let t = ()
py3 p_t = vim.bindeval('t')
call assert_equal(0, py3eval('len(p_t)'))
let t = ('a', )
py3 p_t = vim.bindeval('t')
call assert_equal(1, py3eval('len(p_t)'))
let t = ('a', 'b', 'c')
py3 p_t = vim.bindeval('t')
call assert_equal(3, py3eval('len(p_t)'))
" membership test
let t = ('a', 'b', 'c')
py3 p_t = vim.bindeval('t')
call assert_true(py3eval("b'c' in p_t"))
call assert_true(py3eval("b'd' not in p_t"))
py3 x = vim.eval('("a", (2), [3], {})')
call assert_equal(('a', '2', ['3'], {}), py3eval('x'))
" Using a keyword argument for a tuple
call AssertException(['py3 x = vim.Tuple(a=1)'],
\ 'Vim(py3):TypeError: tuple constructor does not accept keyword arguments')
" Using dict as an index
call AssertException(['py3 x = tt[{}]'],
\ 'Vim(py3):TypeError: index must be int or slice, not dict')
call AssertException(['py3 x = tt["abc"]'],
\ 'Vim(py3):TypeError: index must be int or slice, not str')
call AssertException(['py3 del tt.locked'],
\ 'Vim(py3):AttributeError: cannot delete vim.Tuple attributes')
call AssertException(['py3 tt.foobar = 1'],
\ 'Vim(py3):AttributeError: cannot set attribute foobar')
endfunc
" Test for the python Dict object
func Test_python3_dict()
" Try to convert a null Dict
@ -1005,11 +1110,24 @@ func Test_python3_lock_scope_attr()
\ 'Vim(py3):TypeError: cannot modify fixed list')
endfunc
" Test for locking/unlocking a tuple
func Test_tuple_lock()
let t = (1, 2, 3)
py3 t = vim.bindeval('t')
py3 t.locked = True
call assert_equal(1, islocked('t'))
py3 t.locked = False
call assert_equal(0, islocked('t'))
endfunc
" Test for py3eval()
func Test_python3_pyeval()
let l = py3eval('[0, 1, 2]')
call assert_equal([0, 1, 2], l)
let t = py3eval('("a", "b", "c")')
call assert_equal(("a", "b", "c"), t)
let d = py3eval('{"a": "b", "c": 1, "d": ["e"]}')
call assert_equal([['a', 'b'], ['c', 1], ['d', ['e']]], sort(items(d)))
@ -1036,12 +1154,14 @@ func Test_python3_pyeval_locals()
let num = 0xbadb33f
let d = {'a': 1, 'b': 2, 'c': str}
let l = [ str, num, d ]
let t = ( str, num, d )
let locals = #{
\ s: str,
\ n: num,
\ d: d,
\ l: l,
\ t: t,
\ }
" check basics
@ -1049,9 +1169,11 @@ func Test_python3_pyeval_locals()
call assert_equal(0xbadb33f, py3eval('n', locals))
call assert_equal(d, py3eval('d', locals))
call assert_equal(l, py3eval('l', locals))
call assert_equal(t, py3eval('t', locals))
call assert_equal('a,b,c', py3eval('b",".join(l)', {'l': ['a', 'b', 'c']}))
call assert_equal('hello', 's'->py3eval({'s': 'hello'}))
call assert_equal('a,b,c', 'b",".join(l)'->py3eval({'l': ['a', 'b', 'c']}))
call assert_equal('a-b-c', 'b"-".join(t)'->py3eval({'t': ('a', 'b', 'c')}))
py3 << trim EOF
def __UpdateDict(d, upd):
@ -1218,6 +1340,92 @@ func Test_python3_list_slice()
\ s:system_error_pat)
endfunc
" Slice
func Test_python3_tuple_slice()
py3 tt = vim.bindeval('(0, 1, 2, 3, 4, 5)')
py3 t = tt[:4]
call assert_equal((0, 1, 2, 3), py3eval('t'))
py3 t = tt[2:]
call assert_equal((2, 3, 4, 5), py3eval('t'))
py3 t = tt[:-4]
call assert_equal((0, 1), py3eval('t'))
py3 t = tt[-2:]
call assert_equal((4, 5), py3eval('t'))
py3 t = tt[2:4]
call assert_equal((2, 3), py3eval('t'))
py3 t = tt[4:2]
call assert_equal((), py3eval('t'))
py3 t = tt[-4:-2]
call assert_equal((2, 3), py3eval('t'))
py3 t = tt[-2:-4]
call assert_equal((), py3eval('t'))
py3 t = tt[:]
call assert_equal((0, 1, 2, 3, 4, 5), py3eval('t'))
py3 t = tt[0:6]
call assert_equal((0, 1, 2, 3, 4, 5), py3eval('t'))
py3 t = tt[-10:10]
call assert_equal((0, 1, 2, 3, 4, 5), py3eval('t'))
py3 t = tt[4:2:-1]
call assert_equal((4, 3), py3eval('t'))
py3 t = tt[::2]
call assert_equal((0, 2, 4), py3eval('t'))
py3 t = tt[4:2:1]
call assert_equal((), py3eval('t'))
" Error case: Use an invalid index
call AssertException(['py3 x = tt[-10]'], 'Vim(py3):IndexError: tuple index out of range')
" Use a step value of 0
call AssertException(['py3 x = tt[0:3:0]'],
\ 'Vim(py3):ValueError: slice step cannot be zero')
" Error case: Invalid slice type
call AssertException(["py3 x = tt['abc']"],
\ "Vim(py3):TypeError: index must be int or slice, not str")
" Error case: List with a null tuple item
let t = (test_null_tuple(),)
py3 tt = vim.bindeval('t')
call AssertException(["py3 x = tt[:]"], s:system_error_pat)
endfunc
func Test_python3_pytuple_to_vimtuple()
let t = py3eval("('a', 'b')")
call assert_equal(('a', 'b'), t)
let t = py3eval("()")
call assert_equal((), t)
let t = py3eval("('x',)")
call assert_equal(('x',), t)
let t = py3eval("((1, 2), (), (3, 4))")
call assert_equal(((1, 2), (), (3, 4)), t)
let t = py3eval("((1, 2), {'a': 10}, [5, 6])")
call assert_equal(((1, 2), {'a': 10}, [5, 6]), t)
" Invalid python tuple
py3 << trim END
class FailingIter(object):
def __iter__(self):
raise NotImplementedError('iter')
END
call assert_fails('call py3eval("(1, FailingIter, 2)")',
\ 'E859: Failed to convert returned python object to a Vim value')
py3 del FailingIter
endfunc
" Test for tuple garbage collection
func Test_python3_tuple_garbage_collect()
let t = (1, (2, 3), [4, 5], {'a': 6})
py3 py_t = vim.bindeval('t')
let save_testing = v:testing
let v:testing = 1
call test_garbagecollect_now()
let v:testing = save_testing
let new_t = py3eval('py_t')
call assert_equal((1, (2, 3), [4, 5], {'a': 6}), new_t)
endfunc
" Vars
func Test_python3_vars()
let g:foo = 'bac'
@ -2189,6 +2397,7 @@ func Test_python3_dir_method()
('range', vim.current.range),
('dictionary', vim.bindeval('{}')),
('list', vim.bindeval('[]')),
('tuple', vim.bindeval('()')),
('function', vim.bindeval('function("tr")')),
('output', sys.stdout),
):
@ -2204,6 +2413,7 @@ func Test_python3_dir_method()
range:__dir__,append,end,start
dictionary:__dir__,get,has_key,items,keys,locked,pop,popitem,scope,update,values
list:__dir__,extend,locked
tuple:__dir__,locked
function:__dir__,args,auto_rebind,self,softspace
output:__dir__,close,closed,flush,isatty,readable,seekable,softspace,writable,write,writelines
END
@ -2216,7 +2426,9 @@ func Test_python3_new()
call assert_equal({'a': 1}, py3eval('vim.Dictionary(a=1)'))
call assert_equal({'a': 1}, py3eval('vim.Dictionary(((''a'', 1),))'))
call assert_equal([], py3eval('vim.List()'))
call assert_equal((), py3eval('vim.Tuple()'))
call assert_equal(['a', 'b', 'c', '7'], py3eval('vim.List(iter(''abc7''))'))
call assert_equal(('a', 'b', 'c', '7'), py3eval('vim.Tuple(iter(''abc7''))'))
call assert_equal(function('tr'), py3eval('vim.Function(''tr'')'))
call assert_equal(function('tr', [123, 3, 4]),
\ py3eval('vim.Function(''tr'', args=[123, 3, 4])'))
@ -4065,6 +4277,7 @@ 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:tuple_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
@ -4080,6 +4293,12 @@ func Test_python3_iter_ref()
for el in v:
vim.vars['list_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
create_tuple = vim.Function('Create_vim_tuple')
v = create_tuple()
base_ref_count = sys.getrefcount(v)
for el in v:
vim.vars['tuple_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)
@ -4100,6 +4319,7 @@ func Test_python3_iter_ref()
EOF
call assert_equal(1, g:list_iter_ref_count_increase)
call assert_equal(1, g:tuple_iter_ref_count_increase)
call assert_equal(1, g:dict_iter_ref_count_increase)
if py3eval('sys.version_info[:2] < (3, 13)')
call assert_equal(1, g:bufmap_iter_ref_count_increase)

View File

@ -1241,12 +1241,43 @@ endfunc
" Test for locking and unlocking a tuple variable
func Test_tuple_lock()
" lockvar 0
let g:t = ([0, 1],)
let lines =<< trim END
VAR t = ([0, 1],)
call add(t[0], 2)
call assert_equal(([0, 1, 2], ), t)
lockvar 0 g:t
LET g:t = ()
END
call v9.CheckSourceLegacyAndVim9Success(lines)
call v9.CheckSourceLegacyAndVim9Failure(lines, [
\ 'E1122: Variable is locked: g:t',
\ 'E1122: Variable is locked: t',
\ 'E1122: Variable is locked: g:t'])
unlet g:t
" Tuple is immutable. So "lockvar 1" is not applicable to a tuple.
" lockvar 2
let g:t = ([0, 1],)
let lines =<< trim END
lockvar 2 g:t
call add(g:t[0], 2)
END
call v9.CheckSourceLegacyAndVim9Failure(lines, [
\ 'E741: Value is locked: add() argument',
\ 'E741: Value is locked: add() argument',
\ 'E741: Value is locked: add() argument'])
unlet g:t
" lockvar 3
let g:t = ([0, 1],)
let lines =<< trim END
lockvar 3 g:t
LET g:t[0][0] = 10
END
call v9.CheckSourceLegacyAndVim9Failure(lines, [
\ 'E741: Value is locked: g:t[0][0] = 10',
\ 'E1119: Cannot change locked list item',
\ 'E741: Value is locked: g:t[0][0] = 10'])
unlet g:t
let lines =<< trim END
VAR t = ([0, 1],)
@ -1810,6 +1841,25 @@ func Test_tuple_insert()
\ 'E1226: List or Blob required for argument 1'])
endfunc
" Test for islocked()
func Test_tuple_islocked()
let lines =<< trim END
let t = (1, [2], 3)
call assert_equal(0, islocked('t'))
call assert_equal(0, islocked('t[1]'))
lockvar 1 t
call assert_equal(1, islocked('t'))
call assert_equal(0, islocked('t[1]'))
unlockvar t
call assert_equal(0, islocked('t'))
lockvar 2 t
call assert_equal(1, islocked('t[1]'))
unlockvar t
call assert_equal(0, islocked('t[1]'))
END
call v9.CheckSourceSuccess(lines)
endfunc
" Test for items()
func Test_tuple_items()
let lines =<< trim END

View File

@ -2095,6 +2095,9 @@ tv_islocked(typval_T *tv)
|| (tv->v_type == VAR_LIST
&& tv->vval.v_list != NULL
&& (tv->vval.v_list->lv_lock & VAR_LOCKED))
|| (tv->v_type == VAR_TUPLE
&& tv->vval.v_tuple != NULL
&& (tv->vval.v_tuple->tv_lock & VAR_LOCKED))
|| (tv->v_type == VAR_DICT
&& tv->vval.v_dict != NULL
&& (tv->vval.v_dict->dv_lock & VAR_LOCKED));

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1239,
/**/
1238,
/**/