]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #13577: Built-in methods and functions now have a __qualname__.
authorAntoine Pitrou <solipsis@pitrou.net>
Fri, 23 Dec 2011 11:40:16 +0000 (12:40 +0100)
committerAntoine Pitrou <solipsis@pitrou.net>
Fri, 23 Dec 2011 11:40:16 +0000 (12:40 +0100)
Patch by sbt.

Include/methodobject.h
Lib/test/test_funcattrs.py
Misc/NEWS
Objects/methodobject.c
Objects/typeobject.c

index 7e67c0bf9aab716a46072b22148e72863c82fbaf..d798d134e2c0e9967f4ee02713b72035b6804dad 100644 (file)
@@ -30,7 +30,8 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *);
 #define PyCFunction_GET_FUNCTION(func) \
         (((PyCFunctionObject *)func) -> m_ml -> ml_meth)
 #define PyCFunction_GET_SELF(func) \
-       (((PyCFunctionObject *)func) -> m_self)
+        (((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_STATIC ? \
+         NULL : ((PyCFunctionObject *)func) -> m_self)
 #define PyCFunction_GET_FLAGS(func) \
        (((PyCFunctionObject *)func) -> m_ml -> ml_flags)
 #endif
index f4a38b961d820dca4132d335a77b777c8fa27118..c8ed83020edac68e10cdf3f073942bf33a739e40 100644 (file)
@@ -342,11 +342,37 @@ class StaticMethodAttrsTest(unittest.TestCase):
         self.assertTrue(s.__func__ is f)
 
 
+class BuiltinFunctionPropertiesTest(unittest.TestCase):
+    # XXX Not sure where this should really go since I can't find a
+    # test module specifically for builtin_function_or_method.
+
+    def test_builtin__qualname__(self):
+        import time
+
+        # builtin function:
+        self.assertEqual(len.__qualname__, 'len')
+        self.assertEqual(time.time.__qualname__, 'time')
+
+        # builtin classmethod:
+        self.assertEqual(dict.fromkeys.__qualname__, 'dict.fromkeys')
+        self.assertEqual(float.__getformat__.__qualname__,
+                         'float.__getformat__')
+
+        # builtin staticmethod:
+        self.assertEqual(str.maketrans.__qualname__, 'str.maketrans')
+        self.assertEqual(bytes.maketrans.__qualname__, 'bytes.maketrans')
+
+        # builtin bound instance method:
+        self.assertEqual([1, 2, 3].append.__qualname__, 'list.append')
+        self.assertEqual({'foo': 'bar'}.pop.__qualname__, 'dict.pop')
+
+
 def test_main():
     support.run_unittest(FunctionPropertiesTest, InstancemethodAttrTest,
                               ArbitraryFunctionAttrTest, FunctionDictsTest,
                               FunctionDocstringTest, CellTest,
-                              StaticMethodAttrsTest)
+                              StaticMethodAttrsTest,
+                              BuiltinFunctionPropertiesTest)
 
 if __name__ == "__main__":
     test_main()
index cff314042935d3ae0424299d64ef1fe3744bd496..1dc721af9e557c2c1ad5ef9735db983f2d6bb14a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
 Core and Builtins
 -----------------
 
+- Issue #13577: Built-in methods and functions now have a __qualname__.
+  Patch by sbt.
+
 - Issue #6695: Full garbage collection runs now clear the freelist of set
   objects.  Initial patch by Matthias Troffaes.
 
index a7334cfe8856b569b54a1c5e6470948b07a8a0eb..c3a64098dc9efc7d40a73c128e5c943f1ceee1ff 100644 (file)
@@ -44,7 +44,7 @@ PyCFunction_GetFunction(PyObject *op)
         PyErr_BadInternalCall();
         return NULL;
     }
-    return ((PyCFunctionObject *)op) -> m_ml -> ml_meth;
+    return PyCFunction_GET_FUNCTION(op);
 }
 
 PyObject *
@@ -54,7 +54,7 @@ PyCFunction_GetSelf(PyObject *op)
         PyErr_BadInternalCall();
         return NULL;
     }
-    return ((PyCFunctionObject *)op) -> m_self;
+    return PyCFunction_GET_SELF(op);
 }
 
 int
@@ -64,7 +64,7 @@ PyCFunction_GetFlags(PyObject *op)
         PyErr_BadInternalCall();
         return -1;
     }
-    return ((PyCFunctionObject *)op) -> m_ml -> ml_flags;
+    return PyCFunction_GET_FLAGS(op);
 }
 
 PyObject *
@@ -151,6 +151,41 @@ meth_get__name__(PyCFunctionObject *m, void *closure)
     return PyUnicode_FromString(m->m_ml->ml_name);
 }
 
+static PyObject *
+meth_get__qualname__(PyCFunctionObject *m, void *closure)
+{
+    /* If __self__ is a module or NULL, return m.__name__
+       (e.g. len.__qualname__ == 'len')
+
+       If __self__ is a type, return m.__self__.__qualname__ + '.' + m.__name__
+       (e.g. dict.fromkeys.__qualname__ == 'dict.fromkeys')
+
+       Otherwise return type(m.__self__).__qualname__ + '.' + m.__name__
+       (e.g. [].append.__qualname__ == 'list.append') */
+    PyObject *type, *type_qualname, *res;
+    _Py_IDENTIFIER(__qualname__);
+
+    if (m->m_self == NULL || PyModule_Check(m->m_self))
+        return PyUnicode_FromString(m->m_ml->ml_name);
+
+    type = PyType_Check(m->m_self) ? m->m_self : (PyObject*)Py_TYPE(m->m_self);
+
+    type_qualname = _PyObject_GetAttrId(type, &PyId___qualname__);
+    if (type_qualname == NULL)
+        return NULL;
+
+    if (!PyUnicode_Check(type_qualname)) {
+        PyErr_SetString(PyExc_TypeError, "<method>.__class__."
+                        "__qualname__ is not a unicode object");
+        Py_XDECREF(type_qualname);
+        return NULL;
+    }
+
+    res = PyUnicode_FromFormat("%S.%s", type_qualname, m->m_ml->ml_name);
+    Py_DECREF(type_qualname);
+    return res;
+}
+
 static int
 meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg)
 {
@@ -164,7 +199,7 @@ meth_get__self__(PyCFunctionObject *m, void *closure)
 {
     PyObject *self;
 
-    self = m->m_self;
+    self = PyCFunction_GET_SELF(m);
     if (self == NULL)
         self = Py_None;
     Py_INCREF(self);
@@ -174,6 +209,7 @@ meth_get__self__(PyCFunctionObject *m, void *closure)
 static PyGetSetDef meth_getsets [] = {
     {"__doc__",  (getter)meth_get__doc__,  NULL, NULL},
     {"__name__", (getter)meth_get__name__, NULL, NULL},
+    {"__qualname__", (getter)meth_get__qualname__, NULL, NULL},
     {"__self__", (getter)meth_get__self__, NULL, NULL},
     {0}
 };
index 02999d6e9d7edab4e98984641e8652fcb99ac36c..508e35c4d259bb9a397c488ae6ffb349a798c497 100644 (file)
@@ -3726,7 +3726,7 @@ add_methods(PyTypeObject *type, PyMethodDef *meth)
             descr = PyDescr_NewClassMethod(type, meth);
         }
         else if (meth->ml_flags & METH_STATIC) {
-            PyObject *cfunc = PyCFunction_New(meth, NULL);
+            PyObject *cfunc = PyCFunction_New(meth, (PyObject*)type);
             if (cfunc == NULL)
                 return -1;
             descr = PyStaticMethod_New(cfunc);