diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2011-12-12 12:47:25 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2011-12-12 12:47:25 (GMT) |
commit | 9d57481f043cb9b94bfc45c1ee041415d915cf8a (patch) | |
tree | 4806f5aa23aa5f72bf9cafe2e1b438d69ccb1f51 | |
parent | 16e6a80923db90031a50790613ef3673b30886d2 (diff) | |
download | cpython-9d57481f043cb9b94bfc45c1ee041415d915cf8a.zip cpython-9d57481f043cb9b94bfc45c1ee041415d915cf8a.tar.gz cpython-9d57481f043cb9b94bfc45c1ee041415d915cf8a.tar.bz2 |
Issue #13577: various kinds of descriptors now have a __qualname__ attribute.
Patch by sbt.
-rw-r--r-- | Include/descrobject.h | 1 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 18 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 8 | ||||
-rw-r--r-- | Objects/descrobject.c | 51 |
4 files changed, 74 insertions, 4 deletions
diff --git a/Include/descrobject.h b/Include/descrobject.h index 646b3cc..e2ba97f 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -42,6 +42,7 @@ typedef struct { PyObject_HEAD PyTypeObject *d_type; PyObject *d_name; + PyObject *d_qualname; } PyDescrObject; #define PyDescr_COMMON PyDescrObject d_common diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 4a7a9d2..2b2026c 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4442,6 +4442,24 @@ order (MRO) for bases """ self.assertIn("can't delete X.__doc__", str(cm.exception)) 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): def setUp(self): class C(object): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 3169f67..f89514f 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -670,17 +670,17 @@ class SizeofTest(unittest.TestCase): # complex check(complex(0,1), size(h + '2d')) # method_descriptor (descriptor object) - check(str.lower, size(h + '2PP')) + check(str.lower, size(h + '3PP')) # classmethod_descriptor (descriptor object) # XXX # member_descriptor (descriptor object) import datetime - check(datetime.timedelta.days, size(h + '2PP')) + check(datetime.timedelta.days, size(h + '3PP')) # getset_descriptor (descriptor object) import collections - check(collections.defaultdict.default_factory, size(h + '2PP')) + check(collections.defaultdict.default_factory, size(h + '3PP')) # wrapper_descriptor (descriptor object) - check(int.__add__, size(h + '2P2P')) + check(int.__add__, size(h + '3P2P')) # method-wrapper (descriptor object) check({}.__iter__, size(h + '2P')) # dict diff --git a/Objects/descrobject.c b/Objects/descrobject.c index b328a45..32b1593 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -9,6 +9,7 @@ descr_dealloc(PyDescrObject *descr) _PyObject_GC_UNTRACK(descr); Py_XDECREF(descr->d_type); Py_XDECREF(descr->d_name); + Py_XDECREF(descr->d_qualname); PyObject_GC_Del(descr); } @@ -321,6 +322,44 @@ method_get_doc(PyMethodDescrObject *descr, void *closure) 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[] = { {"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY}, {"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY}, @@ -329,6 +368,7 @@ static PyMemberDef descr_members[] = { static PyGetSetDef method_getset[] = { {"__doc__", (getter)method_get_doc}, + {"__qualname__", (getter)descr_get_qualname}, {0} }; @@ -344,6 +384,7 @@ member_get_doc(PyMemberDescrObject *descr, void *closure) static PyGetSetDef member_getset[] = { {"__doc__", (getter)member_get_doc}, + {"__qualname__", (getter)descr_get_qualname}, {0} }; @@ -359,6 +400,7 @@ getset_get_doc(PyGetSetDescrObject *descr, void *closure) static PyGetSetDef getset_getset[] = { {"__doc__", (getter)getset_get_doc}, + {"__qualname__", (getter)descr_get_qualname}, {0} }; @@ -374,6 +416,7 @@ wrapperdescr_get_doc(PyWrapperDescrObject *descr, void *closure) static PyGetSetDef wrapperdescr_getset[] = { {"__doc__", (getter)wrapperdescr_get_doc}, + {"__qualname__", (getter)descr_get_qualname}, {0} }; @@ -585,6 +628,7 @@ descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name) Py_DECREF(descr); descr = NULL; } + descr->d_qualname = NULL; } 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[] = { {"__objclass__", (getter)wrapper_objclass}, {"__name__", (getter)wrapper_name}, + {"__qualname__", (getter)wrapper_qualname}, {"__doc__", (getter)wrapper_doc}, {0} }; |