diff options
author | Tim Peters <tim.peters@gmail.com> | 2001-09-03 05:47:38 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2001-09-03 05:47:38 (GMT) |
commit | 5d2b77cf31c5a3cbabc74936831480b9caea3a12 (patch) | |
tree | dabb2f29553f94a18e3c5ae96d6f232196415f50 /Python/bltinmodule.c | |
parent | 95c99e57b37ede725af1fdd1ff914c91284e3048 (diff) | |
download | cpython-5d2b77cf31c5a3cbabc74936831480b9caea3a12.zip cpython-5d2b77cf31c5a3cbabc74936831480b9caea3a12.tar.gz cpython-5d2b77cf31c5a3cbabc74936831480b9caea3a12.tar.bz2 |
Make dir() wordier (see the new docstring). The new behavior is a mixed
bag. It's clearly wrong for classic classes, at heart because a classic
class doesn't have a __class__ attribute, and I'm unclear on whether
that's feature or bug. I'll repair this once I find out (in the
meantime, dir() applied to classic classes won't find the base classes,
while dir() applied to a classic-class instance *will* find the base
classes but not *their* base classes).
Please give the new dir() a try and see whether you love it or hate it.
The new dir([]) behavior is something I could come to love. Here's
something to hate:
>>> class C:
... pass
...
>>> c = C()
>>> dir(c)
['__doc__', '__module__']
>>>
The idea that an instance has a __doc__ attribute is jarring (of course
it's really c.__class__.__doc__ == C.__doc__; likewise for __module__).
OTOH, the code already has too many special cases, and dir(x) doesn't
have a compelling or clear purpose when x isn't a module.
Diffstat (limited to 'Python/bltinmodule.c')
-rw-r--r-- | Python/bltinmodule.c | 187 |
1 files changed, 134 insertions, 53 deletions
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 62bf2a7..d5dc322 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -426,80 +426,161 @@ the effects of any future statements in effect in the code calling\n\ compile; if absent or zero these statements do influence the compilation,\n\ in addition to any features explicitly specified."; +/* Merge the __dict__ of aclass into dict, and recursively also all + the __dict__s of aclass's base classes. The order of merging isn't + defined, as it's expected that only the final set of dict keys is + interesting. + Return 0 on success, -1 on error. +*/ + +static int +merge_class_dict(PyObject* dict, PyObject* aclass) +{ + PyObject *classdict; + PyObject *bases; + + assert(PyDict_Check(dict)); + /* XXX Class objects fail the PyType_Check check. Don't + XXX know of others. */ + /* assert(PyType_Check(aclass)); */ + assert(aclass); + + /* Merge in the type's dict (if any). */ + classdict = PyObject_GetAttrString(aclass, "__dict__"); + if (classdict == NULL) + PyErr_Clear(); + else { + int status = PyDict_Update(dict, classdict); + Py_DECREF(classdict); + if (status < 0) + return -1; + } + + /* Recursively merge in the base types' (if any) dicts. */ + bases = PyObject_GetAttrString(aclass, "__bases__"); + if (bases != NULL) { + int i, n; + assert(PyTuple_Check(bases)); + n = PyTuple_GET_SIZE(bases); + for (i = 0; i < n; i++) { + PyObject *base = PyTuple_GET_ITEM(bases, i); + if (merge_class_dict(dict, base) < 0) { + Py_DECREF(bases); + return -1; + } + } + Py_DECREF(bases); + } + return 0; +} static PyObject * builtin_dir(PyObject *self, PyObject *args) { - static char *attrlist[] = {"__members__", "__methods__", NULL}; - PyObject *v = NULL, *l = NULL, *m = NULL; - PyObject *d, *x; - int i; - char **s; + PyObject *arg = NULL; + /* Set exactly one of these non-NULL before the end. */ + PyObject *result = NULL; /* result list */ + PyObject *masterdict = NULL; /* result is masterdict.keys() */ - if (!PyArg_ParseTuple(args, "|O:dir", &v)) + if (!PyArg_ParseTuple(args, "|O:dir", &arg)) return NULL; - if (v == NULL) { - x = PyEval_GetLocals(); - if (x == NULL) + + /* If no arg, return the locals. */ + if (arg == NULL) { + PyObject *locals = PyEval_GetLocals(); + if (locals == NULL) goto error; - l = PyMapping_Keys(x); - if (l == NULL) + result = PyMapping_Keys(locals); + if (result == NULL) + goto error; + } + + /* Elif this is some form of module, we only want its dict. */ + else if (PyObject_TypeCheck(arg, &PyModule_Type)) { + masterdict = PyObject_GetAttrString(arg, "__dict__"); + if (masterdict == NULL) + goto error; + } + + /* Elif some form of type, recurse. */ + else if (PyType_Check(arg)) { + masterdict = PyDict_New(); + if (masterdict == NULL) + goto error; + if (merge_class_dict(masterdict, arg) < 0) goto error; } + + /* Else look at its dict, and the attrs reachable from its class. */ else { - d = PyObject_GetAttrString(v, "__dict__"); - if (d == NULL) + PyObject *itsclass; + /* Create a dict to start with. */ + masterdict = PyObject_GetAttrString(arg, "__dict__"); + if (masterdict == NULL) { PyErr_Clear(); - else { - l = PyMapping_Keys(d); - if (l == NULL) - PyErr_Clear(); - Py_DECREF(d); + masterdict = PyDict_New(); + if (masterdict == NULL) + goto error; } - if (l == NULL) { - l = PyList_New(0); - if (l == NULL) + else { + /* The object may have returned a reference to its + dict, so copy it to avoid mutating it. */ + PyObject *temp = PyDict_Copy(masterdict); + if (temp == NULL) goto error; + Py_DECREF(masterdict); + masterdict = temp; } - for (s = attrlist; *s != NULL; s++) { - m = PyObject_GetAttrString(v, *s); - if (m == NULL) { - PyErr_Clear(); - continue; - } - for (i = 0; ; i++) { - x = PySequence_GetItem(m, i); - if (x == NULL) { - PyErr_Clear(); - break; - } - if (PyList_Append(l, x) != 0) { - Py_DECREF(x); - Py_DECREF(m); - goto error; - } - Py_DECREF(x); - } - Py_DECREF(m); + /* Merge in attrs reachable from its class. */ + itsclass = PyObject_GetAttrString(arg, "__class__"); + /* XXX Sometimes this is null! Like after "class C: pass", + C.__class__ raises AttributeError. Don't know of other + cases. */ + if (itsclass == NULL) + PyErr_Clear(); + else { + int status = merge_class_dict(masterdict, itsclass); + Py_DECREF(itsclass); + if (status < 0) + goto error; } } - if (PyList_Sort(l) != 0) + + assert((result == NULL) ^ (masterdict == NULL)); + if (masterdict != NULL) { + /* The result comes from its keys. */ + assert(result == NULL); + result = PyMapping_Keys(masterdict); + if (result == NULL) + goto error; + } + + assert(result); + if (PyList_Sort(result) != 0) goto error; - return l; + else + goto normal_return; + error: - Py_XDECREF(l); - return NULL; + Py_XDECREF(result); + result = NULL; + /* fall through */ + normal_return: + Py_XDECREF(masterdict); + return result; } static char dir_doc[] = -"dir([object]) -> list of strings\n\ -\n\ -Return an alphabetized list of names comprising (some of) the attributes\n\ -of the given object. Without an argument, the names in the current scope\n\ -are listed. With an instance argument, only the instance attributes are\n\ -returned. With a class argument, attributes of the base class are not\n\ -returned. For other types or arguments, this may list members or methods."; - +"dir([object]) -> list of strings\n" +"\n" +"Return an alphabetized list of names comprising (some of) the attributes\n" +"of the given object, and of attributes reachable from it:\n" +"\n" +"No argument: the names in the current scope.\n" +"Module object: the module attributes.\n" +"Type object: its attributes, and recursively the attributes of its bases.\n" +"Otherwise: its attributes, its class's attributes, and recursively the\n" +" attributes of its class's base classes."; static PyObject * builtin_divmod(PyObject *self, PyObject *args) |