At Guido's suggestion, here's a new C API function, PyObject_Dir(), like

__builtin__.dir().  Moved the guts from bltinmodule.c to object.c.
This commit is contained in:
Tim Peters 2001-09-04 22:08:56 +00:00
parent 2f760c35e2
commit 7eea37e831
5 changed files with 167 additions and 138 deletions

View file

@ -1720,6 +1720,16 @@ must return an integer or long integer, which is returned as the file
descriptor value. Returns \code{-1} on failure. descriptor value. Returns \code{-1} on failure.
\end{cfuncdesc} \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}} \section{Number Protocol \label{number}}

View file

@ -346,6 +346,14 @@ extern DL_IMPORT(int) PyNumber_CoerceEx(PyObject **, PyObject **);
extern DL_IMPORT(void) (*PyObject_ClearWeakRefs)(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 */ /* Helpers for printing recursive container types */
extern DL_IMPORT(int) Py_ReprEnter(PyObject *); extern DL_IMPORT(int) Py_ReprEnter(PyObject *);
extern DL_IMPORT(void) Py_ReprLeave(PyObject *); extern DL_IMPORT(void) Py_ReprLeave(PyObject *);

View file

@ -96,7 +96,9 @@ Tools
Build 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 - 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 callers checked for it. It's more likely to fail now, because overflow

View file

@ -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. NoObject is usable as a non-NULL undefined value, used by the macro None.

View file

@ -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\ compile; if absent or zero these statements do influence the compilation,\n\
in addition to any features explicitly specified."; 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 * static PyObject *
builtin_dir(PyObject *self, PyObject *args) builtin_dir(PyObject *self, PyObject *args)
{ {
PyObject *arg = NULL; 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)) if (!PyArg_ParseTuple(args, "|O:dir", &arg))
return NULL; return NULL;
return PyObject_Dir(arg);
/* 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;
} }
static char dir_doc[] = static char dir_doc[] =