diff -r c53d24ee3963 Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst Sat Jul 25 02:45:18 2015 +0200 +++ b/Doc/reference/datamodel.rst Sat Jul 25 11:29:14 2015 +0200 @@ -1430,6 +1430,14 @@ result of implicit invocation via language syntax or built-in functions. See :ref:`special-lookup`. +.. method:: type.__getdescriptor__(cls, name) + + Called during attribute resolution for objects (:meth:`object.__getattribute__`) + and :func:`super` objects to look up a name relative to *cls*, which is + a class on the MRO of an object. The method does not access superclasses. + + Returns the attribute without calling descriptors, and raises :exc:`AttributeError` + when the attribute cannot be found in this class. .. method:: object.__setattr__(self, name, value) diff -r c53d24ee3963 Include/object.h --- a/Include/object.h Sat Jul 25 02:45:18 2015 +0200 +++ b/Include/object.h Sat Jul 25 11:29:14 2015 +0200 @@ -336,6 +336,10 @@ typedef int (*initproc)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *); typedef PyObject *(*allocfunc)(struct _typeobject *, Py_ssize_t); +typedef PyObject* (*getdescriptorfunc)(struct _typeobject*, PyObject*); + +/* XXX: Temporary to make it easier to test external patches: */ +#define Py_HAVE_GETDESCRIPTOR 1 #ifdef Py_LIMITED_API typedef struct _typeobject PyTypeObject; /* opaque */ @@ -420,6 +424,9 @@ destructor tp_finalize; + /* lookups in tp_dict. Added in version 3.6 */ + getdescriptorfunc tp_getdescriptor; + #ifdef COUNT_ALLOCS /* these must be last and never explicitly initialized */ Py_ssize_t tp_allocs; @@ -497,8 +504,8 @@ PyAPI_FUNC(PyObject *) PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *); #ifndef Py_LIMITED_API -PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); -PyAPI_FUNC(PyObject *) _PyType_LookupId(PyTypeObject *, _Py_Identifier *); +PyAPI_FUNC(PyObject *) _PyType_LookupName(PyTypeObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyType_LookupId2(PyTypeObject *, _Py_Identifier *); PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, _Py_Identifier *); PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *); #endif @@ -614,6 +621,9 @@ /* Set if the type allows subclassing */ #define Py_TPFLAGS_BASETYPE (1UL << 10) +/* Set if the type has a custom tp_getdescriptor method */ +#define Py_TPFLAGS_GETDESCRIPTOR (1UL << 11) + /* Set if the type is 'ready' -- fully initialized */ #define Py_TPFLAGS_READY (1UL << 12) diff -r c53d24ee3963 Lib/test/test_descr.py --- a/Lib/test/test_descr.py Sat Jul 25 02:45:18 2015 +0200 +++ b/Lib/test/test_descr.py Sat Jul 25 11:29:14 2015 +0200 @@ -4573,7 +4573,7 @@ class MiscTests(unittest.TestCase): def test_type_lookup_mro_reference(self): - # Issue #14199: _PyType_Lookup() has to keep a strong reference to + # Issue #14199: _PyType_LookupName() has to keep a strong reference to # the type MRO because it may be modified during the lookup, if # __bases__ is set during the lookup for example. class MyKey(object): diff -r c53d24ee3963 Lib/test/test_sys.py --- a/Lib/test/test_sys.py Sat Jul 25 02:45:18 2015 +0200 +++ b/Lib/test/test_sys.py Sat Jul 25 11:29:14 2015 +0200 @@ -1023,11 +1023,11 @@ check((1,2,3), vsize('') + 3*self.P) # type # static type: PyTypeObject - s = vsize('P2n15Pl4Pn9Pn11PIP') + s = vsize('P2n15Pl4Pn9Pn11PIPP') check(int, s) # (PyTypeObject + PyAsyncMethods + PyNumberMethods + PyMappingMethods + # PySequenceMethods + PyBufferProcs + 4P) - s = vsize('P2n17Pl4Pn9Pn11PIP') + struct.calcsize('34P 3P 3P 10P 2P 4P') + s = vsize('P2n17Pl4Pn9Pn11PIPP') + struct.calcsize('34P 3P 3P 10P 2P 4P') # Separate block for PyDictKeysObject with 4 entries s += struct.calcsize("2nPn") + 4*struct.calcsize("n2P") # class diff -r c53d24ee3963 Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Modules/_collectionsmodule.c Sat Jul 25 11:29:14 2015 +0200 @@ -2114,10 +2114,10 @@ PyObject *zero = NULL; PyObject *one = NULL; PyObject *bound_get = NULL; - PyObject *mapping_get; - PyObject *dict_get; - PyObject *mapping_setitem; - PyObject *dict_setitem; + PyObject *mapping_get = NULL; + PyObject *dict_get = NULL; + PyObject *mapping_setitem = NULL; + PyObject *dict_setitem = NULL; if (!PyArg_UnpackTuple(args, "_count_elements", 2, 2, &mapping, &iterable)) return NULL; @@ -2133,10 +2133,10 @@ /* Only take the fast path when get() and __setitem__() * have not been overridden. */ - mapping_get = _PyType_LookupId(Py_TYPE(mapping), &PyId_get); - dict_get = _PyType_LookupId(&PyDict_Type, &PyId_get); - mapping_setitem = _PyType_LookupId(Py_TYPE(mapping), &PyId___setitem__); - dict_setitem = _PyType_LookupId(&PyDict_Type, &PyId___setitem__); + mapping_get = _PyType_LookupId2(Py_TYPE(mapping), &PyId_get); + dict_get = _PyType_LookupId2(&PyDict_Type, &PyId_get); + mapping_setitem = _PyType_LookupId2(Py_TYPE(mapping), &PyId___setitem__); + dict_setitem = _PyType_LookupId2(&PyDict_Type, &PyId___setitem__); if (mapping_get != NULL && mapping_get == dict_get && mapping_setitem != NULL && mapping_setitem == dict_setitem) { @@ -2207,6 +2207,10 @@ } done: + Py_XDECREF(mapping_get); + Py_XDECREF(dict_get); + Py_XDECREF(mapping_setitem); + Py_XDECREF(dict_setitem); Py_DECREF(it); Py_XDECREF(key); Py_XDECREF(newval); diff -r c53d24ee3963 Modules/_lsprof.c --- a/Modules/_lsprof.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Modules/_lsprof.c Sat Jul 25 11:29:14 2015 +0200 @@ -205,8 +205,7 @@ PyObject *modname = fn->m_module; if (name != NULL) { - PyObject *mo = _PyType_Lookup(Py_TYPE(self), name); - Py_XINCREF(mo); + PyObject *mo = _PyType_LookupName(Py_TYPE(self), name); Py_DECREF(name); if (mo != NULL) { PyObject *res = PyObject_Repr(mo); diff -r c53d24ee3963 Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Modules/_testcapimodule.c Sat Jul 25 11:29:14 2015 +0200 @@ -4049,6 +4049,139 @@ }; + +/* + * Helper (meta) type for the pep447 tests + */ + +PyObject* lookup_getdescriptor(PyTypeObject* cls, PyObject* name) +{ + PyObject* result; + PyObject* upper_name = PyObject_CallMethod(name, "upper", ""); + if (upper_name == NULL) { + return NULL; + } + + result = PyDict_GetItem(cls->tp_dict, upper_name); + Py_DECREF(upper_name); + Py_XINCREF(result); + return result; +} + + +static PyTypeObject TypeWithGetDescriptor = { + PyVarObject_HEAD_INIT(NULL, 0) + "TypeWithGetDescriptor", + 0, /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_GETDESCRIPTOR + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Type subclass with __getdescriptor__", + 0, /* traverseproc tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + lookup_getdescriptor, /* tp_getdescriptor */ +}; + +/* + * "TypeWithoutGetDescriptor" doesn't have the GETDESCRIPTOR type flag + * which means its tp_getdescriptor should not be used. + */ +static PyTypeObject TypeWithoutGetDescriptor = { + PyVarObject_HEAD_INIT(NULL, 0) + "TypeWithoutGetDescriptor", + 0, /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Type subclass with invalid __getdescriptor__", + 0, /* traverseproc tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + lookup_getdescriptor, /* tp_getdescriptor */ +}; + + + static struct PyModuleDef _testcapimodule = { PyModuleDef_HEAD_INIT, "_testcapi", @@ -4117,6 +4250,18 @@ PyModule_AddIntConstant(m, "the_number_three", 3); + TypeWithGetDescriptor.tp_base = &PyType_Type; + TypeWithGetDescriptor.tp_new = PyType_Type.tp_new; + TypeWithGetDescriptor.tp_dealloc = PyType_Type.tp_dealloc; + PyType_Ready(&TypeWithGetDescriptor); + PyModule_AddObject(m, "TypeWithGetDescriptor", (PyObject*)&TypeWithGetDescriptor); + + TypeWithoutGetDescriptor.tp_base = &PyType_Type; + TypeWithoutGetDescriptor.tp_new = PyType_Type.tp_new; + TypeWithoutGetDescriptor.tp_dealloc = PyType_Type.tp_dealloc; + PyType_Ready(&TypeWithoutGetDescriptor); + PyModule_AddObject(m, "TypeWithoutGetDescriptor", (PyObject*)&TypeWithoutGetDescriptor); + TestError = PyErr_NewException("_testcapi.error", NULL, NULL); Py_INCREF(TestError); PyModule_AddObject(m, "error", TestError); diff -r c53d24ee3963 Objects/classobject.c --- a/Objects/classobject.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Objects/classobject.c Sat Jul 25 11:29:14 2015 +0200 @@ -142,15 +142,16 @@ if (PyType_Ready(tp) < 0) return NULL; } - descr = _PyType_Lookup(tp, name); + descr = _PyType_LookupName(tp, name); } if (descr != NULL) { descrgetfunc f = TP_DESCR_GET(descr->ob_type); - if (f != NULL) - return f(descr, obj, (PyObject *)obj->ob_type); - else { - Py_INCREF(descr); + if (f != NULL) { + PyObject* result = f(descr, obj, (PyObject *)obj->ob_type); + Py_DECREF(descr); + return result; + } else { return descr; } } @@ -480,14 +481,15 @@ if (PyType_Ready(tp) < 0) return NULL; } - descr = _PyType_Lookup(tp, name); + descr = _PyType_LookupName(tp, name); if (descr != NULL) { descrgetfunc f = TP_DESCR_GET(descr->ob_type); - if (f != NULL) - return f(descr, self, (PyObject *)self->ob_type); - else { - Py_INCREF(descr); + if (f != NULL) { + PyObject* result = f(descr, self, (PyObject *)self->ob_type); + Py_DECREF(descr); + return result; + } else { return descr; } } diff -r c53d24ee3963 Objects/object.c --- a/Objects/object.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Objects/object.c Sat Jul 25 11:29:14 2015 +0200 @@ -1049,8 +1049,7 @@ goto done; } - descr = _PyType_Lookup(tp, name); - Py_XINCREF(descr); + descr = _PyType_LookupName(tp, name); f = NULL; if (descr != NULL) { @@ -1141,8 +1140,7 @@ Py_INCREF(name); - descr = _PyType_Lookup(tp, name); - Py_XINCREF(descr); + descr = _PyType_LookupName(tp, name); f = NULL; if (descr != NULL) { diff -r c53d24ee3963 Objects/typeobject.c --- a/Objects/typeobject.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Objects/typeobject.c Sat Jul 25 11:29:14 2015 +0200 @@ -168,6 +168,9 @@ return PyUnicode_FromStringAndSize(start, end - start); } +static PyObject* +type_getdescriptor(PyTypeObject* type, PyObject* name); + unsigned int PyType_ClearCache(void) { @@ -1386,7 +1389,7 @@ Two variants: - lookup_maybe() returns NULL without raising an exception - when the _PyType_Lookup() call fails; + when the _PyType_LookupName() call fails; - lookup_method() always raises an exception upon errors. @@ -1398,13 +1401,14 @@ { PyObject *res; - res = _PyType_LookupId(Py_TYPE(self), attrid); + res = _PyType_LookupId2(Py_TYPE(self), attrid); if (res != NULL) { descrgetfunc f; - if ((f = Py_TYPE(res)->tp_descr_get) == NULL) - Py_INCREF(res); - else - res = f(res, self, (PyObject *)(Py_TYPE(self))); + if ((f = Py_TYPE(res)->tp_descr_get) != NULL) { + PyObject* tmp = f(res, self, (PyObject *)(Py_TYPE(self))); + Py_DECREF(res); + res = tmp; + } } return res; } @@ -2055,9 +2059,11 @@ { PyObject *descr; - descr = _PyType_LookupId(type, &PyId___dict__); - if (descr == NULL || !PyDescr_IsData(descr)) + descr = _PyType_LookupId2(type, &PyId___dict__); + if (descr == NULL || !PyDescr_IsData(descr)) { + Py_XDECREF(descr); return NULL; + } return descr; } @@ -2079,16 +2085,21 @@ if (base != NULL) { descrgetfunc func; PyObject *descr = get_dict_descriptor(base); + PyObject* result; + if (descr == NULL) { raise_dict_descr_error(obj); return NULL; } func = Py_TYPE(descr)->tp_descr_get; if (func == NULL) { + Py_DECREF(descr); raise_dict_descr_error(obj); return NULL; } - return func(descr, obj, (PyObject *)(Py_TYPE(obj))); + result = func(descr, obj, (PyObject *)(Py_TYPE(obj))); + Py_DECREF(descr); + return result; } return PyObject_GenericGetDict(obj, context); } @@ -2102,6 +2113,8 @@ base = get_builtin_base_with_dict(Py_TYPE(obj)); if (base != NULL) { descrsetfunc func; + int result; + PyObject *descr = get_dict_descriptor(base); if (descr == NULL) { raise_dict_descr_error(obj); @@ -2109,10 +2122,13 @@ } func = Py_TYPE(descr)->tp_descr_set; if (func == NULL) { + Py_DECREF(descr); raise_dict_descr_error(obj); return -1; } - return func(descr, obj, value); + result = func(descr, obj, value); + Py_DECREF(descr); + return result; } /* Almost like PyObject_GenericSetDict, but allow __dict__ to be deleted. */ dictptr = _PyObject_GetDictPtr(obj); @@ -2851,16 +2867,19 @@ } /* Internal API to look for a name through the MRO. - This returns a borrowed reference, and doesn't set an exception! */ + This doesn't set an exception! */ PyObject * -_PyType_Lookup(PyTypeObject *type, PyObject *name) +_PyType_LookupName(PyTypeObject *type, PyObject *name) { Py_ssize_t i, n; - PyObject *mro, *res, *base, *dict; + PyObject *mro, *res, *base; unsigned int h; if (MCACHE_CACHEABLE_NAME(name) && + PyType_HasFeature(type, Py_TPFLAGS_READY) && + !PyType_HasFeature(type, Py_TPFLAGS_GETDESCRIPTOR) && PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) { + /* fast path */ h = MCACHE_HASH_METHOD(type, name); if (method_cache[h].version == type->tp_version_tag && @@ -2868,6 +2887,7 @@ #if MCACHE_STATS method_cache_hits++; #endif + Py_XINCREF(method_cache[h].value); return method_cache[h].value; } } @@ -2890,15 +2910,21 @@ for (i = 0; i < n; i++) { base = PyTuple_GET_ITEM(mro, i); assert(PyType_Check(base)); - dict = ((PyTypeObject *)base)->tp_dict; - assert(dict && PyDict_Check(dict)); - res = PyDict_GetItem(dict, name); - if (res != NULL) + if (PyType_HasFeature(Py_TYPE(base), Py_TPFLAGS_GETDESCRIPTOR)) { + res = Py_TYPE(base)->tp_getdescriptor((PyTypeObject*)base, name); + } else { + res = type_getdescriptor((PyTypeObject*)base, name); + } + if (res != NULL) { break; + } } Py_DECREF(mro); - if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(type)) { + if (PyType_HasFeature(type, Py_TPFLAGS_READY) && + !PyType_HasFeature(type, Py_TPFLAGS_GETDESCRIPTOR) && + MCACHE_CACHEABLE_NAME(name) && assign_version_tag(type)) { + h = MCACHE_HASH_METHOD(type, name); method_cache[h].version = type->tp_version_tag; method_cache[h].value = res; /* borrowed */ @@ -2917,17 +2943,17 @@ } PyObject * -_PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name) +_PyType_LookupId2(PyTypeObject *type, struct _Py_Identifier *name) { PyObject *oname; oname = _PyUnicode_FromId(name); /* borrowed */ if (oname == NULL) return NULL; - return _PyType_Lookup(type, oname); + return _PyType_LookupName(type, oname); } /* This is similar to PyObject_GenericGetAttr(), - but uses _PyType_Lookup() instead of just looking in type->tp_dict. */ + but uses _PyType_LookupName() instead of just looking in type->tp_dict. */ static PyObject * type_getattro(PyTypeObject *type, PyObject *name) { @@ -2952,7 +2978,7 @@ meta_get = NULL; /* Look for the attribute in the metatype */ - meta_attribute = _PyType_Lookup(metatype, name); + meta_attribute = _PyType_LookupName(metatype, name); if (meta_attribute != NULL) { meta_get = Py_TYPE(meta_attribute)->tp_descr_get; @@ -2962,15 +2988,16 @@ * writes. Assume the attribute is not overridden in * type's tp_dict (and bases): call the descriptor now. */ - return meta_get(meta_attribute, (PyObject *)type, + PyObject* result = meta_get(meta_attribute, (PyObject *)type, (PyObject *)metatype); + Py_DECREF(meta_attribute); + return result; } - Py_INCREF(meta_attribute); } /* No data descriptor found on metatype. Look in tp_dict of this * type and its bases */ - attribute = _PyType_Lookup(type, name); + attribute = _PyType_LookupName(type, name); if (attribute != NULL) { /* Implement descriptor functionality, if any */ descrgetfunc local_get = Py_TYPE(attribute)->tp_descr_get; @@ -2980,11 +3007,12 @@ if (local_get != NULL) { /* NULL 2nd argument indicates the descriptor was * found on the target object itself (or a base) */ - return local_get(attribute, (PyObject *)NULL, + PyObject* result = local_get(attribute, (PyObject *)NULL, (PyObject *)type); + Py_DECREF(attribute); + return result; } - Py_INCREF(attribute); return attribute; } @@ -3212,6 +3240,18 @@ "type(object) -> the object's type\n" "type(name, bases, dict) -> a new type"); +Py_LOCAL_INLINE(PyObject*) +type_getdescriptor(PyTypeObject* type, PyObject* name) +{ + PyObject* result; + + if (type->tp_dict == NULL) return NULL; + + result = PyDict_GetItem(type->tp_dict, name); + Py_XINCREF(result); + return result; +} + static int type_traverse(PyTypeObject *type, visitproc visit, void *arg) { @@ -3333,6 +3373,15 @@ type_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ (inquiry)type_is_gc, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + type_getdescriptor, /* tp_getdescriptor */ }; @@ -4724,6 +4773,10 @@ * obvious to be done -- the type is on its own. */ } + if (base->tp_flags & Py_TPFLAGS_GETDESCRIPTOR) { + type->tp_flags |= Py_TPFLAGS_GETDESCRIPTOR; + } + COPYSLOT(tp_getdescriptor); } static int add_operators(PyTypeObject *); @@ -5469,6 +5522,31 @@ } static PyObject * +wrap_getdescriptor(PyObject* self, PyObject* args, void* wrapped) +{ + getdescriptorfunc func = (getdescriptorfunc)wrapped; + PyObject* name; + PyObject* result; + + if (!check_num_args(args, 1)) { + return NULL; + } + + if (!PyType_Check(self)) { + PyErr_SetString(PyExc_TypeError, "__getdescriptor__() called with non-type 'self'"); + return NULL; + } + + name = PyTuple_GET_ITEM(args, 0); + result = (*func)((PyTypeObject*)self, name); + + if (result == NULL && !PyErr_Occurred()) { + PyErr_SetObject(PyExc_AttributeError, name); + } + return result; +} + +static PyObject * wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds) { initproc func = (initproc)wrapped; @@ -5689,15 +5767,15 @@ PyObject *func, *args = NULL, *ival = NULL, *retval = NULL; descrgetfunc f; - func = _PyType_LookupId(Py_TYPE(self), &PyId___getitem__); + func = _PyType_LookupId2(Py_TYPE(self), &PyId___getitem__); if (func != NULL) { - if ((f = Py_TYPE(func)->tp_descr_get) == NULL) - Py_INCREF(func); - else { - func = f(func, self, (PyObject *)(Py_TYPE(self))); - if (func == NULL) { + if ((f = Py_TYPE(func)->tp_descr_get) != NULL) { + PyObject* tmp = f(func, self, (PyObject *)(Py_TYPE(self))); + Py_DECREF(func); + if (tmp == NULL) { return NULL; } + func = tmp; } ival = PyLong_FromSsize_t(i); if (ival != NULL) { @@ -5904,6 +5982,19 @@ SLOT1(slot_nb_inplace_true_divide, "__itruediv__", PyObject *, "O") static PyObject * +slot_tp_getdescriptor(PyTypeObject* self, PyObject* name) +{ + PyObject* result; + _Py_IDENTIFIER(__getdescriptor__); + + result = call_method((PyObject*)self, &PyId___getdescriptor__, "(O)", name); + if (result == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + } + return result; +} + +static PyObject * slot_tp_repr(PyObject *self) { PyObject *func, *res; @@ -6044,33 +6135,31 @@ __getattr__, even when the attribute is present. So we use _PyType_Lookup and create the method only when needed, with call_attribute. */ - getattr = _PyType_LookupId(tp, &PyId___getattr__); + getattr = _PyType_LookupId2(tp, &PyId___getattr__); if (getattr == NULL) { /* No __getattr__ hook: use a simpler dispatcher */ tp->tp_getattro = slot_tp_getattro; return slot_tp_getattro(self, name); } - Py_INCREF(getattr); /* speed hack: we could use lookup_maybe, but that would resolve the method fully for each attribute lookup for classes with __getattr__, even when self has the default __getattribute__ method. So we use _PyType_Lookup and create the method only when needed, with call_attribute. */ - getattribute = _PyType_LookupId(tp, &PyId___getattribute__); + getattribute = _PyType_LookupId2(tp, &PyId___getattribute__); if (getattribute == NULL || (Py_TYPE(getattribute) == &PyWrapperDescr_Type && ((PyWrapperDescrObject *)getattribute)->d_wrapped == (void *)PyObject_GenericGetAttr)) res = PyObject_GenericGetAttr(self, name); else { - Py_INCREF(getattribute); res = call_attribute(self, getattribute, name); - Py_DECREF(getattribute); } if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Clear(); res = call_attribute(self, getattr, name); } + Py_XDECREF(getattribute); Py_DECREF(getattr); return res; } @@ -6163,9 +6252,10 @@ { PyTypeObject *tp = Py_TYPE(self); PyObject *get; + PyObject *result; _Py_IDENTIFIER(__get__); - get = _PyType_LookupId(tp, &PyId___get__); + get = _PyType_LookupId2(tp, &PyId___get__); if (get == NULL) { /* Avoid further slowdowns */ if (tp->tp_descr_get == slot_tp_descr_get) @@ -6177,7 +6267,9 @@ obj = Py_None; if (type == NULL) type = Py_None; - return PyObject_CallFunctionObjArgs(get, self, obj, type, NULL); + result = PyObject_CallFunctionObjArgs(get, self, obj, type, NULL); + Py_DECREF(get); + return result; } static int @@ -6442,6 +6534,9 @@ "__new__(type, /, *args, **kwargs)\n--\n\n" "Create and return new object. See help(type) for accurate signature."), TPSLOT("__del__", tp_finalize, slot_tp_finalize, (wrapperfunc)wrap_del, ""), + TPSLOT("__getdescriptor__", tp_getdescriptor, slot_tp_getdescriptor, wrap_getdescriptor, + "type.__getdescriptor__(name)" + ), AMSLOT("__await__", am_await, slot_am_await, wrap_unaryfunc, "__await__($self, /)\n--\n\nReturn an iterator to be used in await expression."), @@ -6681,7 +6776,7 @@ return p; } do { - descr = _PyType_Lookup(type, p->name_strobj); + descr = _PyType_LookupName(type, p->name_strobj); if (descr == NULL) { if (ptr == (void**)&type->tp_iternext) { specific = (void *)_PyObject_NextNotImplemented; @@ -6737,11 +6832,21 @@ use_generic = 1; generic = p->function; } + Py_DECREF(descr); } while ((++p)->offset == offset); if (specific && !use_generic) *ptr = specific; else *ptr = generic; + + if (ptr == (void**)&type->tp_getdescriptor) { + /* + * The subclass has a definition of __getdescriptor__, use a + * typeflag to actually make use of that. + */ + type->tp_flags |= Py_TPFLAGS_GETDESCRIPTOR; + } + return p; } @@ -7050,19 +7155,19 @@ replaced during PyDict_GetItem(dict, name) */ Py_INCREF(mro); do { - PyObject *res, *tmp, *dict; + PyObject *res, *tmp; descrgetfunc f; tmp = PyTuple_GET_ITEM(mro, i); assert(PyType_Check(tmp)); - dict = ((PyTypeObject *)tmp)->tp_dict; - assert(dict != NULL && PyDict_Check(dict)); - - res = PyDict_GetItem(dict, name); + if (PyType_HasFeature(Py_TYPE(tmp), Py_TPFLAGS_GETDESCRIPTOR)) { + res = Py_TYPE(tmp)->tp_getdescriptor((PyTypeObject*)tmp, name); + } else { + res = type_getdescriptor((PyTypeObject*)tmp, name); + } + if (res != NULL) { - Py_INCREF(res); - f = Py_TYPE(res)->tp_descr_get; if (f != NULL) { tmp = f(res,