# HG changeset patch # User Bram Moolenaar # Date 1369859770 -7200 # Node ID 89bec74fd7936cc425b42f9e882f1f3864b1c95d # Parent 18914d6c96c801dfe1b23c0e9784c9fc6814176d updated for version 7.3.1047 Problem: Python: dir() does not work properly. Solution: Python patch 8. Add __dir__ method to all objects with custom tp_getattr supplemented by __members__ attribute for at least python-2* versions. __members__ is not mentioned in python-3* dir() output even if it is accessible. (ZyX) 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 @@ -117,6 +117,59 @@ StringToChars(PyObject *object, PyObject return (char_u *) p; } + static int +add_string(PyObject *list, char *s) +{ + PyObject *string; + + if (!(string = PyString_FromString(s))) + return -1; + if (PyList_Append(list, string)) + { + Py_DECREF(string); + return -1; + } + + Py_DECREF(string); + return 0; +} + + static PyObject * +ObjectDir(PyObject *self, char **attributes) +{ + PyMethodDef *method; + char **attr; + PyObject *r; + + if (!(r = PyList_New(0))) + return NULL; + + if (self) + for (method = self->ob_type->tp_methods ; method->ml_name != NULL ; ++method) + if (add_string(r, (char *) method->ml_name)) + { + Py_DECREF(r); + return NULL; + } + + for (attr = attributes ; *attr ; ++attr) + if (add_string(r, *attr)) + { + Py_DECREF(r); + return NULL; + } + +#if PY_MAJOR_VERSION < 3 + if (add_string(r, "__members__")) + { + Py_DECREF(r); + return NULL; + } +#endif + + return r; +} + /* Output buffer management */ @@ -132,6 +185,17 @@ typedef struct long error; } OutputObject; +static char *OutputAttrs[] = { + "softspace", + NULL +}; + + static PyObject * +OutputDir(PyObject *self) +{ + return ObjectDir(self, OutputAttrs); +} + static int OutputSetattr(OutputObject *self, char *name, PyObject *val) { @@ -291,6 +355,7 @@ static struct PyMethodDef OutputMethods[ {"write", (PyCFunction)OutputWrite, METH_VARARGS, ""}, {"writelines", (PyCFunction)OutputWritelines, METH_VARARGS, ""}, {"flush", (PyCFunction)OutputFlush, METH_NOARGS, ""}, + {"__dir__", (PyCFunction)OutputDir, METH_NOARGS, ""}, { NULL, NULL, 0, NULL} }; @@ -826,6 +891,17 @@ DictionaryDestructor(DictionaryObject *s DESTRUCTOR_FINISH(self); } +static char *DictionaryAttrs[] = { + "locked", "scope", + NULL +}; + + static PyObject * +DictionaryDir(PyObject *self) +{ + return ObjectDir(self, DictionaryAttrs); +} + static int DictionarySetattr(DictionaryObject *self, char *name, PyObject *val) { @@ -985,7 +1061,8 @@ static PyMappingMethods DictionaryAsMapp static struct PyMethodDef DictionaryMethods[] = { {"keys", (PyCFunction)DictionaryListKeys, METH_NOARGS, ""}, - { NULL, NULL, 0, NULL } + {"__dir__", (PyCFunction)DictionaryDir, METH_NOARGS, ""}, + { NULL, NULL, 0, NULL} }; static PyTypeObject ListType; @@ -1331,6 +1408,17 @@ ListConcatInPlace(ListObject *self, PyOb return (PyObject *)(self); } +static char *ListAttrs[] = { + "locked", + NULL +}; + + static PyObject * +ListDir(PyObject *self) +{ + return ObjectDir(self, ListAttrs); +} + static int ListSetattr(ListObject *self, char *name, PyObject *val) { @@ -1368,8 +1456,9 @@ ListSetattr(ListObject *self, char *name } static struct PyMethodDef ListMethods[] = { - {"extend", (PyCFunction)ListConcatInPlace, METH_O, ""}, - { NULL, NULL, 0, NULL } + {"extend", (PyCFunction)ListConcatInPlace, METH_O, ""}, + {"__dir__", (PyCFunction)ListDir, METH_NOARGS, ""}, + { NULL, NULL, 0, NULL} }; typedef struct @@ -1408,6 +1497,17 @@ FunctionDestructor(FunctionObject *self) DESTRUCTOR_FINISH(self); } +static char *FunctionAttrs[] = { + "softspace", + NULL +}; + + static PyObject * +FunctionDir(PyObject *self) +{ + return ObjectDir(self, FunctionAttrs); +} + static PyObject * FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs) { @@ -1472,8 +1572,9 @@ FunctionCall(FunctionObject *self, PyObj } static struct PyMethodDef FunctionMethods[] = { - {"__call__", (PyCFunction)FunctionCall, METH_VARARGS|METH_KEYWORDS, ""}, - { NULL, NULL, 0, NULL} + {"__call__",(PyCFunction)FunctionCall, METH_VARARGS|METH_KEYWORDS, ""}, + {"__dir__", (PyCFunction)FunctionDir, METH_NOARGS, ""}, + { NULL, NULL, 0, NULL} }; /* @@ -1842,6 +1943,17 @@ TabPageDestructor(TabPageObject *self) DESTRUCTOR_FINISH(self); } +static char *TabPageAttrs[] = { + "windows", "number", "vars", "window", "valid", + NULL +}; + + static PyObject * +TabPageDir(PyObject *self) +{ + return ObjectDir(self, TabPageAttrs); +} + static PyObject * TabPageAttrValid(TabPageObject *self, char *name) { @@ -1873,6 +1985,8 @@ TabPageAttr(TabPageObject *self, char *n else return WindowNew(self->tab->tp_curwin, self->tab); } + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, TabPageAttrs); return NULL; } @@ -1901,8 +2015,9 @@ TabPageRepr(TabPageObject *self) } static struct PyMethodDef TabPageMethods[] = { - /* name, function, calling, documentation */ - { NULL, NULL, 0, NULL } + /* name, function, calling, documentation */ + {"__dir__", (PyCFunction)TabPageDir, METH_NOARGS, ""}, + { NULL, NULL, 0, NULL} }; /* @@ -2049,6 +2164,17 @@ get_firstwin(TabPageObject *tabObject) else return firstwin; } +static char *WindowAttrs[] = { + "buffer", "cursor", "height", "vars", "options", "number", "row", "col", + "tabpage", "valid", + NULL +}; + + static PyObject * +WindowDir(PyObject *self) +{ + return ObjectDir(self, WindowAttrs); +} static PyObject * WindowAttrValid(WindowObject *self, char *name) @@ -2103,9 +2229,8 @@ WindowAttr(WindowObject *self, char *nam Py_INCREF(self->tabObject); return (PyObject *)(self->tabObject); } - else if (strcmp(name,"__members__") == 0) - return Py_BuildValue("[ssssssssss]", "buffer", "cursor", "height", - "vars", "options", "number", "row", "col", "tabpage", "valid"); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, WindowAttrs); else return NULL; } @@ -2228,8 +2353,9 @@ WindowRepr(WindowObject *self) } static struct PyMethodDef WindowMethods[] = { - /* name, function, calling, documentation */ - { NULL, NULL, 0, NULL } + /* name, function, calling, documentation */ + {"__dir__", (PyCFunction)WindowDir, METH_NOARGS, ""}, + { NULL, NULL, 0, NULL} }; /* @@ -3122,6 +3248,17 @@ RangeSlice(RangeObject *self, PyInt lo, return RBSlice(self->buf, lo, hi, self->start, self->end); } +static char *RangeAttrs[] = { + "start", "end", + NULL +}; + + static PyObject * +RangeDir(PyObject *self) +{ + return ObjectDir(self, RangeAttrs); +} + static PyObject * RangeAppend(RangeObject *self, PyObject *args) { @@ -3162,7 +3299,8 @@ RangeRepr(RangeObject *self) static struct PyMethodDef RangeMethods[] = { /* name, function, calling, documentation */ {"append", (PyCFunction)RangeAppend, METH_VARARGS, "Append data to the Vim range" }, - { NULL, NULL, 0, NULL } + {"__dir__", (PyCFunction)RangeDir, METH_NOARGS, ""}, + { NULL, NULL, 0, NULL} }; static PyTypeObject BufferType; @@ -3239,6 +3377,17 @@ BufferSlice(BufferObject *self, PyInt lo return RBSlice(self, lo, hi, 1, -1); } +static char *BufferAttrs[] = { + "name", "number", "vars", "options", "valid", + NULL +}; + + static PyObject * +BufferDir(PyObject *self) +{ + return ObjectDir(self, BufferAttrs); +} + static PyObject * BufferAttrValid(BufferObject *self, char *name) { @@ -3265,9 +3414,8 @@ BufferAttr(BufferObject *self, char *nam else if (strcmp(name, "options") == 0) return OptionsNew(SREQ_BUF, self->buf, (checkfun) CheckBuffer, (PyObject *) self); - else if (strcmp(name,"__members__") == 0) - return Py_BuildValue("[sssss]", "name", "number", "vars", "options", - "valid"); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, BufferAttrs); else return NULL; } @@ -3403,10 +3551,8 @@ static struct PyMethodDef BufferMethods[ {"append", (PyCFunction)BufferAppend, METH_VARARGS, "Append data to Vim buffer" }, {"mark", (PyCFunction)BufferMark, METH_VARARGS, "Return (row,col) representing position of named mark" }, {"range", (PyCFunction)BufferRange, METH_VARARGS, "Return a range object which represents the part of the given buffer between line numbers s and e" }, -#if PY_VERSION_HEX >= 0x03000000 - {"__dir__", (PyCFunction)BufferDir, METH_NOARGS, "List buffer attributes" }, -#endif - { NULL, NULL, 0, NULL } + {"__dir__", (PyCFunction)BufferDir, METH_NOARGS, ""}, + { NULL, NULL, 0, NULL} }; /* @@ -3538,6 +3684,17 @@ static PyMappingMethods BufMapAsMapping /* Current items object */ +static char *CurrentAttrs[] = { + "buffer", "window", "line", "range", "tabpage", + NULL +}; + + static PyObject * +CurrentDir(PyObject *self) +{ + return ObjectDir(self, CurrentAttrs); +} + static PyObject * CurrentGetattr(PyObject *self UNUSED, char *name) { @@ -3551,14 +3708,14 @@ CurrentGetattr(PyObject *self UNUSED, ch return GetBufferLine(curbuf, (PyInt)curwin->w_cursor.lnum); else if (strcmp(name, "range") == 0) return RangeNew(curbuf, RangeStart, RangeEnd); - else if (strcmp(name,"__members__") == 0) - return Py_BuildValue("[sssss]", "buffer", "window", "line", "range", - "tabpage"); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, CurrentAttrs); else - { - PyErr_SetString(PyExc_AttributeError, name); +#if PY_MAJOR_VERSION < 3 + return Py_FindMethod(WindowMethods, self, name); +#else return NULL; - } +#endif } static int @@ -3661,6 +3818,12 @@ CurrentSetattr(PyObject *self UNUSED, ch } } +static struct PyMethodDef CurrentMethods[] = { + /* name, function, calling, documentation */ + {"__dir__", (PyCFunction)CurrentDir, METH_NOARGS, ""}, + { NULL, NULL, 0, NULL} +}; + static void init_range_cmd(exarg_T *eap) { @@ -4397,6 +4560,7 @@ init_structs(void) CurrentType.tp_basicsize = sizeof(CurrentObject); CurrentType.tp_flags = Py_TPFLAGS_DEFAULT; CurrentType.tp_doc = "vim current object"; + CurrentType.tp_methods = CurrentMethods; #if PY_MAJOR_VERSION >= 3 CurrentType.tp_getattro = (getattrofunc)CurrentGetattro; CurrentType.tp_setattro = (setattrofunc)CurrentSetattro; diff --git a/src/if_python.c b/src/if_python.c --- a/src/if_python.c +++ b/src/if_python.c @@ -1066,6 +1066,8 @@ OutputGetattr(PyObject *self, char *name { if (strcmp(name, "softspace") == 0) return PyInt_FromLong(((OutputObject *)(self))->softspace); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, OutputAttrs); return Py_FindMethod(OutputMethods, self, name); } @@ -1177,6 +1179,8 @@ RangeGetattr(PyObject *self, char *name) return Py_BuildValue(Py_ssize_t_fmt, ((RangeObject *)(self))->start - 1); else if (strcmp(name, "end") == 0) return Py_BuildValue(Py_ssize_t_fmt, ((RangeObject *)(self))->end - 1); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, RangeAttrs); else return Py_FindMethod(RangeMethods, self, name); } @@ -1396,6 +1400,8 @@ DictionaryGetattr(PyObject *self, char * return PyInt_FromLong(this->dict->dv_lock); else if (strcmp(name, "scope") == 0) return PyInt_FromLong(this->dict->dv_scope); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, DictionaryAttrs); return Py_FindMethod(DictionaryMethods, self, name); } @@ -1420,6 +1426,8 @@ ListGetattr(PyObject *self, char *name) { if (strcmp(name, "locked") == 0) return PyInt_FromLong(((ListObject *)(self))->list->lv_lock); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, ListAttrs); return Py_FindMethod(ListMethods, self, name); } @@ -1431,6 +1439,8 @@ FunctionGetattr(PyObject *self, char *na if (strcmp(name, "name") == 0) return PyString_FromString((char *)(this->name)); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, FunctionAttrs); 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 @@ -666,7 +666,6 @@ call_PyType_GenericAlloc(PyTypeObject *t return PyType_GenericAlloc(type,nitems); } -static PyObject *BufferDir(PyObject *); static PyObject *OutputGetattro(PyObject *, PyObject *); static int OutputSetattro(PyObject *, PyObject *, PyObject *); static PyObject *BufferGetattro(PyObject *, PyObject *); @@ -1094,14 +1093,6 @@ BufferSetattro(PyObject *self, PyObject return BufferSetattr((BufferObject *)(self), name, val); } - static PyObject * -BufferDir(PyObject *self UNUSED) -{ - return Py_BuildValue("[ssssssss]", - "name", "number", "vars", "options", "valid", - "append", "mark", "range"); -} - /******************/ static PyObject * @@ -1368,8 +1359,11 @@ static PySequenceMethods WinListAsSeq = static PyObject * CurrentGetattro(PyObject *self, PyObject *nameobj) { + PyObject *r; GET_ATTR_STRING(name, nameobj); - return CurrentGetattr(self, name); + if (!(r = CurrentGetattr(self, name))) + return PyObject_GenericGetAttr(self, nameobj); + return r; } static int diff --git a/src/testdir/test86.in b/src/testdir/test86.in --- a/src/testdir/test86.in +++ b/src/testdir/test86.in @@ -691,6 +691,24 @@ for expr, attr in ( cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr))) EOF :" +:" Test __dir__() method +py << EOF +for name, o in ( + ('current', vim.current), + ('buffer', vim.current.buffer), + ('window', vim.current.window), + ('tabpage', vim.current.tabpage), + ('range', vim.current.range), + ('dictionary', vim.bindeval('{}')), + ('list', vim.bindeval('[]')), + ('function', vim.bindeval('function("tr")')), + ('output', sys.stdout), + ): + cb.append(name + ':' + ','.join(dir(o))) +del name +del o +EOF +:" :" Test exceptions :fun Exe(e) : execute a:e diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok --- a/src/testdir/test86.ok +++ b/src/testdir/test86.ok @@ -382,6 +382,15 @@ vim.current.buffer:Buffer:True vim.current.range:Range:True vim.current.window:Window:True vim.current.tabpage:TabPage:True +current:__dir__,__members__,buffer,line,range,tabpage,window +buffer:__dir__,__members__,append,mark,name,number,options,range,valid,vars +window:__dir__,__members__,buffer,col,cursor,height,number,options,row,tabpage,valid,vars +tabpage:__dir__,__members__,number,valid,vars,window,windows +range:__dir__,__members__,append,end,start +dictionary:__dir__,__members__,keys,locked,scope +list:__dir__,__members__,extend,locked +function:__call__,__dir__,__members__,softspace +output:__dir__,__members__,flush,softspace,write,writelines (, 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 @@ -669,6 +669,24 @@ for expr, attr in ( cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr))) EOF :" +:" Test __dir__() method +py3 << EOF +for name, o in ( + ('current', vim.current), + ('buffer', vim.current.buffer), + ('window', vim.current.window), + ('tabpage', vim.current.tabpage), + ('range', vim.current.range), + ('dictionary', vim.bindeval('{}')), + ('list', vim.bindeval('[]')), + ('function', vim.bindeval('function("tr")')), + ('output', sys.stdout), + ): + cb.append(name + ':' + ','.join(dir(o))) +del name +del o +EOF +:" :" Test exceptions :fun Exe(e) : execute a:e diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok --- a/src/testdir/test87.ok +++ b/src/testdir/test87.ok @@ -371,6 +371,15 @@ vim.current.buffer:Buffer:True vim.current.range:Range:True vim.current.window:Window:True vim.current.tabpage:TabPage:True +current:__dir__,buffer,line,range,tabpage,window +buffer:__dir__,append,mark,name,number,options,range,valid,vars +window:__dir__,buffer,col,cursor,height,number,options,row,tabpage,valid,vars +tabpage:__dir__,number,valid,vars,window,windows +range:__dir__,append,end,start +dictionary:__dir__,keys,locked,scope +list:__dir__,extend,locked +function:__call__,__dir__,softspace +output:__dir__,flush,softspace,write,writelines (, 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 */ /**/ + 1047, +/**/ 1046, /**/ 1045,