From 305b5857f6bef93e0fb21a821f7a5b995cd0889b Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 17 Sep 2001 02:38:46 +0000 Subject: PyObject_Dir(): Merge in __members__ and __methods__ too (if they exist, and are lists, and then just the string elements (if any)). There are good and bad reasons for this. The good reason is to support dir() "like before" on objects of extension types that haven't migrated to the class introspection API yet. The bad reason is that Python's own method objects are such a type, and this is the quickest way to get their im_self etc attrs to "show up" via dir(). It looks much messier to move them to the new scheme, as their current getattr implementation presents a view of their attrs that's a untion of their own attrs plus their im_func's attrs. In particular, methodobject.__dict__ actually returns methodobject.im_func.__dict__, and if that's important to preserve it doesn't seem to fit the class introspection model at all. --- Lib/test/test_descr.py | 8 ++++++++ Objects/object.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 401489d..dbfce0c 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -190,6 +190,7 @@ def test_dir(): cstuff = ['Cdata', 'Cmethod', '__doc__', '__module__'] verify(dir(C) == cstuff) + verify('im_self' in dir(C.Cmethod)) c = C() # c.__doc__ is an odd thing to see here; ditto c.__module__. verify(dir(c) == cstuff) @@ -197,6 +198,7 @@ def test_dir(): c.cdata = 2 c.cmethod = lambda self: 0 verify(dir(c) == cstuff + ['cdata', 'cmethod']) + verify('im_self' in dir(c.Cmethod)) class A(C): Adata = 1 @@ -204,8 +206,10 @@ def test_dir(): astuff = ['Adata', 'Amethod'] + cstuff verify(dir(A) == astuff) + verify('im_self' in dir(A.Amethod)) a = A() verify(dir(a) == astuff) + verify('im_self' in dir(a.Amethod)) a.adata = 42 a.amethod = lambda self: 3 verify(dir(a) == astuff + ['adata', 'amethod']) @@ -224,10 +228,12 @@ def test_dir(): c = C() verify(interesting(dir(c)) == cstuff) + verify('im_self' in dir(C.Cmethod)) c.cdata = 2 c.cmethod = lambda self: 0 verify(interesting(dir(c)) == cstuff + ['cdata', 'cmethod']) + verify('im_self' in dir(c.Cmethod)) class A(C): Adata = 1 @@ -235,11 +241,13 @@ def test_dir(): astuff = ['Adata', 'Amethod'] + cstuff verify(interesting(dir(A)) == astuff) + verify('im_self' in dir(A.Amethod)) a = A() verify(interesting(dir(a)) == astuff) a.adata = 42 a.amethod = lambda self: 3 verify(interesting(dir(a)) == astuff + ['adata', 'amethod']) + verify('im_self' in dir(a.Amethod)) # Try a module subclass. import sys diff --git a/Objects/object.c b/Objects/object.c index fd31e51..c56c3be 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1417,6 +1417,43 @@ merge_class_dict(PyObject* dict, PyObject* aclass) return 0; } +/* Helper for PyObject_Dir. + If obj has an attr named attrname that's a list, merge its string + elements into keys of dict. + Return 0 on success, -1 on error. Errors due to not finding the attr, + or the attr not being a list, are suppressed. +*/ + +static int +merge_list_attr(PyObject* dict, PyObject* obj, char *attrname) +{ + PyObject *list; + int result = 0; + + assert(PyDict_Check(dict)); + assert(obj); + assert(attrname); + + list = PyObject_GetAttrString(obj, attrname); + if (list == NULL) + PyErr_Clear(); + + else if (PyList_Check(list)) { + int i; + for (i = 0; i < PyList_GET_SIZE(list); ++i) { + PyObject *item = PyList_GET_ITEM(list, i); + if (PyString_Check(item)) { + result = PyDict_SetItem(dict, item, Py_None); + if (result < 0) + break; + } + } + } + + Py_XDECREF(list); + return result; +} + /* Like __builtin__.dir(arg). See bltinmodule.c's builtin_dir for the docstring, which should be kept in synch with this implementation. */ @@ -1484,6 +1521,14 @@ PyObject_Dir(PyObject *arg) if (masterdict == NULL) goto error; + /* Merge in __members__ and __methods__ (if any). + XXX Would like this to go away someday; for now, it's + XXX needed to get at im_self etc of method objects. */ + if (merge_list_attr(masterdict, arg, "__members__") < 0) + goto error; + if (merge_list_attr(masterdict, arg, "__methods__") < 0) + goto error; + /* Merge in attrs reachable from its class. CAUTION: Not all objects have a __class__ attr. */ itsclass = PyObject_GetAttrString(arg, "__class__"); -- cgit v0.12