changeset: 74141:7a7b698f6f21 parent: 74139:0466ee1816b1 user: Antoine Pitrou date: Fri Dec 23 12:40:16 2011 +0100 files: Include/methodobject.h Lib/test/test_funcattrs.py Misc/NEWS Objects/methodobject.c Objects/typeobject.c description: Issue #13577: Built-in methods and functions now have a __qualname__. Patch by sbt. diff -r 0466ee1816b1 -r 7a7b698f6f21 Include/methodobject.h --- a/Include/methodobject.h Fri Dec 23 17:04:23 2011 +0800 +++ b/Include/methodobject.h Fri Dec 23 12:40:16 2011 +0100 @@ -30,7 +30,8 @@ #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 diff -r 0466ee1816b1 -r 7a7b698f6f21 Lib/test/test_funcattrs.py --- a/Lib/test/test_funcattrs.py Fri Dec 23 17:04:23 2011 +0800 +++ b/Lib/test/test_funcattrs.py Fri Dec 23 12:40:16 2011 +0100 @@ -342,11 +342,37 @@ 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() diff -r 0466ee1816b1 -r 7a7b698f6f21 Misc/NEWS --- a/Misc/NEWS Fri Dec 23 17:04:23 2011 +0800 +++ b/Misc/NEWS Fri Dec 23 12:40:16 2011 +0100 @@ -10,6 +10,9 @@ 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. diff -r 0466ee1816b1 -r 7a7b698f6f21 Objects/methodobject.c --- a/Objects/methodobject.c Fri Dec 23 17:04:23 2011 +0800 +++ b/Objects/methodobject.c Fri Dec 23 12:40:16 2011 +0100 @@ -44,7 +44,7 @@ PyErr_BadInternalCall(); return NULL; } - return ((PyCFunctionObject *)op) -> m_ml -> ml_meth; + return PyCFunction_GET_FUNCTION(op); } PyObject * @@ -54,7 +54,7 @@ PyErr_BadInternalCall(); return NULL; } - return ((PyCFunctionObject *)op) -> m_self; + return PyCFunction_GET_SELF(op); } int @@ -64,7 +64,7 @@ PyErr_BadInternalCall(); return -1; } - return ((PyCFunctionObject *)op) -> m_ml -> ml_flags; + return PyCFunction_GET_FLAGS(op); } PyObject * @@ -151,6 +151,41 @@ 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, ".__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 @@ { PyObject *self; - self = m->m_self; + self = PyCFunction_GET_SELF(m); if (self == NULL) self = Py_None; Py_INCREF(self); @@ -174,6 +209,7 @@ 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} }; diff -r 0466ee1816b1 -r 7a7b698f6f21 Objects/typeobject.c --- a/Objects/typeobject.c Fri Dec 23 17:04:23 2011 +0800 +++ b/Objects/typeobject.c Fri Dec 23 12:40:16 2011 +0100 @@ -3726,7 +3726,7 @@ 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);