Issue #13577: various kinds of descriptors now have a __qualname__ attribute.
Patch by sbt.
This commit is contained in:
parent
16e6a80923
commit
9d57481f04
|
@ -42,6 +42,7 @@ typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
PyTypeObject *d_type;
|
PyTypeObject *d_type;
|
||||||
PyObject *d_name;
|
PyObject *d_name;
|
||||||
|
PyObject *d_qualname;
|
||||||
} PyDescrObject;
|
} PyDescrObject;
|
||||||
|
|
||||||
#define PyDescr_COMMON PyDescrObject d_common
|
#define PyDescr_COMMON PyDescrObject d_common
|
||||||
|
|
|
@ -4442,6 +4442,24 @@ order (MRO) for bases """
|
||||||
self.assertIn("can't delete X.__doc__", str(cm.exception))
|
self.assertIn("can't delete X.__doc__", str(cm.exception))
|
||||||
self.assertEqual(X.__doc__, "banana")
|
self.assertEqual(X.__doc__, "banana")
|
||||||
|
|
||||||
|
def test_qualname(self):
|
||||||
|
descriptors = [str.lower, complex.real, float.real, int.__add__]
|
||||||
|
types = ['method', 'member', 'getset', 'wrapper']
|
||||||
|
|
||||||
|
# make sure we have an example of each type of descriptor
|
||||||
|
for d, n in zip(descriptors, types):
|
||||||
|
self.assertEqual(type(d).__name__, n + '_descriptor')
|
||||||
|
|
||||||
|
for d in descriptors:
|
||||||
|
qualname = d.__objclass__.__qualname__ + '.' + d.__name__
|
||||||
|
self.assertEqual(d.__qualname__, qualname)
|
||||||
|
|
||||||
|
self.assertEqual(str.lower.__qualname__, 'str.lower')
|
||||||
|
self.assertEqual(complex.real.__qualname__, 'complex.real')
|
||||||
|
self.assertEqual(float.real.__qualname__, 'float.real')
|
||||||
|
self.assertEqual(int.__add__.__qualname__, 'int.__add__')
|
||||||
|
|
||||||
|
|
||||||
class DictProxyTests(unittest.TestCase):
|
class DictProxyTests(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class C(object):
|
class C(object):
|
||||||
|
|
|
@ -670,17 +670,17 @@ class SizeofTest(unittest.TestCase):
|
||||||
# complex
|
# complex
|
||||||
check(complex(0,1), size(h + '2d'))
|
check(complex(0,1), size(h + '2d'))
|
||||||
# method_descriptor (descriptor object)
|
# method_descriptor (descriptor object)
|
||||||
check(str.lower, size(h + '2PP'))
|
check(str.lower, size(h + '3PP'))
|
||||||
# classmethod_descriptor (descriptor object)
|
# classmethod_descriptor (descriptor object)
|
||||||
# XXX
|
# XXX
|
||||||
# member_descriptor (descriptor object)
|
# member_descriptor (descriptor object)
|
||||||
import datetime
|
import datetime
|
||||||
check(datetime.timedelta.days, size(h + '2PP'))
|
check(datetime.timedelta.days, size(h + '3PP'))
|
||||||
# getset_descriptor (descriptor object)
|
# getset_descriptor (descriptor object)
|
||||||
import collections
|
import collections
|
||||||
check(collections.defaultdict.default_factory, size(h + '2PP'))
|
check(collections.defaultdict.default_factory, size(h + '3PP'))
|
||||||
# wrapper_descriptor (descriptor object)
|
# wrapper_descriptor (descriptor object)
|
||||||
check(int.__add__, size(h + '2P2P'))
|
check(int.__add__, size(h + '3P2P'))
|
||||||
# method-wrapper (descriptor object)
|
# method-wrapper (descriptor object)
|
||||||
check({}.__iter__, size(h + '2P'))
|
check({}.__iter__, size(h + '2P'))
|
||||||
# dict
|
# dict
|
||||||
|
|
|
@ -9,6 +9,7 @@ descr_dealloc(PyDescrObject *descr)
|
||||||
_PyObject_GC_UNTRACK(descr);
|
_PyObject_GC_UNTRACK(descr);
|
||||||
Py_XDECREF(descr->d_type);
|
Py_XDECREF(descr->d_type);
|
||||||
Py_XDECREF(descr->d_name);
|
Py_XDECREF(descr->d_name);
|
||||||
|
Py_XDECREF(descr->d_qualname);
|
||||||
PyObject_GC_Del(descr);
|
PyObject_GC_Del(descr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,6 +322,44 @@ method_get_doc(PyMethodDescrObject *descr, void *closure)
|
||||||
return PyUnicode_FromString(descr->d_method->ml_doc);
|
return PyUnicode_FromString(descr->d_method->ml_doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
calculate_qualname(PyDescrObject *descr)
|
||||||
|
{
|
||||||
|
PyObject *type_qualname, *res;
|
||||||
|
_Py_IDENTIFIER(__qualname__);
|
||||||
|
|
||||||
|
if (descr->d_name == NULL || !PyUnicode_Check(descr->d_name)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"<descriptor>.__name__ is not a unicode object");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
type_qualname = _PyObject_GetAttrId((PyObject *)descr->d_type,
|
||||||
|
&PyId___qualname__);
|
||||||
|
if (type_qualname == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!PyUnicode_Check(type_qualname)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "<descriptor>.__objclass__."
|
||||||
|
"__qualname__ is not a unicode object");
|
||||||
|
Py_XDECREF(type_qualname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = PyUnicode_FromFormat("%S.%S", type_qualname, descr->d_name);
|
||||||
|
Py_DECREF(type_qualname);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
descr_get_qualname(PyDescrObject *descr)
|
||||||
|
{
|
||||||
|
if (descr->d_qualname == NULL)
|
||||||
|
descr->d_qualname = calculate_qualname(descr);
|
||||||
|
Py_XINCREF(descr->d_qualname);
|
||||||
|
return descr->d_qualname;
|
||||||
|
}
|
||||||
|
|
||||||
static PyMemberDef descr_members[] = {
|
static PyMemberDef descr_members[] = {
|
||||||
{"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY},
|
{"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY},
|
||||||
{"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY},
|
{"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY},
|
||||||
|
@ -329,6 +368,7 @@ static PyMemberDef descr_members[] = {
|
||||||
|
|
||||||
static PyGetSetDef method_getset[] = {
|
static PyGetSetDef method_getset[] = {
|
||||||
{"__doc__", (getter)method_get_doc},
|
{"__doc__", (getter)method_get_doc},
|
||||||
|
{"__qualname__", (getter)descr_get_qualname},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -344,6 +384,7 @@ member_get_doc(PyMemberDescrObject *descr, void *closure)
|
||||||
|
|
||||||
static PyGetSetDef member_getset[] = {
|
static PyGetSetDef member_getset[] = {
|
||||||
{"__doc__", (getter)member_get_doc},
|
{"__doc__", (getter)member_get_doc},
|
||||||
|
{"__qualname__", (getter)descr_get_qualname},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -359,6 +400,7 @@ getset_get_doc(PyGetSetDescrObject *descr, void *closure)
|
||||||
|
|
||||||
static PyGetSetDef getset_getset[] = {
|
static PyGetSetDef getset_getset[] = {
|
||||||
{"__doc__", (getter)getset_get_doc},
|
{"__doc__", (getter)getset_get_doc},
|
||||||
|
{"__qualname__", (getter)descr_get_qualname},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -374,6 +416,7 @@ wrapperdescr_get_doc(PyWrapperDescrObject *descr, void *closure)
|
||||||
|
|
||||||
static PyGetSetDef wrapperdescr_getset[] = {
|
static PyGetSetDef wrapperdescr_getset[] = {
|
||||||
{"__doc__", (getter)wrapperdescr_get_doc},
|
{"__doc__", (getter)wrapperdescr_get_doc},
|
||||||
|
{"__qualname__", (getter)descr_get_qualname},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -585,6 +628,7 @@ descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name)
|
||||||
Py_DECREF(descr);
|
Py_DECREF(descr);
|
||||||
descr = NULL;
|
descr = NULL;
|
||||||
}
|
}
|
||||||
|
descr->d_qualname = NULL;
|
||||||
}
|
}
|
||||||
return descr;
|
return descr;
|
||||||
}
|
}
|
||||||
|
@ -987,9 +1031,16 @@ wrapper_doc(wrapperobject *wp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
wrapper_qualname(wrapperobject *wp)
|
||||||
|
{
|
||||||
|
return descr_get_qualname((PyDescrObject *)wp->descr);
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef wrapper_getsets[] = {
|
static PyGetSetDef wrapper_getsets[] = {
|
||||||
{"__objclass__", (getter)wrapper_objclass},
|
{"__objclass__", (getter)wrapper_objclass},
|
||||||
{"__name__", (getter)wrapper_name},
|
{"__name__", (getter)wrapper_name},
|
||||||
|
{"__qualname__", (getter)wrapper_qualname},
|
||||||
{"__doc__", (getter)wrapper_doc},
|
{"__doc__", (getter)wrapper_doc},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue