[vim] 45/139: patch 7.4.1731 Problem: Python: turns partial into simple funcref. Solution: Use partials like partials. (Nikolai Pavlov, closes #734)

James McCoy jamessan at debian.org
Fri May 6 03:59:59 UTC 2016


This is an automated email from the git hooks/post-receive script.

jamessan pushed a commit to branch debian/sid
in repository vim.

commit 8110a091bc749d8748a20807a724a3af3ca6d509
Author: Bram Moolenaar <Bram at vim.org>
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 #734)
---
 runtime/doc/if_pyth.txt |  28 ++++-
 src/eval.c              |   3 +-
 src/if_py_both.h        | 294 +++++++++++++++++++++++++++++++++++++++++++-----
 src/if_python.c         |  10 +-
 src/if_python3.c        |  10 +-
 src/proto/eval.pro      |   1 +
 src/testdir/test86.in   | 203 +++++++++++++++++++++++++++++++--
 src/testdir/test86.ok   | 114 ++++++++++++++++++-
 src/testdir/test87.in   | 206 +++++++++++++++++++++++++++++++--
 src/testdir/test87.ok   | 114 ++++++++++++++++++-
 src/version.c           |   2 +
 11 files changed, 923 insertions(+), 62 deletions(-)

diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt
index 4f79c57..3d91814 100644
--- 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
index 9dd4d84..5ced88e 100644
--- 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
index ee88260..d6ae880 100644
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -72,6 +72,7 @@ typedef void (*runner)(const char *, void *
 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_u *name)
 	    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_u *name)
 FunctionConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
 {
     PyObject	*self;
+    PyObject	*selfdictObject;
+    PyObject	*argsObject = NULL;
     char_u	*name;
+    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)
+    if (kwargs != NULL)
     {
-	PyErr_SET_STRING(PyExc_TypeError,
-		N_("function constructor does not accept keyword arguments"));
-	return 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 *subtype, PyObject *args, PyObject *kwargs)
     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, PyObject *argsObject, PyObject *kwargs)
     PyObject	*selfdictObject;
     PyObject	*ret;
     int		error;
+    partial_T	pt;
+    partial_T	*pt_ptr = NULL;
 
-    if (ConvertFromPyObject(argsObject, &args) == -1)
+    if (ConvertFromPySequence(argsObject, &args) == -1)
 	return NULL;
 
     if (kwargs != NULL)
@@ -2940,11 +3067,17 @@ FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs)
 	}
     }
 
