]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #13577: various kinds of descriptors now have a __qualname__ attribute.
authorAntoine Pitrou <solipsis@pitrou.net>
Mon, 12 Dec 2011 12:47:25 +0000 (13:47 +0100)
committerAntoine Pitrou <solipsis@pitrou.net>
Mon, 12 Dec 2011 12:47:25 +0000 (13:47 +0100)
Patch by sbt.

Include/descrobject.h
Lib/test/test_descr.py
Lib/test/test_sys.py
Objects/descrobject.c

index 646b3cca4e43e4f90814e95fd0cb20a5e6967f48..e2ba97fc87944cb80fcde1cdc263f8313cf0541f 100644 (file)
@@ -42,6 +42,7 @@ typedef struct {
     PyObject_HEAD
     PyTypeObject *d_type;
     PyObject *d_name;
+    PyObject *d_qualname;
 } PyDescrObject;
 
 #define PyDescr_COMMON PyDescrObject d_common
index 4a7a9d21977457e564e0aa79eb06e4f7c7cf0a72..2b2026cee9b39c14139a4c25cb1fd6bde5cb5a87 100644 (file)
@@ -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):
index 3169f67be8c0e87b456bb1e8485f6fab3d10c906..f89514f81902f5fd78fc88d39b45e92e305b7e6d 100644 (file)
@@ -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
index b328a45befc3801be08a3d4278e4691d1a21e971..32b1593282097f899bb3969f6d939389db6ae032 100644 (file)
@@ -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}
 };