diff --git a/Doc/api/api.tex b/Doc/api/api.tex index 8836cdf57a2..a185509898b 100644 --- a/Doc/api/api.tex +++ b/Doc/api/api.tex @@ -1720,6 +1720,16 @@ must return an integer or long integer, which is returned as the file descriptor value. Returns \code{-1} on failure. \end{cfuncdesc} +\begin{cfuncdesc}{PyObject*}{PyObject_Dir}{PyObject *o} +This is equivalent to the Python expression \samp{dir(\var{o})}, +returning a (possibly empty) list of strings appropriate for the +object argument, or \NULL{} in case of error. +If the argument is \NULL{}, this is like the Python \samp{dir()}, +returning the names of the current locals; in this case, if no +execution frame is active then \NULL{} is returned but +\cfunction{PyErr_Occurred()} will return false. +\end{cfuncdesc} + \section{Number Protocol \label{number}} diff --git a/Include/object.h b/Include/object.h index 6cd20a6e618..d9c35144b32 100644 --- a/Include/object.h +++ b/Include/object.h @@ -346,6 +346,14 @@ extern DL_IMPORT(int) PyNumber_CoerceEx(PyObject **, PyObject **); extern DL_IMPORT(void) (*PyObject_ClearWeakRefs)(PyObject *); +/* PyObject_Dir(obj) acts like Python __builtin__.dir(obj), returning a + list of strings. PyObject_Dir(NULL) is like __builtin__.dir(), + returning the names of the current locals. In this case, if there are + no current locals, NULL is returned, and PyErr_Occurred() is false. +*/ +extern DL_IMPORT(PyObject *) PyObject_Dir(PyObject *); + + /* Helpers for printing recursive container types */ extern DL_IMPORT(int) Py_ReprEnter(PyObject *); extern DL_IMPORT(void) Py_ReprLeave(PyObject *); diff --git a/Misc/NEWS b/Misc/NEWS index a8f05c7e82d..7d44a5930f4 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -96,7 +96,9 @@ Tools Build -API +C API + +- New function PyObject_Dir(obj), like Python __builtin__.dir(obj). - Note that PyLong_AsDouble can fail! This has always been true, but no callers checked for it. It's more likely to fail now, because overflow diff --git a/Objects/object.c b/Objects/object.c index 66b4eae2b2f..365d1313f79 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1357,6 +1357,151 @@ PyCallable_Check(PyObject *x) } } +/* Helper for PyObject_Dir. + 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)); + 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; +} + +/* Like __builtin__.dir(arg). See bltinmodule.c's builtin_dir for the + docstring, which should be kept in synch with this implementation. */ + +PyObject * +PyObject_Dir(PyObject *arg) +{ + /* Set exactly one of these non-NULL before the end. */ + PyObject *result = NULL; /* result list */ + PyObject *masterdict = NULL; /* result is masterdict.keys() */ + + /* If NULL arg, return the locals. */ + if (arg == NULL) { + PyObject *locals = PyEval_GetLocals(); + if (locals == NULL) + goto error; + result = PyDict_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; + assert(PyDict_Check(masterdict)); + } + + /* Elif some form of type or class, grab its dict and its bases. + We deliberately don't suck up its __class__, as methods belonging + to the metaclass would probably be more confusing than helpful. */ + else if (PyType_Check(arg) || PyClass_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 { + PyObject *itsclass; + /* Create a dict to start with. CAUTION: Not everything + responding to __dict__ returns a dict! */ + masterdict = PyObject_GetAttrString(arg, "__dict__"); + if (masterdict == NULL) { + PyErr_Clear(); + masterdict = PyDict_New(); + } + else if (!PyDict_Check(masterdict)) { + Py_DECREF(masterdict); + masterdict = PyDict_New(); + } + else { + /* The object may have returned a reference to its + dict, so copy it to avoid mutating it. */ + PyObject *temp = PyDict_Copy(masterdict); + Py_DECREF(masterdict); + masterdict = temp; + } + if (masterdict == NULL) + goto error; + + /* Merge in attrs reachable from its class. + CAUTION: Not all objects have a __class__ attr. */ + itsclass = PyObject_GetAttrString(arg, "__class__"); + if (itsclass == NULL) + PyErr_Clear(); + else { + int status = merge_class_dict(masterdict, itsclass); + Py_DECREF(itsclass); + if (status < 0) + goto error; + } + } + + assert((result == NULL) ^ (masterdict == NULL)); + if (masterdict != NULL) { + /* The result comes from its keys. */ + assert(result == NULL); + result = PyDict_Keys(masterdict); + if (result == NULL) + goto error; + } + + assert(result); + if (PyList_Sort(result) != 0) + goto error; + else + goto normal_return; + + error: + Py_XDECREF(result); + result = NULL; + /* fall through */ + normal_return: + Py_XDECREF(masterdict); + return result; +} /* NoObject is usable as a non-NULL undefined value, used by the macro None. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index d3d32c9ff56..8fff44c279a 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -426,150 +426,14 @@ 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)); - 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) { 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", &arg)) return NULL; - - /* If no arg, return the locals. */ - if (arg == NULL) { - PyObject *locals = PyEval_GetLocals(); - if (locals == NULL) - goto error; - result = PyDict_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; - assert(PyDict_Check(masterdict)); - } - - /* Elif some form of type or class, grab its dict and its bases. - We deliberately don't suck up its __class__, as methods belonging - to the metaclass would probably be more confusing than helpful. */ - else if (PyType_Check(arg) || PyClass_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 { - PyObject *itsclass; - /* Create a dict to start with. CAUTION: Not everything - responding to __dict__ returns a dict! */ - masterdict = PyObject_GetAttrString(arg, "__dict__"); - if (masterdict == NULL) { - PyErr_Clear(); - masterdict = PyDict_New(); - } - else if (!PyDict_Check(masterdict)) { - Py_DECREF(masterdict); - masterdict = PyDict_New(); - } - else { - /* The object may have returned a reference to its - dict, so copy it to avoid mutating it. */ - PyObject *temp = PyDict_Copy(masterdict); - Py_DECREF(masterdict); - masterdict = temp; - } - if (masterdict == NULL) - goto error; - - /* Merge in attrs reachable from its class. - CAUTION: Not all objects have a __class__ attr. */ - itsclass = PyObject_GetAttrString(arg, "__class__"); - if (itsclass == NULL) - PyErr_Clear(); - else { - int status = merge_class_dict(masterdict, itsclass); - Py_DECREF(itsclass); - if (status < 0) - goto error; - } - } - - assert((result == NULL) ^ (masterdict == NULL)); - if (masterdict != NULL) { - /* The result comes from its keys. */ - assert(result == NULL); - result = PyDict_Keys(masterdict); - if (result == NULL) - goto error; - } - - assert(result); - if (PyList_Sort(result) != 0) - goto error; - else - goto normal_return; - - error: - Py_XDECREF(result); - result = NULL; - /* fall through */ - normal_return: - Py_XDECREF(masterdict); - return result; + return PyObject_Dir(arg); } static char dir_doc[] =