mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
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:
parent
2f760c35e2
commit
7eea37e831
5 changed files with 167 additions and 138 deletions
|
@ -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}}
|
||||||
|
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
|
@ -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
|
||||||
|
|
145
Objects/object.c
145
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.
|
NoObject is usable as a non-NULL undefined value, used by the macro None.
|
||||||
|
|
|
@ -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[] =
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue