diff options
author | Georg Brandl <georg@python.org> | 2007-03-12 13:17:36 (GMT) |
---|---|---|
committer | Georg Brandl <georg@python.org> | 2007-03-12 13:17:36 (GMT) |
commit | 871f1bc6011c1644b1be2e87518c435e7fb552ad (patch) | |
tree | d318b919f33e98d18b0faa4289a9cfe54e555a6e /Objects | |
parent | 2681beb23e3a8d9d905d99f9e67bb0389264f44f (diff) | |
download | cpython-871f1bc6011c1644b1be2e87518c435e7fb552ad.zip cpython-871f1bc6011c1644b1be2e87518c435e7fb552ad.tar.gz cpython-871f1bc6011c1644b1be2e87518c435e7fb552ad.tar.bz2 |
Backport from Py3k branch:
Patch #1591665: implement the __dir__() special function lookup in PyObject_Dir.
Had to change a few bits of the patch because classobjs and __methods__ are still
in Py2.6.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/object.c | 265 |
1 files changed, 169 insertions, 96 deletions
diff --git a/Objects/object.c b/Objects/object.c index b0672f3..e304d2f 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1566,6 +1566,8 @@ PyCallable_Check(PyObject *x) } } +/* ------------------------- PyObject_Dir() helpers ------------------------- */ + /* 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 @@ -1662,121 +1664,192 @@ merge_list_attr(PyObject* dict, PyObject* obj, const char *attrname) return result; } -/* 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) +/* Helper for PyObject_Dir without arguments: returns the local scope. */ +static PyObject * +_dir_locals() { - /* Set exactly one of these non-NULL before the end. */ - PyObject *result = NULL; /* result list */ - PyObject *masterdict = NULL; /* result is masterdict.keys() */ + PyObject *names; + PyObject *locals = PyEval_GetLocals(); - /* If NULL arg, return the locals. */ - if (arg == NULL) { - PyObject *locals = PyEval_GetLocals(); - if (locals == NULL) - goto error; - result = PyMapping_Keys(locals); - if (result == NULL) - goto error; + if (locals == NULL) { + PyErr_SetString(PyExc_SystemError, "frame does not exist"); + return NULL; } - /* Elif this is some form of module, we only want its dict. */ - else if (PyModule_Check(arg)) { - masterdict = PyObject_GetAttrString(arg, "__dict__"); - if (masterdict == NULL) - goto error; - if (!PyDict_Check(masterdict)) { - PyErr_SetString(PyExc_TypeError, - "module.__dict__ is not a dictionary"); - goto error; - } + names = PyMapping_Keys(locals); + if (!names) + return NULL; + if (!PyList_Check(names)) { + PyErr_Format(PyExc_TypeError, + "dir(): expected keys() of locals to be a list, " + "not '%.200s'", names->ob_type->tp_name); + Py_DECREF(names); + return NULL; } + /* the locals don't need to be DECREF'd */ + return names; +} - /* 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; - } +/* Helper for PyObject_Dir of type objects: returns __dict__ and __bases__. + We deliberately don't suck up its __class__, as methods belonging to the + metaclass would probably be more confusing than helpful. +*/ +static PyObject * +_specialized_dir_type(PyObject *obj) +{ + PyObject *result = NULL; + PyObject *dict = PyDict_New(); - /* 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; + if (dict != NULL && merge_class_dict(dict, obj) == 0) + result = PyDict_Keys(dict); - /* Merge in __members__ and __methods__ (if any). - XXX Would like this to go away someday; for now, it's - XXX needed to get at im_self etc of method objects. */ - if (merge_list_attr(masterdict, arg, "__members__") < 0) - goto error; - if (merge_list_attr(masterdict, arg, "__methods__") < 0) - goto error; + Py_XDECREF(dict); + return result; +} - /* 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(); +/* Helper for PyObject_Dir of module objects: returns the module's __dict__. */ +static PyObject * +_specialized_dir_module(PyObject *obj) +{ + PyObject *result = NULL; + PyObject *dict = PyObject_GetAttrString(obj, "__dict__"); + + if (dict != NULL) { + if (PyDict_Check(dict)) + result = PyDict_Keys(dict); else { - int status = merge_class_dict(masterdict, itsclass); - Py_DECREF(itsclass); - if (status < 0) - goto error; + PyErr_Format(PyExc_TypeError, + "%.200s.__dict__ is not a dictionary", + PyModule_GetName(obj)); } } - 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; + Py_XDECREF(dict); + return result; +} + +/* Helper for PyObject_Dir of generic objects: returns __dict__, __class__, + and recursively up the __class__.__bases__ chain. +*/ +static PyObject * +_generic_dir(PyObject *obj) +{ + PyObject *result = NULL; + PyObject *dict = NULL; + PyObject *itsclass = NULL; + + /* Get __dict__ (which may or may not be a real dict...) */ + dict = PyObject_GetAttrString(obj, "__dict__"); + if (dict == NULL) { + PyErr_Clear(); + dict = PyDict_New(); + } + else if (!PyDict_Check(dict)) { + Py_DECREF(dict); + dict = PyDict_New(); + } + else { + /* Copy __dict__ to avoid mutating it. */ + PyObject *temp = PyDict_Copy(dict); + Py_DECREF(dict); + dict = temp; } - assert(result); - if (!PyList_Check(result)) { - PyErr_Format(PyExc_TypeError, - "Expected keys() to be a list, not '%.200s'", - result->ob_type->tp_name); + if (dict == NULL) goto error; - } - if (PyList_Sort(result) != 0) + + /* Merge in __members__ and __methods__ (if any). + * This is removed in Python 3000. */ + if (merge_list_attr(dict, obj, "__members__") < 0) goto error; - else - goto normal_return; + if (merge_list_attr(dict, obj, "__methods__") < 0) + goto error; + + /* Merge in attrs reachable from its class. */ + itsclass = PyObject_GetAttrString(obj, "__class__"); + if (itsclass == NULL) + /* XXX(tomer): Perhaps fall back to obj->ob_type if no + __class__ exists? */ + PyErr_Clear(); + else { + if (merge_class_dict(dict, itsclass) != 0) + goto error; + } - error: - Py_XDECREF(result); - result = NULL; + result = PyDict_Keys(dict); /* fall through */ - normal_return: - Py_XDECREF(masterdict); +error: + Py_XDECREF(itsclass); + Py_XDECREF(dict); + return result; +} + +/* Helper for PyObject_Dir: object introspection. + This calls one of the above specialized versions if no __dir__ method + exists. */ +static PyObject * +_dir_object(PyObject *obj) +{ + PyObject * result = NULL; + PyObject * dirfunc = PyObject_GetAttrString((PyObject*)obj->ob_type, + "__dir__"); + + assert(obj); + if (dirfunc == NULL) { + /* use default implementation */ + PyErr_Clear(); + if (PyModule_Check(obj)) + result = _specialized_dir_module(obj); + else if (PyType_Check(obj) || PyClass_Check(obj)) + result = _specialized_dir_type(obj); + else + result = _generic_dir(obj); + } + else { + /* use __dir__ */ + result = PyObject_CallFunctionObjArgs(dirfunc, obj, NULL); + Py_DECREF(dirfunc); + if (result == NULL) + return NULL; + + /* result must be a list */ + /* XXX(gbrandl): could also check if all items are strings */ + if (!PyList_Check(result)) { + PyErr_Format(PyExc_TypeError, + "__dir__() must return a list, not %.200s", + result->ob_type->tp_name); + Py_DECREF(result); + result = NULL; + } + } + + return result; +} + +/* Implementation of dir() -- if obj is NULL, returns the names in the current + (local) scope. Otherwise, performs introspection of the object: returns a + sorted list of attribute names (supposedly) accessible from the object +*/ +PyObject * +PyObject_Dir(PyObject *obj) +{ + PyObject * result; + + if (obj == NULL) + /* no object -- introspect the locals */ + result = _dir_locals(); + else + /* object -- introspect the object */ + result = _dir_object(obj); + + assert(result == NULL || PyList_Check(result)); + + if (result != NULL && PyList_Sort(result) != 0) { + /* sorting the list failed */ + Py_DECREF(result); + result = NULL; + } + return result; } |