summaryrefslogtreecommitdiffstats
path: root/Objects/object.c
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-09-04 22:08:56 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-09-04 22:08:56 (GMT)
commit7eea37e8317bda88d9f50b862f13642e837957ce (patch)
tree33b41620f6b0e2266d19d43aa8fad9743f3bb3d4 /Objects/object.c
parent2f760c35e2372dc50102305f407e5eed7e7b5c0a (diff)
downloadcpython-7eea37e8317bda88d9f50b862f13642e837957ce.zip
cpython-7eea37e8317bda88d9f50b862f13642e837957ce.tar.gz
cpython-7eea37e8317bda88d9f50b862f13642e837957ce.tar.bz2
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.
Diffstat (limited to 'Objects/object.c')
-rw-r--r--Objects/object.c145
1 files changed, 145 insertions, 0 deletions
diff --git a/Objects/object.c b/Objects/object.c
index 66b4eae..365d131 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.