diff --git a/Include/object.h b/Include/object.h --- a/Include/object.h +++ b/Include/object.h @@ -314,6 +314,7 @@ #endif typedef PyObject *(*getattrfunc)(PyObject *, char *); typedef PyObject *(*getattrofunc)(PyObject *, PyObject *); +typedef PyObject *(*getattroinclassfunc)(struct _typeobject*, PyObject *, PyObject *); typedef int (*setattrfunc)(PyObject *, char *, PyObject *); typedef int (*setattrofunc)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*reprfunc)(PyObject *); @@ -408,6 +409,9 @@ /* Type attribute cache version tag. Added in version 2.6 */ unsigned int tp_version_tag; + /* Look for attribute at one specific level */ + getattroinclassfunc tp_getattro_in_class; + #ifdef COUNT_ALLOCS /* these must be last and never explicitly initialized */ Py_ssize_t tp_allocs; @@ -519,6 +523,7 @@ PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *); #endif PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyObject_GenericGetAttrInClass(PyTypeObject*, PyObject*, PyObject*); PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_GenericSetDict(PyObject *, PyObject *, void *); @@ -819,7 +824,7 @@ PyObject *_py_xincref_tmp = (PyObject *)(op); \ if (_py_xincref_tmp != NULL) \ Py_INCREF(_py_xincref_tmp); \ - } while (0) + } while (0) #define Py_XDECREF(op) \ do { \ diff --git a/Objects/object.c b/Objects/object.c --- a/Objects/object.c +++ b/Objects/object.c @@ -1060,6 +1060,30 @@ /* Generic GetAttr functions - put these in your tp_[gs]etattro slot */ +PyObject* +PyObject_GenericGetAttrInClass(PyTypeObject* tp, PyObject* obj, PyObject* name) +{ + PyObject* descr; + descrgetfunc f; + + descr = PyDict_GetItem(tp->tp_dict, name); + if (descr == NULL) { + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%U'", + tp->tp_name, name); + return NULL; + } + + f = descr->ob_type->tp_descr_get; + if (f != NULL) { + PyObject* res = f(descr, obj, (PyObject*)obj->ob_type); + Py_DECREF(descr); + return res; + } else { + return descr; + } +} + PyObject * _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict) { diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2411,7 +2411,7 @@ char *s; char *res_start = (char*)res; PyType_Slot *slot; - + /* Set the type name and qualname */ s = strrchr(spec->name, '.'); if (s == NULL) @@ -2432,7 +2432,7 @@ type->tp_name = spec->name; if (!type->tp_name) goto fail; - + /* Adjust for empty tuple bases */ if (!bases) { base = &PyBaseObject_Type; @@ -2516,7 +2516,7 @@ /* Set type.__module__ */ s = strrchr(spec->name, '.'); if (s != NULL) - _PyDict_SetItemId(type->tp_dict, &PyId___module__, + _PyDict_SetItemId(type->tp_dict, &PyId___module__, PyUnicode_FromStringAndSize( spec->name, (Py_ssize_t)(s - spec->name))); @@ -4069,6 +4069,7 @@ type->tp_setattr = base->tp_setattr; type->tp_setattro = base->tp_setattro; } + COPYSLOT(tp_getattro_in_class); /* tp_reserved is ignored */ COPYSLOT(tp_repr); /* tp_hash see tp_richcompare */ @@ -4281,6 +4282,8 @@ type->tp_as_mapping = base->tp_as_mapping; if (type->tp_as_buffer == NULL) type->tp_as_buffer = base->tp_as_buffer; + if (type->tp_getattro_in_class == NULL) + type->tp_getattro_in_class = base->tp_getattro_in_class; } /* Link into each base class's list of subclasses */ @@ -6359,10 +6362,26 @@ Py_INCREF(mro); for (; i < n; i++) { tmp = PyTuple_GET_ITEM(mro, i); - if (PyType_Check(tmp)) + if (PyType_Check(tmp)) { dict = ((PyTypeObject *)tmp)->tp_dict; - else + if (((PyTypeObject*)tmp)->tp_getattro_in_class != NULL) { + res = (((PyTypeObject*)tmp)->tp_getattro_in_class)((PyTypeObject*)tmp, (su->obj == (PyObject *)su->obj_type ? (PyObject *)NULL : su->obj), name); + if (res == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + continue; + } + Py_DECREF(mro); + return NULL; + } + + Py_DECREF(mro); + return res; + } + + } else continue; + res = PyDict_GetItem(dict, name); if (res != NULL) { Py_INCREF(res);