# HG changeset patch # User Bram Moolenaar # Date 1369912453 -7200 # Node ID 4157fef7b95074cac55977127964adfa883ec030 # Parent aaf27e4277492bb0d6ac8c01b8b26aa4b4978c7e updated for version 7.3.1063 Problem: Python: Function is not standard. Solution: Python patch 22: make Function subclassable. (ZyX) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -21933,6 +21933,15 @@ free_all_functions() } #endif + int +translated_function_exists(name) + char_u *name; +{ + if (builtin_function(name)) + return find_internal_func(name) >= 0; + return find_func(name) != NULL; +} + /* * Return TRUE if a function "name" exists. */ @@ -21950,12 +21959,7 @@ function_exists(name) /* Only accept "funcname", "funcname ", "funcname (..." and * "funcname(...", not "funcname!...". */ if (p != NULL && (*nm == NUL || *nm == '(')) - { - if (builtin_function(p)) - n = (find_internal_func(p) >= 0); - else - n = (find_func(p) != NULL); - } + n = translated_function_exists(p); vim_free(p); return n; } @@ -21971,18 +21975,9 @@ get_expanded_name(name, check) p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL); if (p != NULL && *nm == NUL) - { - if (!check) + if (!check || translated_function_exists(p)) return p; - else if (builtin_function(p)) - { - if (find_internal_func(p) >= 0) - return p; - } - else - if (find_func(p) != NULL) - return p; - } + vim_free(p); return NULL; } 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 @@ -1991,30 +1991,73 @@ typedef struct static PyTypeObject FunctionType; +#define NEW_FUNCTION(name) FunctionNew(&FunctionType, name) + static PyObject * -FunctionNew(char_u *name) +FunctionNew(PyTypeObject *subtype, char_u *name) { FunctionObject *self; - self = PyObject_NEW(FunctionObject, &FunctionType); + self = (FunctionObject *) subtype->tp_alloc(subtype, 0); + if (self == NULL) return NULL; - self->name = PyMem_New(char_u, STRLEN(name) + 1); - if (self->name == NULL) + + if (isdigit(*name)) + { + if (!translated_function_exists(name)) + { + PyErr_SetString(PyExc_ValueError, + _("unnamed function does not exist")); + return NULL; + } + self->name = vim_strsave(name); + func_ref(self->name); + } + else { - PyErr_NoMemory(); + self->name = get_expanded_name(name, TRUE); + if (self->name == NULL) + { + if (script_autoload(name, TRUE) && !aborting()) + self->name = get_expanded_name(name, TRUE); + if (self->name == NULL) + { + PyErr_SetString(PyExc_ValueError, _("function does not exist")); + return NULL; + } + } + } + + return (PyObject *)(self); +} + + static PyObject * +FunctionConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) +{ + PyObject *self; + char_u *name; + + if (kwargs) + { + PyErr_SetString(PyExc_TypeError, + _("function constructor does not accept keyword arguments")); return NULL; } - STRCPY(self->name, name); - func_ref(name); - return (PyObject *)(self); + + if (!PyArg_ParseTuple(args, "s", &name)) + return NULL; + + self = FunctionNew(subtype, name); + + return self; } static void FunctionDestructor(FunctionObject *self) { func_unref(self->name); - PyMem_Free(self->name); + vim_free(self->name); DESTRUCTOR_FINISH(self); } @@ -2093,7 +2136,6 @@ FunctionRepr(FunctionObject *self) } static struct PyMethodDef FunctionMethods[] = { - {"__call__",(PyCFunction)FunctionCall, METH_VARARGS|METH_KEYWORDS, ""}, {"__dir__", (PyCFunction)FunctionDir, METH_NOARGS, ""}, { NULL, NULL, 0, NULL} }; @@ -4895,7 +4937,7 @@ ConvertToPyObject(typval_T *tv) case VAR_DICT: return NEW_DICTIONARY(tv->vval.v_dict); case VAR_FUNC: - return FunctionNew(tv->vval.v_string == NULL + return NEW_FUNCTION(tv->vval.v_string == NULL ? (char_u *)"" : tv->vval.v_string); case VAR_UNKNOWN: Py_INCREF(Py_None); @@ -5105,10 +5147,12 @@ init_structs(void) FunctionType.tp_basicsize = sizeof(FunctionObject); FunctionType.tp_dealloc = (destructor)FunctionDestructor; FunctionType.tp_call = (ternaryfunc)FunctionCall; - FunctionType.tp_flags = Py_TPFLAGS_DEFAULT; + FunctionType.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE; FunctionType.tp_doc = "object that calls vim function"; FunctionType.tp_methods = FunctionMethods; FunctionType.tp_repr = (reprfunc)FunctionRepr; + FunctionType.tp_new = (newfunc)FunctionConstructor; + FunctionType.tp_alloc = (allocfunc)PyType_GenericAlloc; #if PY_MAJOR_VERSION >= 3 FunctionType.tp_getattro = (getattrofunc)FunctionGetattro; #else diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -79,6 +79,7 @@ void dict_extend __ARGS((dict_T *d1, dic char_u *get_function_name __ARGS((expand_T *xp, int idx)); char_u *get_expr_name __ARGS((expand_T *xp, int idx)); char_u *get_expanded_name __ARGS((char_u *name, int check)); +int translated_function_exists __ARGS((char_u *name)); int func_call __ARGS((char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv)); void mzscheme_call_vim __ARGS((char_u *name, typval_T *args, typval_T *rettv)); long do_searchpair __ARGS((char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit)); diff --git a/src/testdir/test86.in b/src/testdir/test86.in --- a/src/testdir/test86.in +++ b/src/testdir/test86.in @@ -31,6 +31,9 @@ STARTTEST :" :" Extending Dictionary directly with different types :let d = {} +:fun d.f() +: return 1 +:endfun py << EOF d=vim.bindeval('d') d['1']='asd' @@ -44,12 +47,13 @@ dk.sort(key=repr) dv.sort(key=repr) di.sort(key=repr) EOF +:$put =pyeval('d[''f''](self={})') :$put =pyeval('repr(dk)') :$put =substitute(pyeval('repr(dv)'),'0x\x\+','','g') :$put =substitute(pyeval('repr(di)'),'0x\x\+','','g') -:for [key, val] in sort(items(d)) -: $put =string(key) . ' : ' . string(val) -: unlet key val +:for [key, Val] in sort(items(d)) +: $put =string(key) . ' : ' . string(Val) +: unlet key Val :endfor :" :" removing items with del @@ -66,6 +70,7 @@ EOF :$put =string(l) :" :py del d['-1'] +:py del d['f'] :$put =string(pyeval('d.get(''b'', 1)')) :$put =string(pyeval('d.pop(''b'')')) :$put =string(pyeval('d.get(''b'', 1)')) @@ -187,9 +192,10 @@ EOF :catch : $put =v:exception[:16] :endtry +:py f=l[0] :delfunction New :try -: py l[0](1, 2, 3) +: py f(1, 2, 3) :catch : $put =v:exception[:16] :endtry @@ -737,6 +743,7 @@ EOF :$put =string(pyeval('vim.Dictionary(((''a'', 1),))')) :$put =string(pyeval('vim.List()')) :$put =string(pyeval('vim.List(iter(''abc''))')) +:$put =string(pyeval('vim.Function(''tr'')')) :" :" Test stdout/stderr :redir => messages @@ -747,6 +754,10 @@ EOF :redir END :$put =string(substitute(messages, '\d\+', '', 'g')) :" Test subclassing +:fun Put(...) +: $put =string(a:000) +: return a:000 +:endfun py << EOF class DupDict(vim.Dictionary): def __setitem__(self, key, value): @@ -762,10 +773,17 @@ class DupList(vim.List): dl = DupList() dl2 = DupList(iter('abc')) dl.extend(dl2[0]) + +class DupFun(vim.Function): + def __call__(self, arg): + return super(DupFun, self).__call__(arg, arg) + +df = DupFun('Put') EOF :$put =string(sort(keys(pyeval('dd')))) :$put =string(pyeval('dl')) :$put =string(pyeval('dl2')) +:$put =string(pyeval('df(2)')) :" :" Test exceptions :fun Exe(e) diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok --- a/src/testdir/test86.ok +++ b/src/testdir/test86.ok @@ -4,13 +4,15 @@ start: Vim(put):E684: [0, 'as''d', [1, 2, function('strlen'), {'a': 1}]] [0, function('strlen'), [1, 2, function('strlen'), {'a': 1}]] -['-1', '0', '1', 'b'] -['asd', -1L, , ] -[('-1', ), ('0', -1L), ('1', 'asd'), ('b', )] +1 +['-1', '0', '1', 'b', 'f'] +['asd', -1L, , , ] +[('-1', ), ('0', -1L), ('1', 'asd'), ('b', ), ('f', )] '-1' : {'a': 1} '0' : -1 '1' : 'asd' 'b' : [1, 2, function('strlen')] +'f' : function('1') [0, function('strlen')] [3] [1, 2, function('strlen')] @@ -407,13 +409,14 @@ 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:__call__,__dir__,__members__,softspace +function:__dir__,__members__,softspace output:__dir__,__members__,flush,softspace,write,writelines {} {'a': 1} {'a': 1} [] ['a', 'b', 'c'] +function('tr') ' abcdef line : @@ -424,6 +427,8 @@ abc' ['a', 'dup_a'] ['a', 'a'] ['a', 'b', 'c'] +[2, 2] +[2, 2] (, error('abc',)) (, error('def',)) (, error('ghi',)) diff --git a/src/testdir/test87.in b/src/testdir/test87.in --- a/src/testdir/test87.in +++ b/src/testdir/test87.in @@ -26,6 +26,9 @@ STARTTEST :" :" Extending Dictionary directly with different types :let d = {} +:fun d.f() +: return 1 +:endfun py3 << EOF d=vim.bindeval('d') d['1']='asd' @@ -39,12 +42,13 @@ dk.sort(key=repr) dv.sort(key=repr) di.sort(key=repr) EOF +:$put =py3eval('d[''f''](self={})') :$put =py3eval('repr(dk)') :$put =substitute(py3eval('repr(dv)'),'0x\x\+','','g') :$put =substitute(py3eval('repr(di)'),'0x\x\+','','g') -:for [key, val] in sort(items(d)) -: $put =string(key) . ' : ' . string(val) -: unlet key val +:for [key, Val] in sort(items(d)) +: $put =string(key) . ' : ' . string(Val) +: unlet key Val :endfor :" :" removing items with del @@ -61,6 +65,7 @@ EOF :$put =string(l) :" :py3 del d['-1'] +:py3 del d['f'] :$put =string(py3eval('d.get(''b'', 1)')) :$put =string(py3eval('d.pop(''b'')')) :$put =string(py3eval('d.get(''b'', 1)')) @@ -182,9 +187,10 @@ EOF :catch : $put =v:exception[:13] :endtry +:py3 f=l[0] :delfunction New :try -: py3 l[0](1, 2, 3) +: py3 f(1, 2, 3) :catch : $put =v:exception[:13] :endtry @@ -698,6 +704,7 @@ EOF :$put =string(py3eval('vim.Dictionary(((''a'', 1),))')) :$put =string(py3eval('vim.List()')) :$put =string(py3eval('vim.List(iter(''abc''))')) +:$put =string(py3eval('vim.Function(''tr'')')) :" :" Test stdout/stderr :redir => messages @@ -708,6 +715,10 @@ EOF :redir END :$put =string(substitute(messages, '\d\+', '', 'g')) :" Test subclassing +:fun Put(...) +: $put =string(a:000) +: return a:000 +:endfun py3 << EOF class DupDict(vim.Dictionary): def __setitem__(self, key, value): @@ -723,10 +734,17 @@ class DupList(vim.List): dl = DupList() dl2 = DupList(iter('abc')) dl.extend(dl2[0]) + +class DupFun(vim.Function): + def __call__(self, arg): + return super(DupFun, self).__call__(arg, arg) + +df = DupFun('Put') EOF :$put =string(sort(keys(py3eval('dd')))) :$put =string(py3eval('dl')) :$put =string(py3eval('dl2')) +:$put =string(py3eval('df(2)')) :" :" Test exceptions :fun Exe(e) diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok --- a/src/testdir/test87.ok +++ b/src/testdir/test87.ok @@ -4,13 +4,15 @@ start: Vim(put):E684: [0, 'as''d', [1, 2, function('strlen'), {'a': 1}]] [0, function('strlen'), [1, 2, function('strlen'), {'a': 1}]] -[b'-1', b'0', b'1', b'b'] -[-1, , , b'asd'] -[(b'-1', ), (b'0', -1), (b'1', b'asd'), (b'b', )] +1 +[b'-1', b'0', b'1', b'b', b'f'] +[-1, , , , b'asd'] +[(b'-1', ), (b'0', -1), (b'1', b'asd'), (b'b', ), (b'f', )] '-1' : {'a': 1} '0' : -1 '1' : 'asd' 'b' : [1, 2, function('strlen')] +'f' : function('1') [0, function('strlen')] [3] [1, 2, function('strlen')] @@ -396,13 +398,14 @@ 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:__call__,__dir__,softspace +function:__dir__,softspace output:__dir__,flush,softspace,write,writelines {} {'a': 1} {'a': 1} [] ['a', 'b', 'c'] +function('tr') ' abcdef line : @@ -413,6 +416,8 @@ abc' ['a', 'dup_a'] ['a', 'a'] ['a', 'b', 'c'] +[2, 2] +[2, 2] (, error('abc',)) (, error('def',)) (, error('ghi',)) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1063, +/**/ 1062, /**/ 1061,