diff -r b9d9e7762783 Objects/typeobject.c --- a/Objects/typeobject.c Mon Nov 03 21:05:01 2014 -0500 +++ b/Objects/typeobject.c Tue Nov 04 11:35:32 2014 +0100 @@ -2930,10 +2930,14 @@ type_prepare(PyObject *self, PyObject *a */ static int -merge_class_dict(PyObject *dict, PyObject *aclass) +merge_class_dict(PyObject *dict, PyObject *aclass, int classmode) { PyObject *classdict; PyObject *bases; + PyTypeObject *metaclass; + PyGetSetDef *getset; + PyMemberDef *member; + PyMethodDef *method; _Py_IDENTIFIER(__bases__); assert(PyDict_Check(dict)); @@ -2950,6 +2954,23 @@ merge_class_dict(PyObject *dict, PyObjec return -1; } + if (classmode) { + /* For dir(class), merge in attributes and methods defined by the class' type */ + metaclass = Py_TYPE(aclass); + for (getset = metaclass->tp_getset; getset && getset->name; ++getset) { + /* __abstractmethods__ is special, it is only gettable if it is set in + __dict__, the descriptor getter just retrieves that or raises */ + if (strcmp(getset->name, "__abstractmethods__") == 0) + continue; + if (PyDict_SetItemString(dict, getset->name, Py_None) < 0) + return -1; + } + for (member = metaclass->tp_members; member && member->name; ++member) { + if (PyDict_SetItemString(dict, member->name, Py_None) < 0) + return -1; + } + } + /* Recursively merge in the base types' (if any) dicts. */ bases = _PyObject_GetAttrId(aclass, &PyId___bases__); if (bases == NULL) @@ -2968,7 +2989,7 @@ merge_class_dict(PyObject *dict, PyObjec Py_DECREF(bases); return -1; } - status = merge_class_dict(dict, base); + status = merge_class_dict(dict, base, classmode); Py_DECREF(base); if (status < 0) { Py_DECREF(bases); @@ -2981,7 +3002,8 @@ merge_class_dict(PyObject *dict, PyObjec return 0; } -/* __dir__ for type objects: returns __dict__ and __bases__. +/* __dir__ for type objects: returns class attributes (from __dict__ and + tp_getset/tp_members) and recursively from its bases. We deliberately don't suck up its __class__, as methods belonging to the metaclass would probably be more confusing than helpful. */ @@ -2991,7 +3013,7 @@ type_dir(PyObject *self, PyObject *args) PyObject *result = NULL; PyObject *dict = PyDict_New(); - if (dict != NULL && merge_class_dict(dict, self) == 0) + if (dict != NULL && merge_class_dict(dict, self, 1) == 0) result = PyDict_Keys(dict); Py_XDECREF(dict); @@ -4177,7 +4199,7 @@ object_dir(PyObject *self, PyObject *arg /* XXX(tomer): Perhaps fall back to obj->ob_type if no __class__ exists? */ PyErr_Clear(); - else if (merge_class_dict(dict, itsclass) != 0) + else if (merge_class_dict(dict, itsclass, 0) != 0) goto error; result = PyDict_Keys(dict);