# HG changeset patch # User Christian Brabandt # Date 1460642406 -7200 # Node ID 8755d57debaa4b57af54a3f3f5a503c2c9f86887 # Parent a63c61b66a49b78b8811578635a10d81b69c6d50 commit https://github.com/vim/vim/commit/8110a091bc749d8748a20807a724a3af3ca6d509 Author: Bram Moolenaar Date: Thu Apr 14 15:56:09 2016 +0200 patch 7.4.1731 Problem: Python: turns partial into simple funcref. Solution: Use partials like partials. (Nikolai Pavlov, closes https://github.com/vim/vim/issues/734) diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -653,10 +653,25 @@ vim.List object *python-List* class List(vim.List): # Subclassing vim.Function object *python-Function* - Function-like object, acting like vim |Funcref| object. Supports `.name` - attribute and is callable. Accepts special keyword argument `self`, see - |Dictionary-function|. You can also use `vim.Function(name)` constructor, - it is the same as `vim.bindeval('function(%s)'%json.dumps(name))`. + Function-like object, acting like vim |Funcref| object. Accepts special + keyword argument `self`, see |Dictionary-function|. You can also use + `vim.Function(name)` constructor, it is the same as + `vim.bindeval('function(%s)'%json.dumps(name))`. + + Attributes (read-only): + Attribute Description ~ + name Function name. + args `None` or a |python-List| object with arguments. Note that + this is a copy of the arguments list, constructed each time + you request this attribute. Modifications made to the list + will be ignored (but not to the containers inside argument + list: this is like |copy()| and not |deepcopy()|). + self `None` or a |python-Dictionary| object with self + dictionary. Note that explicit `self` keyword used when + calling resulting object overrides this attribute. + + Constructor additionally accepts `args` and `self` keywords. If any of + them is given then it constructs a partial, see |function()|. Examples: > f = vim.Function('tr') # Constructor @@ -670,6 +685,11 @@ vim.Function object *python-Function* print f(self={}) # Like call('DictFun', [], {}) print isinstance(f, vim.Function) # True + p = vim.Function('DictFun', self={}) + print f() + p = vim.Function('tr', args=['abc', 'a']) + print f('b') + ============================================================================== 8. pyeval() and py3eval() Vim functions *python-pyeval* diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -453,7 +453,6 @@ static long dict_len(dict_T *d); static char_u *dict2string(typval_T *tv, int copyID); static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate); static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); -static char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); static char_u *string_quote(char_u *str, int function); static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); static int find_internal_func(char_u *name); @@ -8153,7 +8152,7 @@ echo_string( * Puts quotes around strings, so that they can be parsed back by eval(). * May return NULL. */ - static char_u * + char_u * tv2string( typval_T *tv, char_u **tofree, diff --git a/src/if_py_both.h b/src/if_py_both.h --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -72,6 +72,7 @@ typedef void (*runner)(const char *, voi static int ConvertFromPyObject(PyObject *, typval_T *); static int _ConvertFromPyObject(PyObject *, typval_T *, PyObject *); static int ConvertFromPyMapping(PyObject *, typval_T *); +static int ConvertFromPySequence(PyObject *, typval_T *); static PyObject *WindowNew(win_T *, tabpage_T *); static PyObject *BufferNew (buf_T *); static PyObject *LineToString(const char *); @@ -1433,6 +1434,7 @@ typedef struct pylinkedlist_S { static pylinkedlist_T *lastdict = NULL; static pylinkedlist_T *lastlist = NULL; +static pylinkedlist_T *lastfunc = NULL; static void pyll_remove(pylinkedlist_T *ref, pylinkedlist_T **last) @@ -2828,14 +2830,20 @@ typedef struct { PyObject_HEAD char_u *name; + int argc; + typval_T *argv; + dict_T *self; + pylinkedlist_T ref; } FunctionObject; static PyTypeObject FunctionType; -#define NEW_FUNCTION(name) FunctionNew(&FunctionType, name) +#define NEW_FUNCTION(name, argc, argv, self) \ + FunctionNew(&FunctionType, name, argc, argv, self) static PyObject * -FunctionNew(PyTypeObject *subtype, char_u *name) +FunctionNew(PyTypeObject *subtype, char_u *name, int argc, typval_T *argv, + dict_T *selfdict) { FunctionObject *self; @@ -2865,6 +2873,13 @@ FunctionNew(PyTypeObject *subtype, char_ return NULL; } + self->argc = argc; + self->argv = argv; + self->self = selfdict; + + if (self->argv || self->self) + pyll_add((PyObject *)(self), &self->ref, &lastfunc); + return (PyObject *)(self); } @@ -2872,19 +2887,59 @@ FunctionNew(PyTypeObject *subtype, char_ FunctionConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { PyObject *self; + PyObject *selfdictObject; + PyObject *argsObject = NULL; char_u *name; - - if (kwargs) - { - PyErr_SET_STRING(PyExc_TypeError, - N_("function constructor does not accept keyword arguments")); - return NULL; + typval_T selfdicttv; + typval_T argstv; + list_T *argslist = NULL; + dict_T *selfdict = NULL; + int argc = 0; + typval_T *argv = NULL; + typval_T *curtv; + listitem_T *li; + + if (kwargs != NULL) + { + selfdictObject = PyDict_GetItemString(kwargs, "self"); + if (selfdictObject != NULL) + { + if (ConvertFromPyMapping(selfdictObject, &selfdicttv) == -1) + return NULL; + selfdict = selfdicttv.vval.v_dict; + } + argsObject = PyDict_GetItemString(kwargs, "args"); + if (argsObject != NULL) + { + if (ConvertFromPySequence(argsObject, &argstv) == -1) + { + dict_unref(selfdict); + return NULL; + } + argslist = argstv.vval.v_list; + + argc = argslist->lv_len; + if (argc != 0) + { + argv = PyMem_New(typval_T, (size_t) argc); + curtv = argv; + for (li = argslist->lv_first; li != NULL; li = li->li_next) + copy_tv(&li->li_tv, curtv++); + } + list_unref(argslist); + } } if (!PyArg_ParseTuple(args, "et", "ascii", &name)) + { + dict_unref(selfdict); + while (argc--) + clear_tv(&argv[argc]); + PyMem_Free(argv); return NULL; - - self = FunctionNew(subtype, name); + } + + self = FunctionNew(subtype, name, argc, argv, selfdict); PyMem_Free(name); @@ -2894,14 +2949,21 @@ FunctionConstructor(PyTypeObject *subtyp static void FunctionDestructor(FunctionObject *self) { + int i; func_unref(self->name); vim_free(self->name); + for (i = 0; i < self->argc; ++i) + clear_tv(&self->argv[i]); + PyMem_Free(self->argv); + dict_unref(self->self); + if (self->argv || self->self) + pyll_remove(&self->ref, &lastfunc); DESTRUCTOR_FINISH(self); } static char *FunctionAttrs[] = { - "softspace", + "softspace", "args", "self", NULL }; @@ -2912,6 +2974,69 @@ FunctionDir(PyObject *self) } static PyObject * +FunctionAttr(FunctionObject *self, char *name) +{ + list_T *list; + int i; + if (strcmp(name, "name") == 0) + return PyString_FromString((char *)(self->name)); + else if (strcmp(name, "args") == 0) + { + if (self->argv == NULL) + return AlwaysNone(NULL); + list = list_alloc(); + for (i = 0; i < self->argc; ++i) + list_append_tv(list, &self->argv[i]); + return NEW_LIST(list); + } + else if (strcmp(name, "self") == 0) + return self->self == NULL + ? AlwaysNone(NULL) + : NEW_DICTIONARY(self->self); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, FunctionAttrs); + return NULL; +} + +/* Populate partial_T given function object. + * + * "exported" should be set to true when it is needed to construct a partial + * that may be stored in a variable (i.e. may be freed by Vim). + */ + static void +set_partial(FunctionObject *self, partial_T *pt, int exported) +{ + typval_T *curtv; + int i; + + pt->pt_name = self->name; + if (self->argv) + { + pt->pt_argc = self->argc; + if (exported) + { + pt->pt_argv = (typval_T *)alloc_clear( + sizeof(typval_T) * self->argc); + for (i = 0; i < pt->pt_argc; ++i) + copy_tv(&self->argv[i], &pt->pt_argv[i]); + } + else + pt->pt_argv = self->argv; + } + else + { + pt->pt_argc = 0; + pt->pt_argv = NULL; + } + pt->pt_dict = self->self; + if (exported && self->self) + ++pt->pt_dict->dv_refcount; + if (exported) + pt->pt_name = vim_strsave(pt->pt_name); + pt->pt_refcount = 1; +} + + static PyObject * FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs) { char_u *name = self->name; @@ -2922,8 +3047,10 @@ FunctionCall(FunctionObject *self, PyObj PyObject *selfdictObject; PyObject *ret; int error; - - if (ConvertFromPyObject(argsObject, &args) == -1) + partial_T pt; + partial_T *pt_ptr = NULL; + + if (ConvertFromPySequence(argsObject, &args) == -1) return NULL; if (kwargs != NULL) @@ -2940,11 +3067,17 @@ FunctionCall(FunctionObject *self, PyObj } } + if (self->argv || self->self) + { + set_partial(self, &pt, FALSE); + pt_ptr = &pt; + } + Py_BEGIN_ALLOW_THREADS Python_Lock_Vim(); VimTryStart(); - error = func_call(name, &args, NULL, selfdict, &rettv); + error = func_call(name, &args, pt_ptr, selfdict, &rettv); Python_Release_Vim(); Py_END_ALLOW_THREADS @@ -2970,14 +3103,49 @@ FunctionCall(FunctionObject *self, PyObj static PyObject * FunctionRepr(FunctionObject *self) { -#ifdef Py_TRACE_REFS - /* For unknown reason self->name may be NULL after calling - * Finalize */ - return PyString_FromFormat("", - (self->name == NULL ? "" : (char *)self->name)); -#else - return PyString_FromFormat("", (char *)self->name); -#endif + PyObject *ret; + garray_T repr_ga; + int i; + char_u *tofree = NULL; + typval_T tv; + char_u numbuf[NUMBUFLEN]; + + ga_init2(&repr_ga, (int)sizeof(char), 70); + ga_concat(&repr_ga, (char_u *)"name) + ga_concat(&repr_ga, self->name); + else + ga_concat(&repr_ga, (char_u *)""); + ga_append(&repr_ga, '\''); + if (self->argv) + { + ga_concat(&repr_ga, (char_u *)", args=["); + ++emsg_silent; + for (i = 0; i < self->argc; i++) + { + if (i != 0) + ga_concat(&repr_ga, (char_u *)", "); + ga_concat(&repr_ga, tv2string(&self->argv[i], &tofree, numbuf, + get_copyID())); + vim_free(tofree); + } + --emsg_silent; + ga_append(&repr_ga, ']'); + } + if (self->self) + { + ga_concat(&repr_ga, (char_u *)", self="); + tv.v_type = VAR_DICT; + tv.vval.v_dict = self->self; + ++emsg_silent; + ga_concat(&repr_ga, tv2string(&tv, &tofree, numbuf, get_copyID())); + --emsg_silent; + vim_free(tofree); + } + ga_append(&repr_ga, '>'); + ret = PyString_FromString((char *)repr_ga.ga_data); + ga_clear(&repr_ga); + return ret; } static struct PyMethodDef FunctionMethods[] = { @@ -5551,11 +5719,13 @@ set_ref_in_py(const int copyID) pylinkedlist_T *cur; dict_T *dd; list_T *ll; + int i; int abort = FALSE; + FunctionObject *func; if (lastdict != NULL) { - for(cur = lastdict ; !abort && cur != NULL ; cur = cur->pll_prev) + for (cur = lastdict ; !abort && cur != NULL ; cur = cur->pll_prev) { dd = ((DictionaryObject *) (cur->pll_obj))->dict; if (dd->dv_copyID != copyID) @@ -5568,7 +5738,7 @@ set_ref_in_py(const int copyID) if (lastlist != NULL) { - for(cur = lastlist ; !abort && cur != NULL ; cur = cur->pll_prev) + for (cur = lastlist ; !abort && cur != NULL ; cur = cur->pll_prev) { ll = ((ListObject *) (cur->pll_obj))->list; if (ll->lv_copyID != copyID) @@ -5579,6 +5749,24 @@ set_ref_in_py(const int copyID) } } + if (lastfunc != NULL) + { + for (cur = lastfunc ; !abort && cur != NULL ; cur = cur->pll_prev) + { + func = (FunctionObject *) cur->pll_obj; + if (func->self != NULL && func->self->dv_copyID != copyID) + { + func->self->dv_copyID = copyID; + abort = abort || set_ref_in_ht( + &func->self->dv_hashtab, copyID, NULL); + } + if (func->argc) + for (i = 0; !abort && i < func->argc; ++i) + abort = abort + || set_ref_in_item(&func->argv[i], copyID, NULL, NULL); + } + } + return abort; } @@ -5880,6 +6068,34 @@ ConvertFromPyMapping(PyObject *obj, typv } static int +ConvertFromPySequence(PyObject *obj, typval_T *tv) +{ + PyObject *lookup_dict; + int ret; + + if (!(lookup_dict = PyDict_New())) + return -1; + + if (PyType_IsSubtype(obj->ob_type, &ListType)) + { + tv->v_type = VAR_LIST; + tv->vval.v_list = (((ListObject *)(obj))->list); + ++tv->vval.v_list->lv_refcount; + } + else if (PyIter_Check(obj) || PySequence_Check(obj)) + return convert_dl(obj, tv, pyseq_to_tv, lookup_dict); + else + { + PyErr_FORMAT(PyExc_TypeError, + N_("unable to convert %s to vim list"), + Py_TYPE_NAME(obj)); + ret = -1; + } + Py_DECREF(lookup_dict); + return ret; +} + + static int ConvertFromPyObject(PyObject *obj, typval_T *tv) { PyObject *lookup_dict; @@ -5909,11 +6125,22 @@ ConvertFromPyObject(PyObject *obj, typva } else if (PyType_IsSubtype(obj->ob_type, &FunctionType)) { - if (set_string_copy(((FunctionObject *) (obj))->name, tv) == -1) - return -1; - - tv->v_type = VAR_FUNC; - func_ref(tv->vval.v_string); + FunctionObject *func = (FunctionObject *) obj; + if (func->self != NULL || func->argv != NULL) + { + partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); + set_partial(func, pt, TRUE); + tv->vval.v_partial = pt; + tv->v_type = VAR_PARTIAL; + } + else + { + if (set_string_copy(func->name, tv) == -1) + return -1; + + tv->v_type = VAR_FUNC; + } + func_ref(func->name); } else if (PyBytes_Check(obj)) { @@ -6009,6 +6236,8 @@ ConvertFromPyObject(PyObject *obj, typva static PyObject * ConvertToPyObject(typval_T *tv) { + typval_T *argv; + int i; if (tv == NULL) { PyErr_SET_VIM(N_("internal error: NULL reference passed")); @@ -6031,10 +6260,23 @@ ConvertToPyObject(typval_T *tv) return NEW_DICTIONARY(tv->vval.v_dict); case VAR_FUNC: return NEW_FUNCTION(tv->vval.v_string == NULL - ? (char_u *)"" : tv->vval.v_string); + ? (char_u *)"" : tv->vval.v_string, + 0, NULL, NULL); case VAR_PARTIAL: + if (tv->vval.v_partial->pt_argc) + { + argv = PyMem_New(typval_T, (size_t)tv->vval.v_partial->pt_argc); + for (i = 0; i < tv->vval.v_partial->pt_argc; i++) + copy_tv(&tv->vval.v_partial->pt_argv[i], &argv[i]); + } + else + argv = NULL; + if (tv->vval.v_partial->pt_dict != NULL) + tv->vval.v_partial->pt_dict->dv_refcount++; return NEW_FUNCTION(tv->vval.v_partial == NULL - ? (char_u *)"" : tv->vval.v_partial->pt_name); + ? (char_u *)"" : tv->vval.v_partial->pt_name, + tv->vval.v_partial->pt_argc, argv, + tv->vval.v_partial->pt_dict); case VAR_UNKNOWN: case VAR_CHANNEL: case VAR_JOB: diff --git a/src/if_python.c b/src/if_python.c --- a/src/if_python.c +++ b/src/if_python.c @@ -1539,12 +1539,12 @@ ListGetattr(PyObject *self, char *name) static PyObject * FunctionGetattr(PyObject *self, char *name) { - FunctionObject *this = (FunctionObject *)(self); + PyObject *r; - if (strcmp(name, "name") == 0) - return PyString_FromString((char *)(this->name)); - else if (strcmp(name, "__members__") == 0) - return ObjectDir(NULL, FunctionAttrs); + r = FunctionAttr((FunctionObject *)(self), name); + + if (r || PyErr_Occurred()) + return r; else return Py_FindMethod(FunctionMethods, self, name); } diff --git a/src/if_python3.c b/src/if_python3.c --- a/src/if_python3.c +++ b/src/if_python3.c @@ -1528,14 +1528,16 @@ ListSetattro(PyObject *self, PyObject *n static PyObject * FunctionGetattro(PyObject *self, PyObject *nameobj) { + PyObject *r; FunctionObject *this = (FunctionObject *)(self); GET_ATTR_STRING(name, nameobj); - if (strcmp(name, "name") == 0) - return PyUnicode_FromString((char *)(this->name)); - - return PyObject_GenericGetAttr(self, nameobj); + r = FunctionAttr(this, name); + if (r || PyErr_Occurred()) + return r; + else + return PyObject_GenericGetAttr(self, nameobj); } /* External interface diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -150,4 +150,5 @@ void ex_oldfiles(exarg_T *eap); void reset_v_option_vars(void); int modify_fname(char_u *src, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen); char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags); +char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); /* vim: set ft=c : */ diff --git a/src/testdir/test86.in b/src/testdir/test86.in --- a/src/testdir/test86.in +++ b/src/testdir/test86.in @@ -13,6 +13,7 @@ STARTTEST :lang C :fun Test() :py import vim +:py cb = vim.current.buffer :let l = [] :py l=vim.bindeval('l') :py f=vim.bindeval('function("strlen")') @@ -207,7 +208,15 @@ EOF :let l = [0, 1, 2, 3] :py l=vim.bindeval('l') :lockvar! l -:py l[2]='i' +py << EOF +def emsg(ei): + return ei[0].__name__ + ':' + repr(ei[1].args) + +try: + l[2]='i' +except vim.error: + cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info())) +EOF :$put =string(l) :unlockvar! l :" @@ -219,7 +228,7 @@ def ee(expr, g=globals(), l=locals()): exec(expr, g, l) except: ei = sys.exc_info() - msg = sys.exc_info()[0].__name__ + ':' + repr(sys.exc_info()[1].args) + msg = emsg(ei) msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'') if expr.find('None') > -1: msg = msg.replace('TypeError:(\'iteration over non-sequence\',)', @@ -611,7 +620,6 @@ EOF : autocmd BufFilePre * python cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) :augroup END py << EOF -cb = vim.current.buffer # Tests BufferAppend and BufferItem cb.append(b[0]) # Tests BufferSlice and BufferAssSlice @@ -865,6 +873,175 @@ EOF :$put =string(pyeval('vim.List()')) :$put =string(pyeval('vim.List(iter(''abc7''))')) :$put =string(pyeval('vim.Function(''tr'')')) +:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4])')) +:$put =string(pyeval('vim.Function(''tr'', args=[])')) +:$put =string(pyeval('vim.Function(''tr'', self={})')) +:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4], self={})')) +:" +:" Test vim.Function +:function Args(...) +: return a:000 +:endfunction +:function SelfArgs(...) dict +: return [a:000, self] +:endfunction +:" The following four lines should not crash +:let Pt = function('tr', [[]], {'l': []}) +:py Pt = vim.bindeval('Pt') +:unlet Pt +:py del Pt +py << EOF +def ecall(out_prefix, func, *args, **kwargs): + line = out_prefix + ': ' + try: + ret = func(*args, **kwargs) + except Exception: + line += '!exception: ' + emsg(sys.exc_info()) + else: + line += '!result: ' + vim.Function('string')(ret) + cb.append(line) +a = vim.Function('Args') +pa1 = vim.Function('Args', args=['abcArgsPA1']) +pa2 = vim.Function('Args', args=[]) +pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'}) +pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'}) +cb.append('a: ' + repr(a)) +cb.append('pa1: ' + repr(pa1)) +cb.append('pa2: ' + repr(pa2)) +cb.append('pa3: ' + repr(pa3)) +cb.append('pa4: ' + repr(pa4)) +sa = vim.Function('SelfArgs') +psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1']) +psa2 = vim.Function('SelfArgs', args=[]) +psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'}) +psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'}) +cb.append('sa: ' + repr(sa)) +cb.append('psa1: ' + repr(psa1)) +cb.append('psa2: ' + repr(psa2)) +cb.append('psa3: ' + repr(psa3)) +cb.append('psa4: ' + repr(psa4)) + +psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'}) +psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]] +psar.self['rec'] = psar +psar.self['self'] = psar.self +psar.self['args'] = psar.args + +try: + cb.append('psar: ' + repr(psar)) +except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) +EOF +:$put ='s(a): '.string(pyeval('a')) +:$put ='s(pa1): '.string(pyeval('pa1')) +:$put ='s(pa2): '.string(pyeval('pa2')) +:$put ='s(pa3): '.string(pyeval('pa3')) +:$put ='s(pa4): '.string(pyeval('pa4')) +:$put ='s(sa): '.string(pyeval('sa')) +:$put ='s(psa1): '.string(pyeval('psa1')) +:$put ='s(psa2): '.string(pyeval('psa2')) +:$put ='s(psa3): '.string(pyeval('psa3')) +:$put ='s(psa4): '.string(pyeval('psa4')) +: +:py ecall('a()', a, ) +:py ecall('pa1()', pa1, ) +:py ecall('pa2()', pa2, ) +:py ecall('pa3()', pa3, ) +:py ecall('pa4()', pa4, ) +:py ecall('sa()', sa, ) +:py ecall('psa1()', psa1, ) +:py ecall('psa2()', psa2, ) +:py ecall('psa3()', psa3, ) +:py ecall('psa4()', psa4, ) +: +:py ecall('a(42, 43)', a, 42, 43) +:py ecall('pa1(42, 43)', pa1, 42, 43) +:py ecall('pa2(42, 43)', pa2, 42, 43) +:py ecall('pa3(42, 43)', pa3, 42, 43) +:py ecall('pa4(42, 43)', pa4, 42, 43) +:py ecall('sa(42, 43)', sa, 42, 43) +:py ecall('psa1(42, 43)', psa1, 42, 43) +:py ecall('psa2(42, 43)', psa2, 42, 43) +:py ecall('psa3(42, 43)', psa3, 42, 43) +:py ecall('psa4(42, 43)', psa4, 42, 43) +: +:py ecall('a(42, self={"20": 1})', a, 42, self={'20': 1}) +:py ecall('pa1(42, self={"20": 1})', pa1, 42, self={'20': 1}) +:py ecall('pa2(42, self={"20": 1})', pa2, 42, self={'20': 1}) +:py ecall('pa3(42, self={"20": 1})', pa3, 42, self={'20': 1}) +:py ecall('pa4(42, self={"20": 1})', pa4, 42, self={'20': 1}) +:py ecall('sa(42, self={"20": 1})', sa, 42, self={'20': 1}) +:py ecall('psa1(42, self={"20": 1})', psa1, 42, self={'20': 1}) +:py ecall('psa2(42, self={"20": 1})', psa2, 42, self={'20': 1}) +:py ecall('psa3(42, self={"20": 1})', psa3, 42, self={'20': 1}) +:py ecall('psa4(42, self={"20": 1})', psa4, 42, self={'20': 1}) +: +:py ecall('a(self={"20": 1})', a, self={'20': 1}) +:py ecall('pa1(self={"20": 1})', pa1, self={'20': 1}) +:py ecall('pa2(self={"20": 1})', pa2, self={'20': 1}) +:py ecall('pa3(self={"20": 1})', pa3, self={'20': 1}) +:py ecall('pa4(self={"20": 1})', pa4, self={'20': 1}) +:py ecall('sa(self={"20": 1})', sa, self={'20': 1}) +:py ecall('psa1(self={"20": 1})', psa1, self={'20': 1}) +:py ecall('psa2(self={"20": 1})', psa2, self={'20': 1}) +:py ecall('psa3(self={"20": 1})', psa3, self={'20': 1}) +:py ecall('psa4(self={"20": 1})', psa4, self={'20': 1}) +py << EOF +def s(v): + if v is None: + return repr(v) + else: + return vim.Function('string')(v) + +cb.append('a.args: ' + s(a.args)) +cb.append('pa1.args: ' + s(pa1.args)) +cb.append('pa2.args: ' + s(pa2.args)) +cb.append('pa3.args: ' + s(pa3.args)) +cb.append('pa4.args: ' + s(pa4.args)) +cb.append('sa.args: ' + s(sa.args)) +cb.append('psa1.args: ' + s(psa1.args)) +cb.append('psa2.args: ' + s(psa2.args)) +cb.append('psa3.args: ' + s(psa3.args)) +cb.append('psa4.args: ' + s(psa4.args)) + +cb.append('a.self: ' + s(a.self)) +cb.append('pa1.self: ' + s(pa1.self)) +cb.append('pa2.self: ' + s(pa2.self)) +cb.append('pa3.self: ' + s(pa3.self)) +cb.append('pa4.self: ' + s(pa4.self)) +cb.append('sa.self: ' + s(sa.self)) +cb.append('psa1.self: ' + s(psa1.self)) +cb.append('psa2.self: ' + s(psa2.self)) +cb.append('psa3.self: ' + s(psa3.self)) +cb.append('psa4.self: ' + s(psa4.self)) + +cb.append('a.name: ' + s(a.name)) +cb.append('pa1.name: ' + s(pa1.name)) +cb.append('pa2.name: ' + s(pa2.name)) +cb.append('pa3.name: ' + s(pa3.name)) +cb.append('pa4.name: ' + s(pa4.name)) +cb.append('sa.name: ' + s(sa.name)) +cb.append('psa1.name: ' + s(psa1.name)) +cb.append('psa2.name: ' + s(psa2.name)) +cb.append('psa3.name: ' + s(psa3.name)) +cb.append('psa4.name: ' + s(psa4.name)) + +del s + +del a +del pa1 +del pa2 +del pa3 +del pa4 +del sa +del psa1 +del psa2 +del psa3 +del psa4 +del psar + +del ecall +EOF :" :" Test stdout/stderr :redir => messages @@ -1140,7 +1317,7 @@ ee('vim.foreach_rtp(FailingCall())') ee('vim.foreach_rtp(int, 2)') cb.append('> import') old_rtp = vim.options['rtp'] -vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') +vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,') ee('import xxx_no_such_module_xxx') ee('import failing_import') ee('import failing') @@ -1224,9 +1401,20 @@ ee('l.locked = FailingTrue()') ee('l.xxx = True') cb.append("> Function") cb.append(">> FunctionConstructor") +cb.append(">>> FunctionConstructor") ee('vim.Function("123")') ee('vim.Function("xxx_non_existent_function_xxx")') ee('vim.Function("xxx#non#existent#function#xxx")') +ee('vim.Function("xxx_non_existent_function_xxx2", args=[])') +ee('vim.Function("xxx_non_existent_function_xxx3", self={})') +ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})') +cb.append(">>> FunctionNew") +ee('vim.Function("tr", self="abcFuncSelf")') +ee('vim.Function("tr", args=427423)') +ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")') +ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")') +ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")') +ee('vim.Function("tr", "")') cb.append(">> FunctionCall") convertfrompyobject_test('f(%s)') convertfrompymapping_test('fd(self=%s)') @@ -1381,7 +1569,7 @@ def test_keyboard_interrupt(): except KeyboardInterrupt: cb.append('Caught KeyboardInterrupt') except Exception: - cb.append('!!!!!!!! Caught exception: ' + repr(sys.exc_info)) + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) else: cb.append('!!!!!!!! No exception') try: @@ -1389,7 +1577,7 @@ def test_keyboard_interrupt(): except KeyboardInterrupt: cb.append('!!!!!!!! Caught KeyboardInterrupt') except Exception: - cb.append('!!!!!!!! Caught exception: ' + repr(sys.exc_info)) + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) else: cb.append('No exception') EOF @@ -1409,6 +1597,7 @@ EOF py << EOF del cb del ee +del emsg del sys del os del vim @@ -1441,7 +1630,7 @@ EOF :" :/^start:/,$wq! test.out :" vim: et ts=4 isk-=\: -:call getchar() +:while getchar(0) isnot 0|endwhile ENDTEST start: diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok --- a/src/testdir/test86.ok +++ b/src/testdir/test86.ok @@ -57,6 +57,7 @@ None [0, 1, 2, 3, 4, 5, 6, 7] [0, 1, 2, 3, 4, 5, 6, 7] [0, 1, 2, 3, 4, 5, 6, 7] +l[2] threw vim.error: error:('list is locked',) [0, 1, 2, 3] [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd'] [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}] @@ -447,7 +448,7 @@ tabpage:__dir__,__members__,number,valid 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 -function:__dir__,__members__,softspace +function:__dir__,__members__,args,self,softspace output:__dir__,__members__,close,flush,isatty,readable,seekable,softspace,writable,write,writelines {} {'a': 1} @@ -455,12 +456,108 @@ output:__dir__,__members__,close,flush,i [] ['a', 'b', 'c', '7'] function('tr') +function('tr', [123, 3, 4]) +function('tr') +function('tr', {}) +function('tr', [123, 3, 4], {}) +a: +pa1: +pa2: +pa3: +pa4: +sa: +psa1: +psa2: +psa3: +psa4: +psar: +s(a): function('Args') +s(pa1): function('Args', ['abcArgsPA1']) +s(pa2): function('Args') +s(pa3): function('Args', ['abcArgsPA3'], {'abcSelfPA3': 'abcSelfPA3Val'}) +s(pa4): function('Args', {'abcSelfPA4': 'abcSelfPA4Val'}) +s(sa): function('SelfArgs') +s(psa1): function('SelfArgs', ['abcArgsPSA1']) +s(psa2): function('SelfArgs') +s(psa3): function('SelfArgs', ['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}) +s(psa4): function('SelfArgs', {'abcSelfPSA4': 'abcSelfPSA4Val'}) +a(): !result: [] +pa1(): !result: ['abcArgsPA1'] +pa2(): !result: [] +pa3(): !result: ['abcArgsPA3'] +pa4(): !result: [] +sa(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa1(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa2(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa3(): !result: [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] +psa4(): !result: [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] +a(42, 43): !result: [42, 43] +pa1(42, 43): !result: ['abcArgsPA1', 42, 43] +pa2(42, 43): !result: [42, 43] +pa3(42, 43): !result: ['abcArgsPA3', 42, 43] +pa4(42, 43): !result: [42, 43] +sa(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa1(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa2(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa3(42, 43): !result: [['abcArgsPSA3', 42, 43], {'abcSelfPSA3': 'abcSelfPSA3Val'}] +psa4(42, 43): !result: [[42, 43], {'abcSelfPSA4': 'abcSelfPSA4Val'}] +a(42, self={"20": 1}): !result: [42] +pa1(42, self={"20": 1}): !result: ['abcArgsPA1', 42] +pa2(42, self={"20": 1}): !result: [42] +pa3(42, self={"20": 1}): !result: ['abcArgsPA3', 42] +pa4(42, self={"20": 1}): !result: [42] +sa(42, self={"20": 1}): !result: [[42], {'20': 1}] +psa1(42, self={"20": 1}): !result: [['abcArgsPSA1', 42], {'20': 1}] +psa2(42, self={"20": 1}): !result: [[42], {'20': 1}] +psa3(42, self={"20": 1}): !result: [['abcArgsPSA3', 42], {'20': 1}] +psa4(42, self={"20": 1}): !result: [[42], {'20': 1}] +a(self={"20": 1}): !result: [] +pa1(self={"20": 1}): !result: ['abcArgsPA1'] +pa2(self={"20": 1}): !result: [] +pa3(self={"20": 1}): !result: ['abcArgsPA3'] +pa4(self={"20": 1}): !result: [] +sa(self={"20": 1}): !result: [[], {'20': 1}] +psa1(self={"20": 1}): !result: [['abcArgsPSA1'], {'20': 1}] +psa2(self={"20": 1}): !result: [[], {'20': 1}] +psa3(self={"20": 1}): !result: [['abcArgsPSA3'], {'20': 1}] +psa4(self={"20": 1}): !result: [[], {'20': 1}] +a.args: None +pa1.args: ['abcArgsPA1'] +pa2.args: None +pa3.args: ['abcArgsPA3'] +pa4.args: None +sa.args: None +psa1.args: ['abcArgsPSA1'] +psa2.args: None +psa3.args: ['abcArgsPSA3'] +psa4.args: None +a.self: None +pa1.self: None +pa2.self: None +pa3.self: {'abcSelfPA3': 'abcSelfPA3Val'} +pa4.self: {'abcSelfPA4': 'abcSelfPA4Val'} +sa.self: None +psa1.self: None +psa2.self: None +psa3.self: {'abcSelfPSA3': 'abcSelfPSA3Val'} +psa4.self: {'abcSelfPSA4': 'abcSelfPSA4Val'} +a.name: 'Args' +pa1.name: 'Args' +pa2.name: 'Args' +pa3.name: 'Args' +pa4.name: 'Args' +sa.name: 'SelfArgs' +psa1.name: 'SelfArgs' +psa2.name: 'SelfArgs' +psa3.name: 'SelfArgs' +psa4.name: 'SelfArgs' ' abcdef -line : +Error detected while processing function RunTest[]..Test: +line : abcdef abcA -line : +line : abcB' ['a', 'dup_a'] ['a', 'a'] @@ -1046,9 +1143,20 @@ l.locked = FailingTrue():NotImplementedE l.xxx = True:AttributeError:('cannot set attribute xxx',) > Function >> FunctionConstructor +>>> FunctionConstructor vim.Function("123"):ValueError:('unnamed function 123 does not exist',) vim.Function("xxx_non_existent_function_xxx"):ValueError:('function xxx_non_existent_function_xxx does not exist',) vim.Function("xxx#non#existent#function#xxx"):NOT FAILED +vim.Function("xxx_non_existent_function_xxx2", args=[]):ValueError:('function xxx_non_existent_function_xxx2 does not exist',) +vim.Function("xxx_non_existent_function_xxx3", self={}):ValueError:('function xxx_non_existent_function_xxx3 does not exist',) +vim.Function("xxx_non_existent_function_xxx4", args=[], self={}):ValueError:('function xxx_non_existent_function_xxx4 does not exist',) +>>> FunctionNew +vim.Function("tr", self="abcFuncSelf"):TypeError:('unable to convert str to vim dictionary',) +vim.Function("tr", args=427423):TypeError:('unable to convert int to vim list',) +vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to vim dictionary',) +vim.Function(self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to vim dictionary',) +vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to vim dictionary',) +vim.Function("tr", ""):TypeError:('function takes exactly 1 argument (2 given)',) >> FunctionCall >>> Testing StringToChars using f({%s : 1}) f({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) diff --git a/src/testdir/test87.in b/src/testdir/test87.in --- a/src/testdir/test87.in +++ b/src/testdir/test87.in @@ -7,6 +7,7 @@ STARTTEST :lang C :fun Test() :py3 import vim +:py3 cb = vim.current.buffer :let l = [] :py3 l=vim.bindeval('l') :py3 f=vim.bindeval('function("strlen")') @@ -200,7 +201,15 @@ EOF :let l = [0, 1, 2, 3] :py3 l=vim.bindeval('l') :lockvar! l -:py3 l[2]='i' +py3 << EOF +def emsg(ei): + return ei[0].__name__ + ':' + repr(ei[1].args) + +try: + l[2]='i' +except vim.error: + cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info())) +EOF :$put =string(l) :unlockvar! l :" @@ -614,7 +623,6 @@ EOF : autocmd BufFilePre * python3 cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) :augroup END py3 << EOF -cb = vim.current.buffer # Tests BufferAppend and BufferItem cb.append(b[0]) # Tests BufferSlice and BufferAssSlice @@ -859,6 +867,175 @@ EOF :$put =string(py3eval('vim.List()')) :$put =string(py3eval('vim.List(iter(''abc7''))')) :$put =string(py3eval('vim.Function(''tr'')')) +:$put =string(py3eval('vim.Function(''tr'', args=[123, 3, 4])')) +:$put =string(py3eval('vim.Function(''tr'', args=[])')) +:$put =string(py3eval('vim.Function(''tr'', self={})')) +:$put =string(py3eval('vim.Function(''tr'', args=[123, 3, 4], self={})')) +:" +:" Test vim.Function +:function Args(...) +: return a:000 +:endfunction +:function SelfArgs(...) dict +: return [a:000, self] +:endfunction +:" The following four lines should not crash +:let Pt = function('tr', [[]], {'l': []}) +:py3 Pt = vim.bindeval('Pt') +:unlet Pt +:py3 del Pt +py3 << EOF +def ecall(out_prefix, func, *args, **kwargs): + line = out_prefix + ': ' + try: + ret = func(*args, **kwargs) + except Exception: + line += '!exception: ' + emsg(sys.exc_info()) + else: + line += '!result: ' + str(vim.Function('string')(ret), 'utf-8') + cb.append(line) +a = vim.Function('Args') +pa1 = vim.Function('Args', args=['abcArgsPA1']) +pa2 = vim.Function('Args', args=[]) +pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'}) +pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'}) +cb.append('a: ' + repr(a)) +cb.append('pa1: ' + repr(pa1)) +cb.append('pa2: ' + repr(pa2)) +cb.append('pa3: ' + repr(pa3)) +cb.append('pa4: ' + repr(pa4)) +sa = vim.Function('SelfArgs') +psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1']) +psa2 = vim.Function('SelfArgs', args=[]) +psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'}) +psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'}) +cb.append('sa: ' + repr(sa)) +cb.append('psa1: ' + repr(psa1)) +cb.append('psa2: ' + repr(psa2)) +cb.append('psa3: ' + repr(psa3)) +cb.append('psa4: ' + repr(psa4)) + +psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'}) +psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]] +psar.self['rec'] = psar +psar.self['self'] = psar.self +psar.self['args'] = psar.args + +try: + cb.append('psar: ' + repr(psar)) +except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) +EOF +:$put ='s(a): '.string(py3eval('a')) +:$put ='s(pa1): '.string(py3eval('pa1')) +:$put ='s(pa2): '.string(py3eval('pa2')) +:$put ='s(pa3): '.string(py3eval('pa3')) +:$put ='s(pa4): '.string(py3eval('pa4')) +:$put ='s(sa): '.string(py3eval('sa')) +:$put ='s(psa1): '.string(py3eval('psa1')) +:$put ='s(psa2): '.string(py3eval('psa2')) +:$put ='s(psa3): '.string(py3eval('psa3')) +:$put ='s(psa4): '.string(py3eval('psa4')) +: +:py3 ecall('a()', a, ) +:py3 ecall('pa1()', pa1, ) +:py3 ecall('pa2()', pa2, ) +:py3 ecall('pa3()', pa3, ) +:py3 ecall('pa4()', pa4, ) +:py3 ecall('sa()', sa, ) +:py3 ecall('psa1()', psa1, ) +:py3 ecall('psa2()', psa2, ) +:py3 ecall('psa3()', psa3, ) +:py3 ecall('psa4()', psa4, ) +: +:py3 ecall('a(42, 43)', a, 42, 43) +:py3 ecall('pa1(42, 43)', pa1, 42, 43) +:py3 ecall('pa2(42, 43)', pa2, 42, 43) +:py3 ecall('pa3(42, 43)', pa3, 42, 43) +:py3 ecall('pa4(42, 43)', pa4, 42, 43) +:py3 ecall('sa(42, 43)', sa, 42, 43) +:py3 ecall('psa1(42, 43)', psa1, 42, 43) +:py3 ecall('psa2(42, 43)', psa2, 42, 43) +:py3 ecall('psa3(42, 43)', psa3, 42, 43) +:py3 ecall('psa4(42, 43)', psa4, 42, 43) +: +:py3 ecall('a(42, self={"20": 1})', a, 42, self={'20': 1}) +:py3 ecall('pa1(42, self={"20": 1})', pa1, 42, self={'20': 1}) +:py3 ecall('pa2(42, self={"20": 1})', pa2, 42, self={'20': 1}) +:py3 ecall('pa3(42, self={"20": 1})', pa3, 42, self={'20': 1}) +:py3 ecall('pa4(42, self={"20": 1})', pa4, 42, self={'20': 1}) +:py3 ecall('sa(42, self={"20": 1})', sa, 42, self={'20': 1}) +:py3 ecall('psa1(42, self={"20": 1})', psa1, 42, self={'20': 1}) +:py3 ecall('psa2(42, self={"20": 1})', psa2, 42, self={'20': 1}) +:py3 ecall('psa3(42, self={"20": 1})', psa3, 42, self={'20': 1}) +:py3 ecall('psa4(42, self={"20": 1})', psa4, 42, self={'20': 1}) +: +:py3 ecall('a(self={"20": 1})', a, self={'20': 1}) +:py3 ecall('pa1(self={"20": 1})', pa1, self={'20': 1}) +:py3 ecall('pa2(self={"20": 1})', pa2, self={'20': 1}) +:py3 ecall('pa3(self={"20": 1})', pa3, self={'20': 1}) +:py3 ecall('pa4(self={"20": 1})', pa4, self={'20': 1}) +:py3 ecall('sa(self={"20": 1})', sa, self={'20': 1}) +:py3 ecall('psa1(self={"20": 1})', psa1, self={'20': 1}) +:py3 ecall('psa2(self={"20": 1})', psa2, self={'20': 1}) +:py3 ecall('psa3(self={"20": 1})', psa3, self={'20': 1}) +:py3 ecall('psa4(self={"20": 1})', psa4, self={'20': 1}) +py3 << EOF +def s(v): + if v is None: + return repr(v) + else: + return str(vim.Function('string')(v), 'utf-8') + +cb.append('a.args: ' + s(a.args)) +cb.append('pa1.args: ' + s(pa1.args)) +cb.append('pa2.args: ' + s(pa2.args)) +cb.append('pa3.args: ' + s(pa3.args)) +cb.append('pa4.args: ' + s(pa4.args)) +cb.append('sa.args: ' + s(sa.args)) +cb.append('psa1.args: ' + s(psa1.args)) +cb.append('psa2.args: ' + s(psa2.args)) +cb.append('psa3.args: ' + s(psa3.args)) +cb.append('psa4.args: ' + s(psa4.args)) + +cb.append('a.self: ' + s(a.self)) +cb.append('pa1.self: ' + s(pa1.self)) +cb.append('pa2.self: ' + s(pa2.self)) +cb.append('pa3.self: ' + s(pa3.self)) +cb.append('pa4.self: ' + s(pa4.self)) +cb.append('sa.self: ' + s(sa.self)) +cb.append('psa1.self: ' + s(psa1.self)) +cb.append('psa2.self: ' + s(psa2.self)) +cb.append('psa3.self: ' + s(psa3.self)) +cb.append('psa4.self: ' + s(psa4.self)) + +cb.append('a.name: ' + s(a.name)) +cb.append('pa1.name: ' + s(pa1.name)) +cb.append('pa2.name: ' + s(pa2.name)) +cb.append('pa3.name: ' + s(pa3.name)) +cb.append('pa4.name: ' + s(pa4.name)) +cb.append('sa.name: ' + s(sa.name)) +cb.append('psa1.name: ' + s(psa1.name)) +cb.append('psa2.name: ' + s(psa2.name)) +cb.append('psa3.name: ' + s(psa3.name)) +cb.append('psa4.name: ' + s(psa4.name)) + +del s + +del a +del pa1 +del pa2 +del pa3 +del pa4 +del sa +del psa1 +del psa2 +del psa3 +del psa4 +del psar + +del ecall +EOF :" :" Test stdout/stderr :redir => messages @@ -1134,7 +1311,7 @@ ee('vim.foreach_rtp(FailingCall())') ee('vim.foreach_rtp(int, 2)') cb.append('> import') old_rtp = vim.options['rtp'] -vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') +vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,') ee('import xxx_no_such_module_xxx') ee('import failing_import') ee('import failing') @@ -1218,9 +1395,20 @@ ee('l.locked = FailingTrue()') ee('l.xxx = True') cb.append("> Function") cb.append(">> FunctionConstructor") +cb.append(">>> FunctionConstructor") ee('vim.Function("123")') ee('vim.Function("xxx_non_existent_function_xxx")') ee('vim.Function("xxx#non#existent#function#xxx")') +ee('vim.Function("xxx_non_existent_function_xxx2", args=[])') +ee('vim.Function("xxx_non_existent_function_xxx3", self={})') +ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})') +cb.append(">>> FunctionNew") +ee('vim.Function("tr", self="abcFuncSelf")') +ee('vim.Function("tr", args=427423)') +ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")') +ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")') +ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")') +ee('vim.Function("tr", "")') cb.append(">> FunctionCall") convertfrompyobject_test('f(%s)') convertfrompymapping_test('fd(self=%s)') @@ -1374,16 +1562,16 @@ def test_keyboard_interrupt(): vim.command('while 1 | endwhile') except KeyboardInterrupt: cb.append('Caught KeyboardInterrupt') - except Exception as e: - cb.append('!!!!!!!! Caught exception: ' + repr(e)) + except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) else: cb.append('!!!!!!!! No exception') try: vim.command('$ put =\'Running :put\'') except KeyboardInterrupt: cb.append('!!!!!!!! Caught KeyboardInterrupt') - except Exception as e: - cb.append('!!!!!!!! Caught exception: ' + repr(e)) + except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) else: cb.append('No exception') EOF @@ -1403,6 +1591,7 @@ EOF py3 << EOF del cb del ee +del emsg del sys del os del vim @@ -1434,8 +1623,9 @@ EOF :call garbagecollect(1) :" :/^start:/,$wq! test.out +:/^start:/,$w! test.out :" vim: et ts=4 isk-=\: -:call getchar() +:while getchar(0) isnot 0|endwhile ENDTEST start: diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok --- a/src/testdir/test87.ok +++ b/src/testdir/test87.ok @@ -57,6 +57,7 @@ None [0, 1, 2, 3, 4, 5, 6, 7] [0, 1, 2, 3, 4, 5, 6, 7] [0, 1, 2, 3, 4, 5, 6, 7] +l[2] threw vim.error: error:('list is locked',) [0, 1, 2, 3] [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd'] [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}] @@ -447,7 +448,7 @@ tabpage:__dir__,number,valid,vars,window range:__dir__,append,end,start dictionary:__dir__,get,has_key,items,keys,locked,pop,popitem,scope,update,values list:__dir__,extend,locked -function:__dir__,softspace +function:__dir__,args,self,softspace output:__dir__,close,flush,isatty,readable,seekable,softspace,writable,write,writelines {} {'a': 1} @@ -455,12 +456,108 @@ output:__dir__,close,flush,isatty,readab [] ['a', 'b', 'c', '7'] function('tr') +function('tr', [123, 3, 4]) +function('tr') +function('tr', {}) +function('tr', [123, 3, 4], {}) +a: +pa1: +pa2: +pa3: +pa4: +sa: +psa1: +psa2: +psa3: +psa4: +psar: +s(a): function('Args') +s(pa1): function('Args', ['abcArgsPA1']) +s(pa2): function('Args') +s(pa3): function('Args', ['abcArgsPA3'], {'abcSelfPA3': 'abcSelfPA3Val'}) +s(pa4): function('Args', {'abcSelfPA4': 'abcSelfPA4Val'}) +s(sa): function('SelfArgs') +s(psa1): function('SelfArgs', ['abcArgsPSA1']) +s(psa2): function('SelfArgs') +s(psa3): function('SelfArgs', ['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}) +s(psa4): function('SelfArgs', {'abcSelfPSA4': 'abcSelfPSA4Val'}) +a(): !result: [] +pa1(): !result: ['abcArgsPA1'] +pa2(): !result: [] +pa3(): !result: ['abcArgsPA3'] +pa4(): !result: [] +sa(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa1(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa2(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa3(): !result: [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] +psa4(): !result: [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] +a(42, 43): !result: [42, 43] +pa1(42, 43): !result: ['abcArgsPA1', 42, 43] +pa2(42, 43): !result: [42, 43] +pa3(42, 43): !result: ['abcArgsPA3', 42, 43] +pa4(42, 43): !result: [42, 43] +sa(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa1(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa2(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) +psa3(42, 43): !result: [['abcArgsPSA3', 42, 43], {'abcSelfPSA3': 'abcSelfPSA3Val'}] +psa4(42, 43): !result: [[42, 43], {'abcSelfPSA4': 'abcSelfPSA4Val'}] +a(42, self={"20": 1}): !result: [42] +pa1(42, self={"20": 1}): !result: ['abcArgsPA1', 42] +pa2(42, self={"20": 1}): !result: [42] +pa3(42, self={"20": 1}): !result: ['abcArgsPA3', 42] +pa4(42, self={"20": 1}): !result: [42] +sa(42, self={"20": 1}): !result: [[42], {'20': 1}] +psa1(42, self={"20": 1}): !result: [['abcArgsPSA1', 42], {'20': 1}] +psa2(42, self={"20": 1}): !result: [[42], {'20': 1}] +psa3(42, self={"20": 1}): !result: [['abcArgsPSA3', 42], {'20': 1}] +psa4(42, self={"20": 1}): !result: [[42], {'20': 1}] +a(self={"20": 1}): !result: [] +pa1(self={"20": 1}): !result: ['abcArgsPA1'] +pa2(self={"20": 1}): !result: [] +pa3(self={"20": 1}): !result: ['abcArgsPA3'] +pa4(self={"20": 1}): !result: [] +sa(self={"20": 1}): !result: [[], {'20': 1}] +psa1(self={"20": 1}): !result: [['abcArgsPSA1'], {'20': 1}] +psa2(self={"20": 1}): !result: [[], {'20': 1}] +psa3(self={"20": 1}): !result: [['abcArgsPSA3'], {'20': 1}] +psa4(self={"20": 1}): !result: [[], {'20': 1}] +a.args: None +pa1.args: ['abcArgsPA1'] +pa2.args: None +pa3.args: ['abcArgsPA3'] +pa4.args: None +sa.args: None +psa1.args: ['abcArgsPSA1'] +psa2.args: None +psa3.args: ['abcArgsPSA3'] +psa4.args: None +a.self: None +pa1.self: None +pa2.self: None +pa3.self: {'abcSelfPA3': 'abcSelfPA3Val'} +pa4.self: {'abcSelfPA4': 'abcSelfPA4Val'} +sa.self: None +psa1.self: None +psa2.self: None +psa3.self: {'abcSelfPSA3': 'abcSelfPSA3Val'} +psa4.self: {'abcSelfPSA4': 'abcSelfPSA4Val'} +a.name: 'Args' +pa1.name: 'Args' +pa2.name: 'Args' +pa3.name: 'Args' +pa4.name: 'Args' +sa.name: 'SelfArgs' +psa1.name: 'SelfArgs' +psa2.name: 'SelfArgs' +psa3.name: 'SelfArgs' +psa4.name: 'SelfArgs' ' abcdef -line : +Error detected while processing function RunTest[]..Test: +line : abcdef abcA -line : +line : abcB' ['a', 'dup_a'] ['a', 'a'] @@ -1046,9 +1143,20 @@ l.locked = FailingTrue():(, AttributeError('cannot set attribute xxx',)) > Function >> FunctionConstructor +>>> FunctionConstructor vim.Function("123"):(, ValueError('unnamed function 123 does not exist',)) vim.Function("xxx_non_existent_function_xxx"):(, ValueError('function xxx_non_existent_function_xxx does not exist',)) vim.Function("xxx#non#existent#function#xxx"):NOT FAILED +vim.Function("xxx_non_existent_function_xxx2", args=[]):(, ValueError('function xxx_non_existent_function_xxx2 does not exist',)) +vim.Function("xxx_non_existent_function_xxx3", self={}):(, ValueError('function xxx_non_existent_function_xxx3 does not exist',)) +vim.Function("xxx_non_existent_function_xxx4", args=[], self={}):(, ValueError('function xxx_non_existent_function_xxx4 does not exist',)) +>>> FunctionNew +vim.Function("tr", self="abcFuncSelf"):(, AttributeError('keys',)) +vim.Function("tr", args=427423):(, TypeError('unable to convert int to vim list',)) +vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) +vim.Function(self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) +vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) +vim.Function("tr", ""):(, TypeError('function takes exactly 1 argument (2 given)',)) >> FunctionCall >>> Testing StringToChars using f({%s : 1}) f({1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -749,6 +749,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1731, +/**/ 1730, /**/ 1729,