+    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, PyObject *argsObject, PyObject *kwargs)
     static PyObject *
 FunctionRepr(FunctionObject *self)
 {
-#ifdef Py_TRACE_REFS
-    /* For unknown reason self->name may be NULL after calling
-     * Finalize */
-    return PyString_FromFormat("<vim.Function '%s'>",
-	    (self->name == NULL ? "<NULL>" : (char *)self->name));
-#else
-    return PyString_FromFormat("<vim.Function '%s'>", (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 *)"<vim.Function '");
+    if (self->name)
+	ga_concat(&repr_ga, self->name);
+    else
+	ga_concat(&repr_ga, (char_u *)"<NULL>");
+    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, typval_T *tv)
 }
 
     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, typval_T *tv, PyObject *lookup_dict)
     }
     else if (PyType_IsSubtype(obj->ob_type, &FunctionType))
     {
-	if (set_string_copy(((FunctionObject *) (obj))->name, tv) == -1)
-	    return -1;
+	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(tv->vval.v_string);
+	    tv->v_type = VAR_FUNC;
+	}
+	func_ref(func->name);
     }
     else if (PyBytes_Check(obj))
     {
@@ -6009,6 +6236,8 @@ _ConvertFromPyObject(PyObject *obj, typval_T *tv, PyObject *lookup_dict)
     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
index fa3fc88..a54a0e2 100644
--- 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
index 5cf508f..d2f6066 100644
--- a/src/if_python3.c
+++ b/src/if_python3.c
@@ -1528,14 +1528,16 @@ ListSetattro(PyObject *self, PyObject *nameobj, PyObject *val)
     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
index 9fda13c..60fad80 100644
--- 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
index cc76cff..6f47ff6 100644
--- 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("<abuf>")') + ':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
index d103909..fe27c05 100644
--- 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,vars,window,windows
 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,isatty,readable,seekable,softspace,writab
 []
 ['a', 'b', 'c', '7']
 function('tr')
+function('tr', [123, 3, 4])
+function('tr')
+function('tr', {})
+function('tr', [123, 3, 4], {})
+a: <vim.Function 'Args'>
+pa1: <vim.Function 'Args', args=['abcArgsPA1']>
+pa2: <vim.Function 'Args'>
+pa3: <vim.Function 'Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'}>
+pa4: <vim.Function 'Args', self={'abcSelfPA4': 'abcSelfPA4Val'}>
+sa: <vim.Function 'SelfArgs'>
+psa1: <vim.Function 'SelfArgs', args=['abcArgsPSA1']>
+psa2: <vim.Function 'SelfArgs'>
+psa3: <vim.Function 'SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'}>
+psa4: <vim.Function 'SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'}>
+psar: <vim.Function 'SelfArgs', args=[{'abcArgsPSAr2': [{'rec': function('SelfArgs', [{...}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], self={'rec': function('SelfArgs', [{'abcArgsPSAr2': [{...}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}>
+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():NotImplementedError:('bool',)
 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
index 535a143..e3bc994 100644
--- 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("<abuf>")') + ':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
index 1d9b6e2..25c0b51 100644
--- 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,windows
 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,readable,seekable,softspace,writable,write,wri
 []
 ['a', 'b', 'c', '7']
 function('tr')
+function('tr', [123, 3, 4])
+function('tr')
+function('tr', {})
+function('tr', [123, 3, 4], {})
+a: <vim.Function 'Args'>
+pa1: <vim.Function 'Args', args=['abcArgsPA1']>
+pa2: <vim.Function 'Args'>
+pa3: <vim.Function 'Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'}>
+pa4: <vim.Function 'Args', self={'abcSelfPA4': 'abcSelfPA4Val'}>
+sa: <vim.Function 'SelfArgs'>
+psa1: <vim.Function 'SelfArgs', args=['abcArgsPSA1']>
+psa2: <vim.Function 'SelfArgs'>
+psa3: <vim.Function 'SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'}>
+psa4: <vim.Function 'SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'}>
+psar: <vim.Function 'SelfArgs', args=[{'abcArgsPSAr2': [{'rec': function('SelfArgs', [{...}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], self={'rec': function('SelfArgs', [{'abcArgsPSAr2': [{...}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}>
+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():(<class 'NotImplementedError'>, NotImplementedError('bo
 l.xxx = True:(<class 'AttributeError'>, AttributeError('cannot set attribute xxx',))
 > Function
 >> FunctionConstructor
+>>> FunctionConstructor
 vim.Function("123"):(<class 'ValueError'>, ValueError('unnamed function 123 does not exist',))
 vim.Function("xxx_non_existent_function_xxx"):(<class 'ValueError'>, 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=[]):(<class 'ValueError'>, ValueError('function xxx_non_existent_function_xxx2 does not exist',))
+vim.Function("xxx_non_existent_function_xxx3", self={}):(<class 'ValueError'>, ValueError('function xxx_non_existent_function_xxx3 does not exist',))
+vim.Function("xxx_non_existent_function_xxx4", args=[], self={}):(<class 'ValueError'>, ValueError('function xxx_non_existent_function_xxx4 does not exist',))
+>>> FunctionNew
+vim.Function("tr", self="abcFuncSelf"):(<class 'AttributeError'>, AttributeError('keys',))
+vim.Function("tr", args=427423):(<class 'TypeError'>, TypeError('unable to convert int to vim list',))
+vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2"):(<class 'AttributeError'>, AttributeError('keys',))
+vim.Function(self="abcFuncSelf2", args="abcFuncArgs2"):(<class 'AttributeError'>, AttributeError('keys',))
+vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2"):(<class 'AttributeError'>, AttributeError('keys',))
+vim.Function("tr", ""):(<class 'TypeError'>, TypeError('function takes exactly 1 argument (2 given)',))
 >> FunctionCall
 >>> Testing StringToChars using f({%s : 1})
 f({1 : 1}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',))
diff --git a/src/version.c b/src/version.c
index 7132e0b..04545d9 100644
--- 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,

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vim/vim.git



More information about the pkg-vim-maintainers mailing list